From 6991e9c963c848d72bc3094d079064ada7e11a3e Mon Sep 17 00:00:00 2001 From: Fabrizio Grosa Date: Mon, 28 Oct 2024 09:25:28 +0100 Subject: [PATCH 1/5] PWGHF: refactory of TOF MC task with recalibration following main TOF tasks --- PWGHF/TableProducer/mcPidTof.cxx | 1122 ++++++++++++++++++++++-------- 1 file changed, 843 insertions(+), 279 deletions(-) diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index 98ac908f4c5..f24b4ffd956 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -12,20 +12,31 @@ /// /// \file mcPidTof.cxx /// \author Fabrizio Grosa fabrizio.grosa@cern.ch -/// \brief Task to produce PID tables for TOF split for pi, K, p, copied from https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/PID/pidTOFFull.cxx +/// \brief Task to produce PID tables for TOF split for pi, K, p, copied from https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/PID/pidTof.cxx /// It works only for MC and adds the possibility to apply postcalibrations for MC. /// +#include +#include +#include #include // O2 includes -#include -#include "TOFBase/EventTimeMaker.h" +#include "Framework/runDataProcessing.h" #include "Framework/AnalysisTask.h" +#include "Framework/HistogramRegistry.h" #include "ReconstructionDataFormats/Track.h" +#include "CCDB/BasicCCDBManager.h" +#include "TOFBase/EventTimeMaker.h" // O2Physics includes #include "TableHelper.h" +#include "MetadataHelper.h" +#include "CollisionTypeHelper.h" +#include "Common/DataModel/TrackSelectionTables.h" +#include "Common/DataModel/EventSelection.h" +#include "Common/DataModel/FT0Corrected.h" +#include "Common/DataModel/Multiplicity.h" #include "Common/TableProducer/PID/pidTOFBase.h" using namespace o2; @@ -34,35 +45,642 @@ using namespace o2::pid; using namespace o2::framework::expressions; using namespace o2::track; -void customize(std::vector& workflowOptions) +MetadataHelper metadataInfo; + +// Input data types +using Trks = o2::soa::Join; +using Cols = aod::Collisions; +using TrksWtof = soa::Join; +using TrksWtofWevTime = soa::Join; + +using EvTimeCollisions = soa::Join; +using EvTimeCollisionsFT0 = soa::Join; + +// Configuration common to all tasks +struct TOFCalibConfig { + template + void init(const CfgType& opt) + { + mUrl = opt.cfgUrl.value; + mPathGrpLhcIf = opt.cfgPathGrpLhcIf.value; + mTimestamp = opt.cfgTimestamp.value; + mTimeShiftCCDBPathPos = opt.cfgTimeShiftCCDBPathPos.value; + mTimeShiftCCDBPathNeg = opt.cfgTimeShiftCCDBPathNeg.value; + mParamFileName = opt.cfgParamFileName.value; + mParametrizationPath = opt.cfgParametrizationPath.value; + mReconstructionPass = opt.cfgReconstructionPass.value; + mLoadResponseFromCCDB = opt.cfgLoadResponseFromCCDB.value; + mFatalOnPassNotAvailable = opt.cfgFatalOnPassNotAvailable.value; + mEnableTimeDependentResponse = opt.cfgEnableTimeDependentResponse.value; + mCollisionSystem = opt.cfgCollisionSystem.value; + mAutoSetProcessFunctions = opt.cfgAutoSetProcessFunctions.value; + } + + template + void getCfg(o2::framework::InitContext& initContext, const std::string name, VType& v, const std::string task) + { + if (!getTaskOptionValue(initContext, task, name, v, true)) { + LOG(fatal) << "Could not get " << name << " from " << task << " task"; + } + } + + void inheritFromBaseTask(o2::framework::InitContext& initContext, const std::string task = "tof-signal") + { + mInitMode = 2; + getCfg(initContext, "ccdb-url", mUrl, task); + getCfg(initContext, "ccdb-path-grplhcif", mPathGrpLhcIf, task); + getCfg(initContext, "ccdb-timestamp", mTimestamp, task); + getCfg(initContext, "timeShiftCCDBPathPos", mTimeShiftCCDBPathPos, task); + getCfg(initContext, "timeShiftCCDBPathNeg", mTimeShiftCCDBPathPos, task); + getCfg(initContext, "paramFileName", mParamFileName, task); + getCfg(initContext, "parametrizationPath", mParametrizationPath, task); + getCfg(initContext, "reconstructionPass", mReconstructionPass, task); + getCfg(initContext, "loadResponseFromCCDB", mLoadResponseFromCCDB, task); + getCfg(initContext, "fatalOnPassNotAvailable", mFatalOnPassNotAvailable, task); + getCfg(initContext, "enableTimeDependentResponse", mEnableTimeDependentResponse, task); + getCfg(initContext, "collisionSystem", mCollisionSystem, task); + getCfg(initContext, "autoSetProcessFunctions", mAutoSetProcessFunctions, task); + } + // @brief Set up the configuration from the calibration object from the init function of the task + template + void initSetup(o2::pid::tof::TOFResoParamsV3& mRespParamsV3, + CCDBObject ccdb) + { + mInitMode = 1; + // First we set the CCDB manager + ccdb->setURL(mUrl); + ccdb->setTimestamp(mTimestamp); + ccdb->setCaching(true); + ccdb->setLocalObjectValidityChecking(); + // Not later than now objects + ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); + + // Then the information about the metadata + if (mReconstructionPass == "metadata") { + LOG(info) << "Getting pass from metadata"; + if (metadataInfo.isMC()) { + mReconstructionPass = metadataInfo.get("AnchorPassName"); + } else { + LOG(fatal) << "This task works only for MC"; + } + LOG(info) << "Passed autodetect mode for pass. Taking '" << mReconstructionPass << "'"; + } + LOG(info) << "Using parameter collection, starting from pass '" << mReconstructionPass << "'"; + + const std::string fname = mParamFileName; + if (!fname.empty()) { // Loading the parametrization from file + LOG(info) << "Loading exp. sigma parametrization from file " << fname << ", using param: " << mParametrizationPath; + if (1) { + o2::tof::ParameterCollection paramCollection; + paramCollection.loadParamFromFile(fname, mParametrizationPath); + LOG(info) << "+++ Loaded parameter collection from file +++"; + if (!paramCollection.retrieveParameters(mRespParamsV3, mReconstructionPass)) { + if (mFatalOnPassNotAvailable) { + LOGF(fatal, "Pass '%s' not available in the retrieved CCDB object", mReconstructionPass.data()); + } else { + LOGF(warning, "Pass '%s' not available in the retrieved CCDB object", mReconstructionPass.data()); + } + } else { + mRespParamsV3.setMomentumChargeShiftParameters(paramCollection.getPars(mReconstructionPass)); + mRespParamsV3.printMomentumChargeShiftParameters(); + } + } else { + mRespParamsV3.loadParamFromFile(fname.data(), mParametrizationPath); + } + } else if (mLoadResponseFromCCDB) { // Loading it from CCDB + LOG(info) << "Loading exp. sigma parametrization from CCDB, using path: " << mParametrizationPath << " for timestamp " << mTimestamp; + o2::tof::ParameterCollection* paramCollection = ccdb->template getForTimeStamp(mParametrizationPath, mTimestamp); + paramCollection->print(); + if (!paramCollection->retrieveParameters(mRespParamsV3, mReconstructionPass)) { // Attempt at loading the parameters with the pass defined + if (mFatalOnPassNotAvailable) { + LOGF(fatal, "Pass '%s' not available in the retrieved CCDB object", mReconstructionPass.data()); + } else { + LOGF(warning, "Pass '%s' not available in the retrieved CCDB object", mReconstructionPass.data()); + } + } else { // Pass is available, load non standard parameters + mRespParamsV3.setMomentumChargeShiftParameters(paramCollection->getPars(mReconstructionPass)); + mRespParamsV3.printMomentumChargeShiftParameters(); + } + } + // Calibration object is defined + mRespParamsV3.print(); + + // Loading additional calibration objects + if (mTimeShiftCCDBPathPos != "") { + if (mTimeShiftCCDBPathPos.find(".root") != std::string::npos) { + mRespParamsV3.setTimeShiftParameters(mTimeShiftCCDBPathPos, "ccdb_object", true); + } else { + if (mReconstructionPass == "") { + mRespParamsV3.setTimeShiftParameters(ccdb->template getForTimeStamp(mTimeShiftCCDBPathPos, mTimestamp), true); + } else { + std::map metadata; + metadata["RecoPassName"] = mReconstructionPass; + mRespParamsV3.setTimeShiftParameters(ccdb->template getSpecific(mTimeShiftCCDBPathPos, mTimestamp, metadata), true); + } + } + } + if (mTimeShiftCCDBPathNeg != "") { + if (mTimeShiftCCDBPathNeg.find(".root") != std::string::npos) { + mRespParamsV3.setTimeShiftParameters(mTimeShiftCCDBPathNeg, "ccdb_object", false); + } else { + if (mReconstructionPass == "") { + mRespParamsV3.setTimeShiftParameters(ccdb->template getForTimeStamp(mTimeShiftCCDBPathNeg, mTimestamp), false); + } else { + std::map metadata; + metadata["RecoPassName"] = mReconstructionPass; + mRespParamsV3.setTimeShiftParameters(ccdb->template getSpecific(mTimeShiftCCDBPathNeg, mTimestamp, metadata), false); + } + } + } + } + + template + void processSetup(o2::pid::tof::TOFResoParamsV3& mRespParamsV3, + CCDBObject ccdb, + const BcType& bc) + { + LOG(debug) << "Processing setup for run number " << bc.runNumber() << " from run " << mLastRunNumber; + // First we check if this run number was already processed + if (mLastRunNumber == bc.runNumber()) { + return; + } + mLastRunNumber = bc.runNumber(); + mTimestamp = bc.timestamp(); + + // Check the beam type + if (mCollisionSystem == -1) { + o2::parameters::GRPLHCIFData* grpo = ccdb->template getForTimeStamp(mPathGrpLhcIf, + mTimestamp); + mCollisionSystem = CollisionSystemType::getCollisionTypeFromGrp(grpo); + } else { + LOG(debug) << "Not setting collisions system as already set to " << mCollisionSystem << " " << CollisionSystemType::getCollisionSystemName(mCollisionSystem); + } + + if (!mEnableTimeDependentResponse) { + return; + } + LOG(debug) << "Updating parametrization from path '" << mParametrizationPath << "' and timestamp " << mTimestamp; + if (!ccdb->template getForTimeStamp(mParametrizationPath, mTimestamp)->retrieveParameters(mRespParamsV3, mReconstructionPass)) { + if (mFatalOnPassNotAvailable) { + LOGF(fatal, "Pass '%s' not available in the retrieved CCDB object", mReconstructionPass.data()); + } else { + LOGF(warning, "Pass '%s' not available in the retrieved CCDB object", mReconstructionPass.data()); + } + } + return; + } + + bool autoSetProcessFunctions() const { return mAutoSetProcessFunctions; } + int collisionSystem() const { return mCollisionSystem; } + + private: + int mLastRunNumber = -1; // Last run number for which the calibration was loaded + int mInitMode = 0; // 0: no init, 1: init, 2: inherit + + // Configurable options + std::string mUrl; + std::string mPathGrpLhcIf; + int64_t mTimestamp; + std::string mTimeShiftCCDBPathPos; + std::string mTimeShiftCCDBPathNeg; + std::string mParamFileName; + std::string mParametrizationPath; + std::string mReconstructionPass; + bool mLoadResponseFromCCDB; + bool mFatalOnPassNotAvailable; + bool mEnableTimeDependentResponse; + int mCollisionSystem; + bool mAutoSetProcessFunctions; +}; + +// Part 1 TOF signal definition + +/// Selection criteria for tracks used for TOF event time +bool isTrackGoodMatchForTOFPID(const Trks::iterator& tr) { - std::vector options{{"add-qa", VariantType::Int, 0, {"Legacy. No effect."}}}; - std::swap(workflowOptions, options); + if (!tr.hasTOF()) { + return false; + } + return true; } -#include "Framework/runDataProcessing.h" +/// Task to produce the TOF signal from the trackTime information +struct tofSignal { + // Tables to produce + o2::framework::Produces table; + o2::framework::Produces tableFlags; + // Running flags + bool enableTableTOFSignal = false; // Flag to check if the TOF signal table is requested or not + bool enableTablepidTOFFlags = false; // Flag to check if the TOF signal flags table is requested or not + // Output histograms + Configurable enableQaHistograms{"enableQaHistograms", false, "Flag to enable the QA histograms"}; + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + // Detector response and input parameters + o2::pid::tof::TOFResoParamsV3 mRespParamsV3; + Service ccdb; + struct : ConfigurableGroup { + Configurable cfgUrl{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"}; + Configurable cfgPathGrpLhcIf{"ccdb-path-grplhcif", "GLO/Config/GRPLHCIF", "Path on the CCDB for the GRPLHCIF object"}; + Configurable cfgTimestamp{"ccdb-timestamp", -1, "timestamp of the object"}; + Configurable cfgTimeShiftCCDBPathPos{"timeShiftCCDBPathPos", "", "Path of the TOF time shift vs eta for pos. tracks. If empty none is taken"}; + Configurable cfgTimeShiftCCDBPathNeg{"timeShiftCCDBPathNeg", "", "Path of the TOF time shift vs eta for neg. tracks. If empty none is taken"}; + Configurable cfgParamFileName{"paramFileName", "", "Path to the parametrization object. If empty the parametrization is not taken from file"}; + Configurable cfgParametrizationPath{"parametrizationPath", "TOF/Calib/Params", "Path of the TOF parametrization on the CCDB or in the file, if the paramFileName is not empty"}; + Configurable cfgReconstructionPass{"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."}}; + Configurable cfgLoadResponseFromCCDB{"loadResponseFromCCDB", false, "Flag to load the response from the CCDB"}; + Configurable cfgFatalOnPassNotAvailable{"fatalOnPassNotAvailable", true, "Flag to throw a fatal if the pass is not available in the retrieved CCDB object"}; + Configurable cfgEnableTimeDependentResponse{"enableTimeDependentResponse", false, "Flag to use the collision timestamp to fetch the PID Response"}; + Configurable cfgCollisionSystem{"collisionSystem", -1, "Collision system: -1 (autoset), 0 (pp), 1 (PbPb), 2 (XeXe), 3 (pPb)"}; + Configurable cfgAutoSetProcessFunctions{"autoSetProcessFunctions", true, "Flag to autodetect the process functions to use"}; + } cfg; // Configurables (only defined here and inherited from other tasks) + + TOFCalibConfig mTOFCalibConfig; // TOF Calib configuration + + void init(o2::framework::InitContext& initContext) + { + mTOFCalibConfig.init(cfg); + // Checking that the table is requested in the workflow and enabling it + enableTableTOFSignal = isTableRequiredInWorkflow(initContext, "TOFSignal"); + if (enableTableTOFSignal) { + LOG(info) << "Table TOFSignal enabled!"; + } + enableTablepidTOFFlags = isTableRequiredInWorkflow(initContext, "pidTOFFlags"); + if (enableTablepidTOFFlags) { + LOG(info) << "Table pidTOFFlags enabled!"; + } + + // If the table is not requested, disable the task. Uless a process function is enabled from the workflow configuration + if (!enableTableTOFSignal && !enableTablepidTOFFlags) { + LOG(info) << "No table or process is enabled. Disabling task"; + return; + } + + mTOFCalibConfig.initSetup(mRespParamsV3, ccdb); // Getting the parametrization parameters + if (!enableQaHistograms) { + return; + } + histos.add("tofSignal", "tofSignal", kTH1D, {{1000, -1000, 1000000, "tofSignal (ps)"}}); + if (enableTablepidTOFFlags) { + histos.add("goodForPIDFlags", "goodForPIDFlags", kTH1D, {{3, 0, 3, "flags"}}); + } + } + + void process(Trks const& tracks) + { + if (!enableTableTOFSignal) { + return; + } + table.reserve(tracks.size()); + if (enableTablepidTOFFlags) { + tableFlags.reserve(tracks.size()); + } + for (auto& trk : tracks) { + const float& sig = o2::pid::tof::TOFSignal::GetTOFSignal(trk); + if (enableQaHistograms) { + histos.fill(HIST("tofSignal"), sig); + } + table(sig); + if (!enableTablepidTOFFlags) { + continue; + } + const auto& b = isTrackGoodMatchForTOFPID(trk); + if (enableQaHistograms) { + histos.fill(HIST("goodForPIDFlags"), sig); + } + tableFlags(b); + } + } +}; + +/// Selection criteria for tracks used for TOF event time +float trackSampleMinMomentum = 0.5f; +float trackSampleMaxMomentum = 2.f; +template +bool filterForTOFEventTime(const trackType& tr) +{ + return (tr.hasTOF() && + tr.p() > trackSampleMinMomentum && tr.p() < trackSampleMaxMomentum && + tr.hasITS() && + tr.hasTPC() && + (tr.trackType() == o2::aod::track::TrackTypeEnum::Track || tr.trackType() == o2::aod::track::TrackTypeEnum::TrackIU)); +} // accept all + +/// Specialization of TOF event time maker +template typename response, + typename trackTypeContainer, + typename responseParametersType> +o2::tof::eventTimeContainer evTimeMakerForTracks(const trackTypeContainer& tracks, + const responseParametersType& responseParameters, + const float& diamond = 6.0) +{ + return o2::tof::evTimeMakerFromParam(tracks, responseParameters, diamond); +} + +// Part 2 event time definition + +/// Task to produce the TOF event time table +struct tofEventTime { + // Tables to produce + Produces tableEvTime; + Produces tableEvTimeTOFOnly; + Produces tableFlags; + static constexpr bool removeTOFEvTimeBias = true; // Flag to subtract the Ev. Time bias for low multiplicity events with TOF + static constexpr float diamond = 6.0; // Collision diamond used in the estimation of the TOF event time + static constexpr float errDiamond = diamond * 33.356409f; + static constexpr float weightDiamond = 1.f / (errDiamond * errDiamond); + + bool enableTableTOFEvTime = false; + bool enableTableEvTimeTOFOnly = false; + // Detector response and input parameters + o2::pid::tof::TOFResoParamsV3 mRespParamsV3; + Service ccdb; + TOFCalibConfig mTOFCalibConfig; // TOF Calib configuration + + // Event time configurations + Configurable minMomentum{"minMomentum", 0.5f, "Minimum momentum to select track sample for TOF event time"}; + Configurable maxMomentum{"maxMomentum", 2.0f, "Maximum momentum to select track sample for TOF event time"}; + Configurable maxEvTimeTOF{"maxEvTimeTOF", 100000.0f, "Maximum value of the TOF event time"}; + Configurable sel8TOFEvTime{"sel8TOFEvTime", false, "Flag to compute the ev. time only for events that pass the sel8 ev. selection"}; + Configurable mComputeEvTimeWithTOF{"computeEvTimeWithTOF", -1, "Compute ev. time with TOF. -1 (autoset), 0 no, 1 yes"}; + Configurable mComputeEvTimeWithFT0{"computeEvTimeWithFT0", -1, "Compute ev. time with FT0. -1 (autoset), 0 no, 1 yes"}; + Configurable maxNtracksInSet{"maxNtracksInSet", 10, "Size of the set to consider for the TOF ev. time computation"}; + + void init(o2::framework::InitContext& initContext) + { + mTOFCalibConfig.inheritFromBaseTask(initContext); + // Checking that the table is requested in the workflow and enabling it + enableTableTOFEvTime = isTableRequiredInWorkflow(initContext, "TOFEvTime"); + + if (!enableTableTOFEvTime) { + LOG(info) << "Table for TOF Event time (TOFEvTime) is not required, disabling it"; + } + LOG(info) << "Table TOFEvTime enabled!"; + + enableTableEvTimeTOFOnly = isTableRequiredInWorkflow(initContext, "EvTimeTOFOnly"); + if (enableTableEvTimeTOFOnly) { + LOG(info) << "Table EvTimeTOFOnly enabled!"; + } + + if (!enableTableTOFEvTime && !enableTableEvTimeTOFOnly) { + LOG(info) << "No table is enabled. Disabling task"; + return; + } + + if (metadataInfo.isFullyDefined()) { + if (!metadataInfo.isRun3()) { + LOG(fatal) << "Metadata says it is Run2, but this task supports only Run3"; + } + } + + trackSampleMinMomentum = minMomentum; + trackSampleMaxMomentum = maxMomentum; + LOG(info) << "Configuring track sample for TOF ev. time: " << trackSampleMinMomentum << " < p < " << trackSampleMaxMomentum; + + if (sel8TOFEvTime.value == true) { + LOG(info) << "TOF event time will be computed for collisions that pass the event selection only!"; + } + mTOFCalibConfig.initSetup(mRespParamsV3, ccdb); // Getting the parametrization parameters + + o2::tof::eventTimeContainer::setMaxNtracksInSet(maxNtracksInSet.value); + o2::tof::eventTimeContainer::printConfig(); + } + + /// + /// Process function to prepare the event for each track on Run 3 data without the FT0 + // Define slice per collision + Preslice perCollision = aod::track::collisionId; + template + using ResponseImplementationEvTime = o2::pid::tof::ExpTimes; + void process(TrksWtof& tracks, + aod::FT0s const&, + EvTimeCollisionsFT0 const&, + aod::BCsWithTimestamps const&) + { + if (!enableTableTOFEvTime) { + return; + } + LOG(debug) << "Processing data for TOF event time"; + + tableEvTime.reserve(tracks.size()); + tableFlags.reserve(tracks.size()); + if (enableTableEvTimeTOFOnly) { + tableEvTimeTOFOnly.reserve(tracks.size()); + } + bool calibUpdated = false; + for (auto const& track : tracks) { // Loop on all tracks + if (!track.has_collision()) { // Skipping tracks without collisions + continue; + } + const auto& coll = track.collision_as(); + if (!coll.has_bc()) { + continue; + } + mTOFCalibConfig.processSetup(mRespParamsV3, ccdb, coll.bc_as()); // Update the calibration parameters + calibUpdated = true; + break; + } + + // Autoset the processing mode for the event time computation + if (calibUpdated) { + if (mComputeEvTimeWithTOF == -1 || mComputeEvTimeWithFT0 == -1) { + switch (mTOFCalibConfig.collisionSystem()) { + case CollisionSystemType::kCollSyspp: // pp + mComputeEvTimeWithTOF.value = ((mComputeEvTimeWithTOF == -1) ? 0 : mComputeEvTimeWithTOF.value); + mComputeEvTimeWithFT0.value = ((mComputeEvTimeWithFT0 == -1) ? 1 : mComputeEvTimeWithFT0.value); + break; + case CollisionSystemType::kCollSysPbPb: // PbPb + mComputeEvTimeWithTOF.value = ((mComputeEvTimeWithTOF == -1) ? 1 : mComputeEvTimeWithTOF.value); + mComputeEvTimeWithFT0.value = ((mComputeEvTimeWithFT0 == -1) ? 0 : mComputeEvTimeWithFT0.value); + break; + default: + LOG(fatal) << "Collision system " << mTOFCalibConfig.collisionSystem() << " " << CollisionSystemType::getCollisionSystemName(mTOFCalibConfig.collisionSystem()) << " not supported for TOF event time computation"; + break; + } + } + } else { + LOG(warning) << "Calibration not updated on " << tracks.size() << " tracks !!"; + } + LOG(debug) << "Running on " << CollisionSystemType::getCollisionSystemName(mTOFCalibConfig.collisionSystem()) << " mComputeEvTimeWithTOF " << mComputeEvTimeWithTOF.value << " mComputeEvTimeWithFT0 " << mComputeEvTimeWithFT0.value; + + if (mComputeEvTimeWithTOF == 1 && mComputeEvTimeWithFT0 == 1) { + int lastCollisionId = -1; // Last collision ID analysed + for (auto const& t : tracks) { // Loop on collisions + if (!t.has_collision() || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection + tableFlags(0); + tableEvTime(0.f, 999.f); + if (enableTableEvTimeTOFOnly) { + tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + } + continue; + } + if (t.collisionId() == lastCollisionId) { // Event time from this collision is already in the table + continue; + } + /// Create new table for the tracks in a collision + lastCollisionId = t.collisionId(); /// Cache last collision ID + + const auto& tracksInCollision = tracks.sliceBy(perCollision, lastCollisionId); + const auto& collision = t.collision_as(); + + // Compute the TOF event time + const auto evTimeMakerTOF = evTimeMakerForTracks(tracksInCollision, mRespParamsV3, diamond); + + float t0AC[2] = {.0f, 999.f}; // Value and error of T0A or T0C or T0AC + float t0TOF[2] = {static_cast(evTimeMakerTOF.mEventTime), static_cast(evTimeMakerTOF.mEventTimeError)}; // Value and error of TOF + + uint8_t flags = 0; + int nGoodTracksForTOF = 0; + float eventTime = 0.f; + float sumOfWeights = 0.f; + float weight = 0.f; + + for (auto const& trk : tracksInCollision) { // Loop on Tracks + // Reset the flag + flags = 0; + // Reset the event time + eventTime = 0.f; + sumOfWeights = 0.f; + weight = 0.f; + // Remove the bias on TOF ev. time + if constexpr (removeTOFEvTimeBias) { + evTimeMakerTOF.removeBias(trk, nGoodTracksForTOF, t0TOF[0], t0TOF[1], 2); + } + if (t0TOF[1] < errDiamond && (maxEvTimeTOF <= 0 || abs(t0TOF[0]) < maxEvTimeTOF)) { + flags |= o2::aod::pidflags::enums::PIDFlags::EvTimeTOF; + + weight = 1.f / (t0TOF[1] * t0TOF[1]); + eventTime += t0TOF[0] * weight; + sumOfWeights += weight; + } + + if (collision.has_foundFT0()) { // T0 measurement is available + // const auto& ft0 = collision.foundFT0(); + if (collision.t0ACValid()) { + t0AC[0] = collision.t0AC() * 1000.f; + t0AC[1] = collision.t0resolution() * 1000.f; + flags |= o2::aod::pidflags::enums::PIDFlags::EvTimeT0AC; + } + + weight = 1.f / (t0AC[1] * t0AC[1]); + eventTime += t0AC[0] * weight; + sumOfWeights += weight; + } + + if (sumOfWeights < weightDiamond) { // avoiding sumOfWeights = 0 or worse that diamond + eventTime = 0; + sumOfWeights = weightDiamond; + tableFlags(0); + } else { + tableFlags(flags); + } + tableEvTime(eventTime / sumOfWeights, sqrt(1. / sumOfWeights)); + if (enableTableEvTimeTOFOnly) { + tableEvTimeTOFOnly((uint8_t)filterForTOFEventTime(trk), t0TOF[0], t0TOF[1], evTimeMakerTOF.mEventTimeMultiplicity); + } + } + } + } else if (mComputeEvTimeWithTOF == 1 && mComputeEvTimeWithFT0 == 0) { + int lastCollisionId = -1; // Last collision ID analysed + for (auto const& t : tracks) { // Loop on collisions + if (!t.has_collision() || ((sel8TOFEvTime.value == true) && !t.collision_as().sel8())) { // Track was not assigned, cannot compute event time or event did not pass the event selection + tableFlags(0); + tableEvTime(0.f, 999.f); + if (enableTableEvTimeTOFOnly) { + tableEvTimeTOFOnly((uint8_t)0, 0.f, 0.f, -1); + } + continue; + } + if (t.collisionId() == lastCollisionId) { // Event time from this collision is already in the table + continue; + } + /// Create new table for the tracks in a collision + lastCollisionId = t.collisionId(); /// Cache last collision ID + + const auto& tracksInCollision = tracks.sliceBy(perCollision, lastCollisionId); + + // First make table for event time + const auto evTimeMakerTOF = evTimeMakerForTracks(tracksInCollision, mRespParamsV3, diamond); + int nGoodTracksForTOF = 0; + float et = evTimeMakerTOF.mEventTime; + float erret = evTimeMakerTOF.mEventTimeError; + + for (auto const& trk : tracksInCollision) { // Loop on Tracks + if constexpr (removeTOFEvTimeBias) { + evTimeMakerTOF.removeBias(trk, nGoodTracksForTOF, et, erret, 2); + } + uint8_t flags = 0; + if (erret < errDiamond && (maxEvTimeTOF <= 0.f || abs(et) < maxEvTimeTOF)) { + flags |= o2::aod::pidflags::enums::PIDFlags::EvTimeTOF; + } else { + et = 0.f; + erret = errDiamond; + } + tableFlags(flags); + tableEvTime(et, erret); + if (enableTableEvTimeTOFOnly) { + tableEvTimeTOFOnly((uint8_t)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); + } + if (!t.has_collision()) { // Track was not assigned, cannot compute event time + tableFlags(0); + tableEvTime(0.f, 999.f); + continue; + } + const auto& collision = t.collision_as(); + + if (collision.has_foundFT0()) { // T0 measurement is available + // const auto& ft0 = collision.foundFT0(); + if (collision.t0ACValid()) { + tableFlags(o2::aod::pidflags::enums::PIDFlags::EvTimeT0AC); + tableEvTime(collision.t0AC() * 1000.f, collision.t0resolution() * 1000.f); + continue; + } + } + tableFlags(0); + tableEvTime(0.f, 999.f); + } + } else { + LOG(fatal) << "Invalid configuration for TOF event time computation"; + } + } +}; + +// Part 3 Nsigma computation + +static constexpr int idxPi = 2; +static constexpr int idxKa = 3; +static constexpr int idxPr = 4; /// Task to produce the response table struct mcPidTof { // Tables to produce - Produces tablePIDPi; - Produces tablePIDKa; - Produces tablePIDPr; + Produces tablePIDPi; + Produces tablePIDKa; + Produces tablePIDPr; + + // Tables to produce (full) + Produces tablePIDFullPi; + Produces tablePIDFullKa; + Produces tablePIDFullPr; + // Detector response parameters - o2::pid::tof::TOFResoParamsV2 mRespParamsV2; + o2::pid::tof::TOFResoParamsV3 mRespParamsV3; Service ccdb; - Configurable inheritFromBaseTask{"inheritFromBaseTask", true, "Flag to iherit all common configurables from the TOF base task"}; - // CCDB configuration (inherited from TOF base task) - Configurable url{"ccdb-url", "", "url of the ccdb repository"}; - Configurable timestamp{"ccdb-timestamp", -1, "timestamp of the object"}; - // TOF Calib configuration (inherited from TOF base task) - Configurable paramFileName{"paramFileName", "", "Path to the parametrization object. If empty the parametrization is not taken from file"}; - Configurable parametrizationPath{"parametrizationPath", "", "Path of the TOF parametrization on the CCDB or in the file, if the paramFileName is not empty"}; - Configurable passName{"passName", "", "Name of the pass inside of the CCDB parameter collection. If empty, the automatically deceted from metadata (to be implemented!!!)"}; - Configurable timeShiftCCDBPath{"timeShiftCCDBPath", "", "Path of the TOF time shift vs eta. If empty none is taken"}; - Configurable loadResponseFromCCDB{"loadResponseFromCCDB", false, "Flag to load the response from the CCDB"}; - Configurable enableTimeDependentResponse{"enableTimeDependentResponse", false, "Flag to use the collision timestamp to fetch the PID Response"}; - Configurable fatalOnPassNotAvailable{"fatalOnPassNotAvailable", true, "Flag to throw a fatal if the pass is not available in the retrieved CCDB object"}; + TOFCalibConfig mTOFCalibConfig; // TOF Calib configuration + Configurable enableQaHistograms{"enableQaHistograms", false, "Flag to enable the QA histograms"}; + + // Histograms for QA + std::array, nSpecies> hnSigma; + std::array, nSpecies> hnSigmaFull; + // postcalibrations to overcome MC FT0 timing issue std::map gMcPostCalibMean{}; std::map gMcPostCalibSigma{}; @@ -70,163 +688,140 @@ struct mcPidTof { struct : ConfigurableGroup { std::string prefix = "mcRecalib"; Configurable enable{"enable", false, "enable MC recalibration for Pi/Ka/Pr"}; - Configurable ccdbPath{"ccdbPath", "Users/f/fgrosa/RecalibMcPidTOF/", "path for MC recalibration objects in CCDB"}; + Configurable ccdbPath{"ccdbPath", "Users/f/fgrosa/RecalibmcPidTof/", "path for MC recalibration objects in CCDB"}; Configurable passName{"passName", "apass6", "reco pass of MC anchoring"}; } mcRecalib; - // Running variables - std::vector mEnabledParticles; // Vector of enabled PID hypotheses to loop on when making tables - int mLastCollisionId = -1; // Last collision ID analysed + HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; + + // Running variables + std::vector mEnabledParticles; // Vector of enabled PID hypotheses to loop on when making tables + std::vector mEnabledParticlesFull; // Vector of enabled PID hypotheses to loop on when making full tables void init(o2::framework::InitContext& initContext) { - if (inheritFromBaseTask.value) { // Inheriting from base task - if (!getTaskOptionValue(initContext, "tof-signal", "ccdb-url", url.value, true)) { - LOG(fatal) << "Could not get ccdb-url from tof-signal task"; - } - if (!getTaskOptionValue(initContext, "tof-signal", "ccdb-timestamp", timestamp.value, true)) { - LOG(fatal) << "Could not get ccdb-timestamp from tof-signal task"; - } - if (!getTaskOptionValue(initContext, "tof-event-time", "paramFileName", paramFileName.value, true)) { - LOG(fatal) << "Could not get paramFileName from tof-event-time task"; - } - if (!getTaskOptionValue(initContext, "tof-event-time", "parametrizationPath", parametrizationPath.value, true)) { - LOG(fatal) << "Could not get parametrizationPath from tof-event-time task"; - } - if (!getTaskOptionValue(initContext, "tof-event-time", "passName", passName.value, true)) { - LOG(fatal) << "Could not get passName from tof-event-time task"; - } - if (!getTaskOptionValue(initContext, "tof-signal", "timeShiftCCDBPath", timeShiftCCDBPath.value, true)) { - LOG(fatal) << "Could not get timeShiftCCDBPath from tof-signal task"; - } - if (!getTaskOptionValue(initContext, "tof-event-time", "loadResponseFromCCDB", loadResponseFromCCDB.value, true)) { - LOG(fatal) << "Could not get loadResponseFromCCDB from tof-event-time task"; - } - if (!getTaskOptionValue(initContext, "tof-event-time", "enableTimeDependentResponse", enableTimeDependentResponse.value, true)) { - LOG(fatal) << "Could not get enableTimeDependentResponse from tof-event-time task"; - } - if (!getTaskOptionValue(initContext, "tof-event-time", "fatalOnPassNotAvailable", fatalOnPassNotAvailable.value, true)) { - LOG(fatal) << "Could not get fatalOnPassNotAvailable from tof-event-time task"; - } - } - if (doprocessWSlice == true && doprocessWoSlice == true) { - LOGF(fatal, "Cannot enable processWoSlice and processWSlice at the same time. Please choose one."); - } - if (doprocessWSlice == false && doprocessWoSlice == false) { - LOGF(fatal, "Cannot run without any of processWoSlice and processWSlice enabled. Please choose one."); - } - + mTOFCalibConfig.inheritFromBaseTask(initContext); // Checking the tables are requested in the workflow and enabling them (only pi, K, p) - std::array supportedSpecies = {2, 3, 4}; + std::array supportedSpecies = {idxPi, idxKa, idxPr}; for (auto iSpecie{0u}; iSpecie < supportedSpecies.size(); ++iSpecie) { + // First checking tiny int flag = -1; - enableFlagIfTableRequired(initContext, "pidTOFFull" + particleNames[supportedSpecies[iSpecie]], flag); + enableFlagIfTableRequired(initContext, "pidTOF" + particleNames[supportedSpecies[iSpecie]], flag); if (flag == 1) { mEnabledParticles.push_back(supportedSpecies[iSpecie]); } + + // Then check full + flag = -1; + enableFlagIfTableRequired(initContext, "pidTOFFull" + particleNames[supportedSpecies[iSpecie]], flag); + if (flag == 1) { + mEnabledParticlesFull.push_back(supportedSpecies[iSpecie]); + } } - // Printing enabled tables - LOG(info) << "++ Enabled tables:"; - for (const int& pidId : mEnabledParticles) { - LOG(info) << "++ pidTOFFull" << particleNames[pidId] << " is enabled"; + mEnabledParticlesFull.push_back(idxPi); + mEnabledParticlesFull.push_back(idxKa); + mEnabledParticlesFull.push_back(idxPr); + if (mEnabledParticlesFull.size() == 0 && mEnabledParticles.size() == 0) { + LOG(info) << "No PID tables are required, disabling process function"; + doprocessFillTables.value = false; + doprocessDummy.value = true; + return; } - - // Getting the parametrization parameters - ccdb->setURL(url.value); - ccdb->setTimestamp(timestamp.value); - ccdb->setCaching(true); - ccdb->setLocalObjectValidityChecking(); - // Not later than now objects - ccdb->setCreatedNotAfter(std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count()); - // - - // TODO: implement the automatic pass name detection from metadata - if (passName.value == "") { - passName.value = "unanchored"; // temporary default - LOG(warning) << "Passed autodetect mode for pass, not implemented yet, waiting for metadata. Taking '" << passName.value << "'"; + if (metadataInfo.isFullyDefined()) { + if (!metadataInfo.isRun3()) { + LOG(fatal) << "Metadata says it is Run2, but this task supports only Run3 data"; + } } - LOG(info) << "Using parameter collection, starting from pass '" << passName.value << "'"; + mTOFCalibConfig.initSetup(mRespParamsV3, ccdb); // Getting the parametrization parameters - const std::string fname = paramFileName.value; - if (!fname.empty()) { // Loading the parametrization from file - LOG(info) << "Loading exp. sigma parametrization from file " << fname << ", using param: " << parametrizationPath.value; - if (1) { - o2::tof::ParameterCollection paramCollection; - paramCollection.loadParamFromFile(fname, parametrizationPath.value); - LOG(info) << "+++ Loaded parameter collection from file +++"; - if (!paramCollection.retrieveParameters(mRespParamsV2, passName.value)) { - if (fatalOnPassNotAvailable) { - LOGF(fatal, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); - } else { - LOGF(warning, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); - } - } else { - mRespParamsV2.setShiftParameters(paramCollection.getPars(passName.value)); - mRespParamsV2.printShiftParameters(); - } - } else { - mRespParamsV2.loadParamFromFile(fname.data(), parametrizationPath.value); - } - } else if (loadResponseFromCCDB) { // Loading it from CCDB - LOG(info) << "Loading exp. sigma parametrization from CCDB, using path: " << parametrizationPath.value << " for timestamp " << timestamp.value; - o2::tof::ParameterCollection* paramCollection = ccdb->getForTimeStamp(parametrizationPath.value, timestamp.value); - paramCollection->print(); - if (!paramCollection->retrieveParameters(mRespParamsV2, passName.value)) { // Attempt at loading the parameters with the pass defined - if (fatalOnPassNotAvailable) { - LOGF(fatal, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); - } else { - LOGF(warning, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); - } - } else { // Pass is available, load non standard parameters - mRespParamsV2.setShiftParameters(paramCollection->getPars(passName.value)); - mRespParamsV2.printShiftParameters(); + // Printing enabled tables and enabling QA histograms if needed + LOG(info) << "++ Enabled tables:"; + const AxisSpec pAxis{100, 0, 5, "#it{p} (GeV/#it{c})"}; + const AxisSpec nSigmaAxis{100, -10, 10, "N_{#sigma}^{TOF}"}; + for (const int& iSpecie : mEnabledParticles) { + LOG(info) << "++ pidTOF" << particleNames[iSpecie] << " is enabled"; + if (!enableQaHistograms) { + continue; } + hnSigma[iSpecie] = histos.add(Form("nSigma/%s", particleNames[iSpecie].c_str()), Form("N_{#sigma}^{TOF}(%s)", particleNames[iSpecie].c_str()), kTH2F, {pAxis, nSigmaAxis}); } - mRespParamsV2.print(); - if (timeShiftCCDBPath.value != "") { - if (timeShiftCCDBPath.value.find(".root") != std::string::npos) { - mRespParamsV2.setTimeShiftParameters(timeShiftCCDBPath.value, "gmean_Pos", true); - mRespParamsV2.setTimeShiftParameters(timeShiftCCDBPath.value, "gmean_Neg", false); - } else { - mRespParamsV2.setTimeShiftParameters(ccdb->getForTimeStamp(Form("%s/pos", timeShiftCCDBPath.value.c_str()), timestamp.value), true); - mRespParamsV2.setTimeShiftParameters(ccdb->getForTimeStamp(Form("%s/neg", timeShiftCCDBPath.value.c_str()), timestamp.value), false); + for (const int& iSpecie : mEnabledParticlesFull) { + LOG(info) << "++ pidTOFFull" << particleNames[iSpecie] << " is enabled"; + if (!enableQaHistograms) { + continue; } + hnSigmaFull[iSpecie] = histos.add(Form("nSigmaFull/%s", particleNames[iSpecie].c_str()), Form("N_{#sigma}^{TOF}(%s)", particleNames[iSpecie].c_str()), kTH2F, {pAxis, nSigmaAxis}); + } + + if (mcRecalib.enable && mTOFCalibConfig.collisionSystem() != CollisionSystemType::kCollSyspp) { + LOGP(info, "Disabling MC recalibration, only available for pp"); + mcRecalib.enable.value = false; } } // Reserves an empty table for the given particle ID with size of the given track table - void reserveTable(const int id, const int64_t& size) + void reserveTable(const int id, const int64_t& size, const bool fullTable = false) { switch (id) { - case 2: - tablePIDPi.reserve(size); + case idxPi: { + if (fullTable) { + tablePIDFullPi.reserve(size); + } else { + tablePIDPi.reserve(size); + } break; - case 3: - tablePIDKa.reserve(size); + } + case idxKa: { + if (fullTable) { + tablePIDFullKa.reserve(size); + } else { + tablePIDKa.reserve(size); + } break; - case 4: - tablePIDPr.reserve(size); + } + case idxPr: { + if (fullTable) { + tablePIDFullPr.reserve(size); + } else { + tablePIDPr.reserve(size); + } break; + } default: - LOG(fatal) << "Wrong particle ID in reserveTable()"; + LOG(fatal) << "Wrong particle ID in reserveTable() for " << (fullTable ? "full" : "tiny") << " tables"; break; } } // Makes the table empty for the given particle ID, filling it with dummy values - void makeTableEmpty(const int id) + void makeTableEmpty(const int id, bool fullTable = false) { switch (id) { - case 2: - tablePIDPi(-999.f, -999.f); + case idxPi: + if (fullTable) { + tablePIDFullPi(-999.f, -999.f); + } else { + aod::pidutils::packInTable(-999.f, + tablePIDPi); + } break; - case 3: - tablePIDKa(-999.f, -999.f); + case idxKa: + if (fullTable) { + tablePIDFullKa(-999.f, -999.f); + } else { + aod::pidutils::packInTable(-999.f, + tablePIDKa); + } break; - case 4: - tablePIDPr(-999.f, -999.f); + case idxPr: + if (fullTable) { + tablePIDFullPr(-999.f, -999.f); + } else { + aod::pidutils::packInTable(-999.f, + tablePIDPr); + } break; default: - LOG(fatal) << "Wrong particle ID in makeTableEmpty()"; + LOG(fatal) << "Wrong particle ID in makeTableEmpty() for " << (fullTable ? "full" : "tiny") << " tables"; break; } } @@ -236,7 +831,7 @@ struct mcPidTof { void retrieveMcPostCalibFromCcdb(int64_t timestamp) { std::map metadata; - metadata["RecoPassName"] = mcRecalib.passName; + metadata["RecoPassName"] = metadataInfo.get("AnchorPassName"); auto calibList = ccdb->getSpecific(mcRecalib.ccdbPath, timestamp, metadata); for (auto const& pidId : mEnabledParticles) { // Loop on enabled particle hypotheses gMcPostCalibMean[pidId] = reinterpret_cast(calibList->FindObject(Form("Mean%s", particleNames[pidId].data()))); @@ -273,189 +868,158 @@ struct mcPidTof { return nSigmaCorr; } - using Trks = soa::Join; - // Define slice per collision - Preslice perCollision = aod::track::collisionId; + void processDummy(Trks const&) {} + PROCESS_SWITCH(mcPidTof, processDummy, "Dummy process function", false); + template - using ResponseImplementation = o2::pid::tof::ExpTimes; - void processWSlice(Trks const& tracks, aod::Collisions const&, aod::BCsWithTimestamps const&, aod::McParticles const&) + using ResponseImplementation = o2::pid::tof::ExpTimes; + void processFillTables(TrksWtofWevTime const& tracks, + Cols const&, + aod::BCsWithTimestamps const&, + aod::McParticles const&) { constexpr auto responsePi = ResponseImplementation(); constexpr auto responseKa = ResponseImplementation(); constexpr auto responsePr = ResponseImplementation(); - for (auto const& pidId : mEnabledParticles) { - reserveTable(pidId, tracks.size()); - } - - int lastCollisionId = -1; // Last collision ID analysed - float resolution = 1.f; // Last resolution assigned for (auto const& track : tracks) { // Loop on all tracks - if (!track.has_collision()) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table - for (auto const& pidId : mEnabledParticles) { - makeTableEmpty(pidId); - } + if (!track.has_collision()) { // Skipping tracks without collisions continue; } - - if (track.collisionId() == lastCollisionId) { // Tracks from last collision already processed + const auto& coll = track.collision(); + if (!coll.has_bc()) { continue; } + mTOFCalibConfig.processSetup(mRespParamsV3, ccdb, coll.bc_as()); // Update the calibration parameters + break; + } - // Fill new table for the tracks in a collision - lastCollisionId = track.collisionId(); // Cache last collision ID - timestamp.value = track.collision().bc_as().timestamp(); - if (enableTimeDependentResponse) { - LOG(debug) << "Updating parametrization from path '" << parametrizationPath.value << "' and timestamp " << timestamp.value; - if (!ccdb->getForTimeStamp(parametrizationPath.value, timestamp.value)->retrieveParameters(mRespParamsV2, passName.value)) { - if (fatalOnPassNotAvailable) { - LOGF(fatal, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); - } else { - LOGF(warning, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); - } + for (auto const& pidId : mEnabledParticles) { + reserveTable(pidId, tracks.size(), false); + } + + for (auto const& pidId : mEnabledParticlesFull) { + reserveTable(pidId, tracks.size(), true); + } + + float resolution = 1.f; // Last resolution assigned + float nSigma = 0; + for (auto const& trk : tracks) { // Loop on all tracks + if (!trk.has_collision()) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table + for (auto const& pidId : mEnabledParticles) { + makeTableEmpty(pidId, false); + } + for (auto const& pidId : mEnabledParticlesFull) { + makeTableEmpty(pidId, true); } + continue; } - // in case of MC recalibrations, check if the objects from CCDB has to be updated if (mcRecalib.enable) { - int runNumber = track.collision().bc_as().runNumber(); + auto runNumber = trk.collision().bc_as().runNumber(); if (runNumber != currentRun) { // update postcalibration files + auto timestamp = trk.collision().bc_as().timestamp(); retrieveMcPostCalibFromCcdb(timestamp); } currentRun = runNumber; } - const auto& tracksInCollision = tracks.sliceBy(perCollision, lastCollisionId); - for (auto const& trkInColl : tracksInCollision) { // Loop on tracks - for (auto const& pidId : mEnabledParticles) { // Loop on enabled particle hypotheses - float nSigma{-999.f}; - switch (pidId) { - case 2: - resolution = responsePi.GetExpectedSigma(mRespParamsV2, trkInColl); - nSigma = responsePi.GetSeparation(mRespParamsV2, trkInColl, resolution); - if (mcRecalib.enable && trkInColl.has_mcParticle()) { - if (std::abs(trkInColl.mcParticle().pdgCode()) == kPiPlus) { // we rescale only true signal - nSigma = applyMcRecalib(pidId, trkInColl.pt(), nSigma); - } - } - tablePIDPi(resolution, nSigma); - break; - case 3: - resolution = responseKa.GetExpectedSigma(mRespParamsV2, trkInColl); - nSigma = responseKa.GetSeparation(mRespParamsV2, trkInColl, resolution); - if (mcRecalib.enable && trkInColl.has_mcParticle()) { - if (std::abs(trkInColl.mcParticle().pdgCode()) == kKPlus) { // we rescale only true signal - nSigma = applyMcRecalib(pidId, trkInColl.pt(), nSigma); - } + for (auto const& pidId : mEnabledParticles) { // Loop on enabled particle hypotheses + switch (pidId) { + case idxPi: { + nSigma = responsePi.GetSeparation(mRespParamsV3, trk); + if (mcRecalib.enable && trk.has_mcParticle()) { + if (std::abs(trk.mcParticle().pdgCode()) == kPiPlus) { // we rescale only true signal + nSigma = applyMcRecalib(pidId, trk.pt(), nSigma); } - tablePIDKa(resolution, nSigma); - break; - case 4: - resolution = responsePr.GetExpectedSigma(mRespParamsV2, trkInColl); - nSigma = responsePr.GetSeparation(mRespParamsV2, trkInColl, resolution); - if (mcRecalib.enable && trkInColl.has_mcParticle()) { - if (std::abs(trkInColl.mcParticle().pdgCode()) == kProton) { // we rescale only true signal - nSigma = applyMcRecalib(pidId, trkInColl.pt(), nSigma); - } + } + aod::pidutils::packInTable(nSigma, tablePIDPi); + break; + } + case idxKa: { + nSigma = responseKa.GetSeparation(mRespParamsV3, trk); + if (mcRecalib.enable && trk.has_mcParticle()) { + if (std::abs(trk.mcParticle().pdgCode()) == kKPlus) { // we rescale only true signal + nSigma = applyMcRecalib(pidId, trk.pt(), nSigma); } - tablePIDPr(resolution, nSigma); - break; - default: - LOG(fatal) << "Wrong particle ID in processWSlice()"; - break; + } + aod::pidutils::packInTable(nSigma, tablePIDKa); + break; } - } - } - } - } - PROCESS_SWITCH(mcPidTof, processWSlice, "Process with track slices", true); - - using TrksIU = soa::Join; - template - using ResponseImplementationIU = o2::pid::tof::ExpTimes; - void processWoSlice(TrksIU const& tracks, aod::Collisions const&, aod::BCsWithTimestamps const&, aod::McParticles const&) - { - constexpr auto responsePi = ResponseImplementationIU(); - constexpr auto responseKa = ResponseImplementationIU(); - constexpr auto responsePr = ResponseImplementationIU(); - - for (auto const& pidId : mEnabledParticles) { - reserveTable(pidId, tracks.size()); - } - float resolution = 1.f; // Last resolution assigned - for (auto const& track : tracks) { // Loop on all tracks - if (!track.has_collision()) { // Track was not assigned, cannot compute NSigma (no event time) -> filling with empty table - for (auto const& pidId : mEnabledParticles) { - makeTableEmpty(pidId); - } - continue; - } - - if (enableTimeDependentResponse && (track.collisionId() != mLastCollisionId)) { // Time dependent calib is enabled and this is a new collision - mLastCollisionId = track.collisionId(); // Cache last collision ID - timestamp.value = track.collision().bc_as().timestamp(); - LOG(debug) << "Updating parametrization from path '" << parametrizationPath.value << "' and timestamp " << timestamp.value; - if (!ccdb->getForTimeStamp(parametrizationPath.value, timestamp.value)->retrieveParameters(mRespParamsV2, passName.value)) { - if (fatalOnPassNotAvailable) { - LOGF(fatal, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); - } else { - LOGF(warning, "Pass '%s' not available in the retrieved CCDB object", passName.value.data()); + case idxPr: { + nSigma = responsePr.GetSeparation(mRespParamsV3, trk); + if (mcRecalib.enable && trk.has_mcParticle()) { + if (std::abs(trk.mcParticle().pdgCode()) == kProton) { // we rescale only true signal + nSigma = applyMcRecalib(pidId, trk.pt(), nSigma); + } + } + aod::pidutils::packInTable(nSigma, tablePIDPr); + break; } + default: + LOG(fatal) << "Wrong particle ID for standard tables"; + break; } - } - - // in case of MC recalibrations, check if the objects from CCDB has to be updated - if (mcRecalib.enable) { - int runNumber = track.collision().bc_as().runNumber(); - if (runNumber != currentRun) { - // update postcalibration files - retrieveMcPostCalibFromCcdb(timestamp); + if (enableQaHistograms) { + hnSigma[pidId]->Fill(trk.p(), nSigma); } - currentRun = runNumber; } - float nSigma{-999.f}; - for (auto const& pidId : mEnabledParticles) { // Loop on enabled particle hypotheses + for (auto const& pidId : mEnabledParticlesFull) { // Loop on enabled particle hypotheses with full tables switch (pidId) { - case 2: - resolution = responsePi.GetExpectedSigma(mRespParamsV2, track); - nSigma = responsePi.GetSeparation(mRespParamsV2, track, resolution); - if (mcRecalib.enable && track.has_mcParticle()) { - if (std::abs(track.mcParticle().pdgCode()) == kPiPlus) { // we rescale only true signal - nSigma = applyMcRecalib(pidId, track.pt(), nSigma); + case idxPi: { + resolution = responsePi.GetExpectedSigma(mRespParamsV3, trk); + nSigma = responsePi.GetSeparation(mRespParamsV3, trk); + if (mcRecalib.enable && trk.has_mcParticle()) { + if (std::abs(trk.mcParticle().pdgCode()) == kPiPlus) { // we rescale only true signal + nSigma = applyMcRecalib(pidId, trk.pt(), nSigma); } } - tablePIDPi(resolution, nSigma); + tablePIDFullPi(resolution, nSigma); break; - case 3: - resolution = responseKa.GetExpectedSigma(mRespParamsV2, track); - nSigma = responseKa.GetSeparation(mRespParamsV2, track, resolution); - if (mcRecalib.enable && track.has_mcParticle()) { - if (std::abs(track.mcParticle().pdgCode()) == kKPlus) { // we rescale only true signal - nSigma = applyMcRecalib(pidId, track.pt(), nSigma); + } + case idxKa: { + resolution = responseKa.GetExpectedSigma(mRespParamsV3, trk); + nSigma = responseKa.GetSeparation(mRespParamsV3, trk, resolution); + if (mcRecalib.enable && trk.has_mcParticle()) { + if (std::abs(trk.mcParticle().pdgCode()) == kKPlus) { // we rescale only true signal + nSigma = applyMcRecalib(pidId, trk.pt(), nSigma); } } - tablePIDKa(resolution, nSigma); + tablePIDFullKa(resolution, nSigma); break; - case 4: - resolution = responsePr.GetExpectedSigma(mRespParamsV2, track); - nSigma = responsePr.GetSeparation(mRespParamsV2, track, resolution); - if (mcRecalib.enable && track.has_mcParticle()) { - if (std::abs(track.mcParticle().pdgCode()) == kProton) { // we rescale only true signal - nSigma = applyMcRecalib(pidId, track.pt(), nSigma); + } + case idxPr: { + resolution = responsePr.GetExpectedSigma(mRespParamsV3, trk); + nSigma = responsePr.GetSeparation(mRespParamsV3, trk, resolution); + if (mcRecalib.enable && trk.has_mcParticle()) { + if (std::abs(trk.mcParticle().pdgCode()) == kProton) { // we rescale only true signal + nSigma = applyMcRecalib(pidId, trk.pt(), nSigma); } } - tablePIDPr(resolution, nSigma); + tablePIDFullPr(resolution, nSigma); break; + } default: - LOG(fatal) << "Wrong particle ID in processWoSlice()"; + LOG(fatal) << "Wrong particle ID for full tables"; break; } + if (enableQaHistograms) { + hnSigmaFull[pidId]->Fill(trk.p(), nSigma); + } } } } - PROCESS_SWITCH(mcPidTof, processWoSlice, "Process without track slices and on TrackIU (faster but only Run3)", false); + PROCESS_SWITCH(mcPidTof, processFillTables, "Process with table filling", true); }; -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { return WorkflowSpec{adaptAnalysisTask(cfgc)}; } +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + // Parse the metadata + metadataInfo.initMetadata(cfgc); + auto workflow = WorkflowSpec{adaptAnalysisTask(cfgc)}; + workflow.push_back(adaptAnalysisTask(cfgc)); + workflow.push_back(adaptAnalysisTask(cfgc)); + return workflow; +} From 7825924f21ede8d8561ec0a0a00f271db310a7cd Mon Sep 17 00:00:00 2001 From: Fabrizio Grosa Date: Mon, 28 Oct 2024 09:29:46 +0100 Subject: [PATCH 2/5] Fix comment --- PWGHF/TableProducer/mcPidTof.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index f24b4ffd956..a324f91022b 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -12,7 +12,7 @@ /// /// \file mcPidTof.cxx /// \author Fabrizio Grosa fabrizio.grosa@cern.ch -/// \brief Task to produce PID tables for TOF split for pi, K, p, copied from https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/PID/pidTof.cxx +/// \brief Task to produce PID tables for TOF split for pi, K, p, copied from https://github.com/AliceO2Group/O2Physics/blob/master/Common/TableProducer/PID/pidTofMerge.cxx /// It works only for MC and adds the possibility to apply postcalibrations for MC. /// #include From 0caabed0b51e99d34dc0c2575177b2171328110e Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 28 Oct 2024 08:30:23 +0000 Subject: [PATCH 3/5] Please consider the following formatting changes --- PWGHF/TableProducer/mcPidTof.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index a324f91022b..0014b653eff 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -694,7 +694,7 @@ struct mcPidTof { HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; - // Running variables + // Running variables std::vector mEnabledParticles; // Vector of enabled PID hypotheses to loop on when making tables std::vector mEnabledParticlesFull; // Vector of enabled PID hypotheses to loop on when making full tables void init(o2::framework::InitContext& initContext) From 0befa5b6c0309cfde9dbbe2f3cafd52777abc9a5 Mon Sep 17 00:00:00 2001 From: Fabrizio Grosa Date: Mon, 28 Oct 2024 09:37:45 +0100 Subject: [PATCH 4/5] Remove unnecessary configurable --- PWGHF/TableProducer/mcPidTof.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index 0014b653eff..9d42157e106 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -689,7 +689,6 @@ struct mcPidTof { std::string prefix = "mcRecalib"; Configurable enable{"enable", false, "enable MC recalibration for Pi/Ka/Pr"}; Configurable ccdbPath{"ccdbPath", "Users/f/fgrosa/RecalibmcPidTof/", "path for MC recalibration objects in CCDB"}; - Configurable passName{"passName", "apass6", "reco pass of MC anchoring"}; } mcRecalib; HistogramRegistry histos{"Histos", {}, OutputObjHandlingPolicy::AnalysisObject}; From e1d84eca852c7ce4da18c15ef57cb6903832198a Mon Sep 17 00:00:00 2001 From: Fabrizio Grosa Date: Mon, 28 Oct 2024 11:06:53 +0100 Subject: [PATCH 5/5] Remove enabling of tables for debug --- PWGHF/TableProducer/mcPidTof.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/PWGHF/TableProducer/mcPidTof.cxx b/PWGHF/TableProducer/mcPidTof.cxx index 9d42157e106..7cd32ff8eb7 100644 --- a/PWGHF/TableProducer/mcPidTof.cxx +++ b/PWGHF/TableProducer/mcPidTof.cxx @@ -716,9 +716,6 @@ struct mcPidTof { mEnabledParticlesFull.push_back(supportedSpecies[iSpecie]); } } - mEnabledParticlesFull.push_back(idxPi); - mEnabledParticlesFull.push_back(idxKa); - mEnabledParticlesFull.push_back(idxPr); if (mEnabledParticlesFull.size() == 0 && mEnabledParticles.size() == 0) { LOG(info) << "No PID tables are required, disabling process function"; doprocessFillTables.value = false;