From d27643f5b79ada2efdcb8e9e2082361754d9c9e7 Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 8 Dec 2025 22:54:07 +0100 Subject: [PATCH] workflow to study ITS residuals --- .../study/CMakeLists.txt | 9 + .../include/GlobalTrackingStudy/CheckResid.h | 27 + .../GlobalTrackingStudy/CheckResidConfig.h | 38 ++ .../GlobalTrackingStudy/CheckResidTypes.h | 42 ++ .../study/src/CheckResid.cxx | 556 ++++++++++++++++++ .../study/src/CheckResidConfig.cxx | 14 + .../study/src/GlobalTrackingStudyLinkDef.h | 6 + .../study/src/check-resid-workflow.cxx | 78 +++ 8 files changed, 770 insertions(+) create mode 100644 Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h create mode 100644 Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h create mode 100644 Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h create mode 100644 Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx create mode 100644 Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx create mode 100644 Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx diff --git a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt index 776d3946283c3..df42af503db46 100644 --- a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt @@ -25,6 +25,8 @@ o2_add_library(GlobalTrackingStudy src/TrackMCStudyConfig.cxx src/TrackMCStudyTypes.cxx src/TPCClusSelector.cxx + src/CheckResid.cxx + src/CheckResidConfig.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers @@ -38,6 +40,8 @@ o2_target_root_dictionary(GlobalTrackingStudy include/GlobalTrackingStudy/TrackInfoExt.h include/GlobalTrackingStudy/TrackMCStudyConfig.h include/GlobalTrackingStudy/TrackMCStudyTypes.h + include/GlobalTrackingStudy/CheckResidTypes.h + include/GlobalTrackingStudy/CheckResidConfig.h LINKDEF src/GlobalTrackingStudyLinkDef.h ) @@ -76,6 +80,11 @@ o2_add_executable(dump-workfow SOURCES src/track-dump-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) +o2_add_executable(resid-workfow + COMPONENT_NAME check + SOURCES src/check-resid-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + if (OpenMP_CXX_FOUND) target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h new file mode 100644 index 0000000000000..a78fa5e8d41da --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h @@ -0,0 +1,27 @@ +// 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 O2_CHECK_RESID_H +#define O2_CHECK_RESID_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +// #include "TPCCalibration/CorrectionMapsLoader.h" + +namespace o2::checkresid +{ +/// create a processor spec +o2::framework::DataProcessorSpec getCheckResidSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/); + +} // namespace o2::checkresid + +#endif // O2_CHECK_RESID_H diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h new file mode 100644 index 0000000000000..53dffeed7ad69 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h @@ -0,0 +1,38 @@ +// 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 O2_CHECK_RESID_CONFIG_H +#define O2_CHECK_RESID_CONFIG_H +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::checkresid +{ +struct CheckResidConfig : o2::conf::ConfigurableParamHelper { + int minPVContributors = 10; + int minTPCCl = 60; + int minITSCl = 7; + float minPt = 0.4f; + float maxPt = 100.f; + float rCompIBOB = 12.f; + + bool pvcontribOnly = true; + bool addPVAsCluster = true; + bool refitPV = true; + bool useStableRef = true; + bool doIBOB = true; + bool doResid = true; + + O2ParamDef(CheckResidConfig, "checkresid"); +}; +} // namespace o2::checkresid + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h new file mode 100644 index 0000000000000..ebb6a7aabe9fa --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h @@ -0,0 +1,42 @@ +// 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 O2_CHECK_RESID_TYPES_H +#define O2_CHECK_RESID_TYPES_H + +#include "ReconstructionDataFormats/Track.h" + +namespace o2::checkresid +{ +struct Point { + float dy = 0.f; + float dz = 0.f; + float sig2y = 0.f; + float sig2z = 0.f; + float phi = 0.f; + float z = 0.f; + int16_t sens = -1; + int8_t lr = -1; // -1 = vtx + ClassDefNV(Point, 1) +}; + +struct Track { + o2::dataformats::GlobalTrackID gid{}; + o2::track::TrackPar track; + o2::track::TrackParCov trIBOut; + o2::track::TrackParCov trOBInw; + std::vector points; + ClassDefNV(Track, 1) +}; + +} // namespace o2::checkresid + +#endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx new file mode 100644 index 0000000000000..34643928db344 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -0,0 +1,556 @@ +// 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 "GlobalTrackingStudy/CheckResid.h" +#include "GlobalTrackingStudy/CheckResidTypes.h" +#include "GlobalTrackingStudy/CheckResidConfig.h" +#include +#include "ReconstructionDataFormats/Track.h" +#include +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsITSMFT/TrkClusRef.h" +#include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "SimulationDataFormat/MCEventLabel.h" +#include "SimulationDataFormat/MCUtils.h" +#include "CommonUtils/NameConf.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "ITSBase/GeometryTGeo.h" +#include "ITStracking/IOUtils.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "DetectorsVertexing/PVertexer.h" + +#ifdef WITH_OPENMP +#include +#endif + +namespace o2::checkresid +{ + +using namespace o2::framework; +using DetID = o2::detectors::DetID; +using DataRequest = o2::globaltracking::DataRequest; + +using PVertex = o2::dataformats::PrimaryVertex; +using V2TRef = o2::dataformats::VtxTrackRef; +using VTIndex = o2::dataformats::VtxTrackIndex; +using GTrackID = o2::dataformats::GlobalTrackID; +using timeEst = o2::dataformats::TimeStampWithError; + +class CheckResidSpec : public Task +{ + public: + CheckResidSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) + { + /* + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + */ + } + ~CheckResidSpec() final = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; + void process(); + + private: + void updateTimeDependentParams(ProcessingContext& pc); + bool refitPV(o2::dataformats::PrimaryVertex& pv, int vid); + bool refitITStrack(o2::track::TrackParCov& track, GTrackID gid); + bool processITSTrack(const o2::its::TrackITS& iTrack, const o2::dataformats::PrimaryVertex& pv, o2::checkresid::Track& resTrack); + + o2::globaltracking::RecoContainer* mRecoData = nullptr; + int mNThreads = 1; + float mITSROFrameLengthMUS = 0.f; + o2::dataformats::MeanVertexObject mMeanVtx{}; + std::vector> mITSClustersArray; ///< ITS clusters created in run() method from compact clusters + const o2::itsmft::TopologyDictionary* mITSDict = nullptr; ///< cluster patterns dictionary + o2::vertexing::PVertexer mVertexer; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + bool mUseMC{false}; ///< MC flag + std::unique_ptr mDBGOut; + GTrackID::mask_t mTracksSrc{}; +}; + +void CheckResidSpec::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + int lane = ic.services().get().inputTimesliceId; + int maxLanes = ic.services().get().maxInputTimeslices; + std::string dbgnm = maxLanes == 1 ? "checkResid.root" : fmt::format("checkResid_t{}.root", lane); + mDBGOut = std::make_unique(dbgnm.c_str(), "recreate"); + mNThreads = ic.options().get("nthreads"); +#ifndef WITH_OPENMP + if (mNThreads > 1) { + LOGP(warn, "No OpenMP"); + } + mNThreads = 1; +#endif + // mTPCCorrMapsLoader.init(ic); +} + +void CheckResidSpec::run(ProcessingContext& pc) +{ + o2::globaltracking::RecoContainer recoData; + mRecoData = &recoData; + mRecoData->collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer + mRecoData = &recoData; + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(); + mRecoData = nullptr; +} + +void CheckResidSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + pc.inputs().get("meanvtx"); + // mTPCVDriftHelper.extractCCDBInputs(pc); + // mTPCCorrMapsLoader.extractCCDBInputs(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + // Note: reading of the ITS AlpideParam needed for ITS timing is done by the RecoContainer + auto grp = o2::base::GRPGeomHelper::instance().getGRPECS(); + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + if (!grp->isDetContinuousReadOut(DetID::ITS)) { + mITSROFrameLengthMUS = alpParams.roFrameLengthTrig / 1.e3; // ITS ROFrame duration in \mus + } else { + mITSROFrameLengthMUS = alpParams.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; // ITS ROFrame duration in \mus + } + auto geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + o2::conf::ConfigurableParam::updateFromString("pvertexer.useMeanVertexConstraint=false"); + mVertexer.init(); + } + bool updateMaps = false; + /* + if (mTPCCorrMapsLoader.isUpdated()) { + mTPCCorrMapsLoader.acknowledgeUpdate(); + updateMaps = true; + } + if (mTPCVDriftHelper.isUpdated()) { + LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, + mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, + mTPCVDriftHelper.getSourceName()); + mTPCVDriftHelper.acknowledgeUpdate(); + updateMaps = true; + } + if (updateMaps) { + mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); + } + */ +} + +void CheckResidSpec::process() +{ + if (!mITSDict) { + LOGP(fatal, "ITS data is not loaded"); + } + const auto itsTracks = mRecoData->getITSTracks(); + // const auto itsLbls = mRecoData->getITSTracksMCLabels(); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto clusITS = mRecoData->getITSClusters(); + const auto patterns = mRecoData->getITSClustersPatterns(); + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + auto pattIt = patterns.begin(); + mITSClustersArray.clear(); + mITSClustersArray.reserve(clusITS.size()); + + o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + + auto pvvec = mRecoData->getPrimaryVertices(); + auto trackIndex = mRecoData->getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks + auto vtxRefs = mRecoData->getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto prop = o2::base::Propagator::Instance(); + static int TFCount = 0; + int nv = vtxRefs.size() - 1; + std::vector> slots; + slots.resize(mNThreads); + int nvGood = 0, nvUse = 0, nvRefFail = 0; + long pvFitDuration{}; + for (int iv = 0; iv < nv; iv++) { + const auto& vtref = vtxRefs[iv]; + auto pve = pvvec[iv]; + if (pve.getNContributors() < params.minPVContributors) { + continue; + } + nvGood++; + if (params.refitPV) { + auto tStartPVF = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + bool res = refitPV(pve, iv); + pvFitDuration += std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - tStartPVF; + if (!res) { + nvRefFail++; + continue; + } + } + nvUse++; + for (int is = 0; is < GTrackID::NSources; is++) { + if (!mTracksSrc[is] || !mRecoData->isTrackSourceLoaded(is)) { + continue; + } + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); + DetID::mask_t dm = GTrackID::getSourceDetectorsMask(is); + if (!dm[DetID::ITS]) { + continue; + } + if (dm[DetID::TPC] && params.minTPCCl > 0 && !mRecoData->isTrackSourceLoaded(GTrackID::TPC)) { + LOGP(fatal, "Cut on TPC tracks is requested by they are not loaded"); + } +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int i = idMin; i < idMax; i++) { + auto vid = trackIndex[i]; + bool pvCont = vid.isPVContributor(); + if (!pvCont && params.pvcontribOnly) { + continue; + } + if (dm[DetID::TPC] && params.minTPCCl > 0 && mRecoData->getTPCTrack(mRecoData->getTPCContributorGID(vid)).getNClusters() < params.minTPCCl) { + continue; + } + auto gidITS = mRecoData->getITSContributorGID(vid); + if (gidITS.getSource() != GTrackID::ITS) { + continue; + } + const auto& trc = mRecoData->getTrackParam(vid); + auto pt = trc.getPt(); + if (pt < params.minPt || pt > params.maxPt) { + continue; + } + const auto& itsTrack = mRecoData->getITSTrack(gidITS); + if (itsTrack.getNClusters() < params.minITSCl) { + continue; + } +#ifdef WITH_OPENMP + auto& accum = slots[omp_get_thread_num()]; +#else + auto& accum = slots[0]; +#endif + auto& resTrack = accum.emplace_back(); + if (!processITSTrack(itsTrack, pve, resTrack)) { + accum.pop_back(); + continue; + } + } + } + } + // output + for (const auto& accum : slots) { + for (const auto& tr : accum) { + (*mDBGOut) << "res" << "tr=" << tr << "\n"; + } + } + LOGP(info, "processed {} PVs out of {} good vertices (out of {} in total), PV refits took {} mus, {} refits failed", nvUse, nvGood, nv, pvFitDuration, nvRefFail); + TFCount++; +} + +bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2::dataformats::PrimaryVertex& pv, o2::checkresid::Track& resTrack) +{ + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + auto trFitInw = iTrack.getParamOut(); // seed for inward refit + auto trFitOut = iTrack.getParamIn(); // seed for outward refit + auto prop = o2::base::Propagator::Instance(); + auto geom = o2::its::GeometryTGeo::Instance(); + float pvAlpha = 0; + float bz = prop->getNominalBz(); + std::array*, 8> clArr{}; + const auto& params = CheckResidConfig::Instance(); + std::array extrapOut, extrapInw; // 2-way Kalman extrapolations, vertex + 7 layers + + auto rotateTrack = [bz](o2::track::TrackParCov& tr, float alpha, o2::track::TrackPar* refLin) { + return refLin ? tr.rotate(alpha, *refLin, bz) : tr.rotate(alpha); + }; + + auto accountCluster = [&](int i, std::array& extrapDest, o2::track::TrackParCov& tr, o2::track::TrackPar* refLin) { + if (clArr[i]) { // update with cluster + if (!rotateTrack(tr, i == 0 ? pvAlpha : geom->getSensorRefAlpha(clArr[i]->getSensorID()), refLin) || + !prop->propagateTo(tr, refLin, clArr[i]->getX(), true)) { + return 0; + } + extrapDest[i] = tr; // before update + if (!tr.update(*clArr[i])) { + return 0; + } + } else { + extrapDest[i].invalidate(); + return -1; + } + return 1; + }; + + auto inv2d = [](float s00, float s11, float s01) -> std::array { + auto det = s00 * s11 - s01 * s01; + if (det < 1e-16) { + return {0.f, 0.f, 0.f}; + } + det = 1.f / det; + return {s11 * det, s00 * det, -s01 * det}; + }; + + resTrack.points.clear(); + if (!prop->propagateToDCA(pv, trFitOut, bz)) { + return false; + } + float cosAlp, sinAlp; + pvAlpha = trFitOut.getAlpha(); + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); // vertex position rotated to track frame + o2::BaseCluster bcPV; + if (params.addPVAsCluster) { + bcPV.setXYZ(pv.getX() * cosAlp + pv.getY() * sinAlp, -pv.getX() * sinAlp + pv.getY() * cosAlp, pv.getZ()); + bcPV.setSigmaY2(0.5 * (pv.getSigmaX2() + pv.getSigmaY2())); + bcPV.setSigmaZ2(pv.getSigmaZ2()); + bcPV.setSensorID(-1); + clArr[0] = &bcPV; + } + // collect all track clusters to array, placing them to layer+1 slot + int nCl = iTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { // clusters are ordered from the outermost to the innermost + const auto& curClu = mITSClustersArray[itsClRefs[iTrack.getClusterEntry(i)]]; + + int llr = geom->getLayer(curClu.getSensorID()); + if (clArr[1 + llr]) { + LOGP(error, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), curClu.getSensorID()); + } + clArr[1 + geom->getLayer(curClu.getSensorID())] = &curClu; + } + o2::track::TrackPar refLinInw0, refLinOut0, *refLinOut = nullptr, *refLinInw = nullptr; + o2::track::TrackPar refLinIBOut0, refLinOBInw0, *refLinOBInw = nullptr, *refLinIBOut = nullptr; + if (params.useStableRef) { + refLinOut = &(refLinOut0 = trFitOut); + refLinInw = &(refLinInw0 = trFitInw); + } + trFitOut.resetCovariance(); + trFitOut.setCov(trFitOut.getQ2Pt() * trFitOut.getQ2Pt() * trFitOut.getCov()[14], 14); + trFitInw.resetCovariance(); + trFitInw.setCov(trFitInw.getQ2Pt() * trFitInw.getQ2Pt() * trFitInw.getCov()[14], 14); + // fit in inward and outward direction + for (int i = 0; i <= 7; i++) { + int resOut, resInw; + // process resOut in ascending order (0-->7) and resInw in descending order (7-->0) + if (!(resOut = accountCluster(i, extrapOut, trFitOut, refLinOut)) || !(resInw = accountCluster(7 - i, extrapInw, trFitInw, refLinInw))) { + return false; + } + // at layer 3, find the IB track (trIBOut) and the OB track (trOBInw) + // propagate both trcaks to a common radius, RCompIBOB (12cm), and rotates + // them to the same reference frame for comparison + if (i == 3 && resOut == 1 && resInw == 1 && params.doIBOB && nCl == 7) { + resTrack.trIBOut = trFitOut; // outward track updated at outermost IB layer + resTrack.trOBInw = trFitInw; // inward track updated at innermost OB layer + o2::track::TrackPar refLinIBOut0, refLinIBIn0; + if (refLinOut) { + refLinIBOut = &(refLinIBOut0 = refLinOut0); + refLinOBInw = &(refLinOBInw0 = refLinInw0); + } + float xRref; + if (!resTrack.trOBInw.getXatLabR(params.rCompIBOB, xRref, bz) || + !prop->propagateTo(resTrack.trOBInw, refLinOBInw, xRref, true) || + !rotateTrack(resTrack.trOBInw, resTrack.trOBInw.getPhiPos(), refLinOBInw) || // propagate OB track to ref R and rotate + !rotateTrack(resTrack.trIBOut, resTrack.trOBInw.getAlpha(), refLinIBOut) || + !prop->propagateTo(resTrack.trIBOut, refLinIBOut, resTrack.trOBInw.getX(), true)) { // rotate OB track to same frame and propagate to same X + // if any propagation or rotation steps fail, invalidate both tracks + return false; + } + } + } + + bool innerDone = false; + if (params.doResid) { + for (int i = 0; i <= 7; i++) { + if (clArr[i]) { + // calculate interpolation as a weighted mean of inward/outward extrapolations to this layer + const auto &tInw = extrapInw[i], &tOut = extrapOut[i]; + auto wInw = inv2d(tInw.getSigmaY2(), tInw.getSigmaZ2(), tInw.getSigmaZY()); + auto wOut = inv2d(tOut.getSigmaY2(), tOut.getSigmaZ2(), tOut.getSigmaZY()); + if (wInw[0] == 0.f || wOut[0] == 0.f) { + return -1; + } + std::array wTot = {wInw[0] + wOut[0], wInw[1] + wOut[1], wInw[2] + wOut[2]}; + auto cTot = inv2d(wTot[0], wTot[1], wTot[2]); + auto ywi = wInw[0] * tInw.getY() + wInw[2] * tInw.getZ() + wOut[0] * tOut.getY() + wOut[2] * tOut.getZ(); + auto zwi = wInw[2] * tInw.getY() + wInw[1] * tInw.getZ() + wOut[2] * tOut.getY() + wOut[1] * tOut.getZ(); + auto yw = ywi * cTot[0] + zwi * cTot[2]; + auto zw = ywi * cTot[2] + zwi * cTot[1]; + // posCl.push_back(clArr[i]->getXYZGlo(*o2::its::GeometryTGeo::Instance())); + auto phi = i == 0 ? tInw.getPhi() : tInw.getPhiPos(); + o2::math_utils::bringTo02Pi(phi); + resTrack.points.emplace_back(clArr[i]->getY() - yw, clArr[i]->getZ() - zw, cTot[0] + clArr[i]->getSigmaY2(), cTot[1] + clArr[i]->getSigmaZ2(), phi, clArr[i]->getZ(), clArr[i]->getSensorID(), i - 1); + if (!innerDone) { + resTrack.track = tInw; + innerDone = true; + } + } else { + LOGP(warn, "No cluster on lr {}", i); + } + } + } + return true; +} + +bool CheckResidSpec::refitPV(o2::dataformats::PrimaryVertex& pv, int vid) +{ + const auto& params = o2::checkresid::CheckResidConfig::Instance(); + std::vector tracks; + std::vector useTrack; + std::vector gidsITS; + int ntr = pv.getNContributors(); + tracks.reserve(ntr); + useTrack.reserve(ntr); + gidsITS.reserve(ntr); + const auto& vtref = mRecoData->getPrimaryVertexMatchedTrackRefs()[vid]; + auto trackIndex = mRecoData->getPrimaryVertexMatchedTracks(); + int itr = vtref.getFirstEntry(), itLim = itr + vtref.getEntries(); + for (; itr < itLim; itr++) { + auto vid = trackIndex[itr]; + if (vid.isPVContributor()) { + tracks.emplace_back().setPID(mRecoData->getTrackParam(vid).getPID()); + gidsITS.push_back(mRecoData->getITSContributorGID(vid)); + } + } + ntr = tracks.size(); + useTrack.resize(ntr); +#ifdef WITH_OPENMP +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) +#endif + for (int itr = 0; itr < ntr; itr++) { + if (!(useTrack[itr] = refitITStrack(tracks[itr], gidsITS[itr]))) { + tracks[itr] = mRecoData->getTrackParam(gidsITS[itr]); // this track will not be used but participates in prepareVertexRefit + } + } + ntr = 0; + for (auto v : useTrack) { + ntr++; + } + if (ntr < params.minPVContributors || !mVertexer.prepareVertexRefit(tracks, pv)) { + return false; + } + // readjust vertexZ + const auto& pool = mVertexer.getTracksPool(); + float zUpd = 0; + for (const auto& t : pool) { + zUpd += t.z; + } + if (pool.size()) { + pv.setZ(zUpd / pool.size()); + mVertexer.prepareVertexRefit(tracks, pv); + } + pv = mVertexer.refitVertex(useTrack, pv); + return pv.getChi2() > 0.f; +} + +bool CheckResidSpec::refitITStrack(o2::track::TrackParCov& track, GTrackID gid) +{ + // destination tack might have non-default PID assigned + const auto& trkITS = mRecoData->getITSTrack(gid); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto& params = CheckResidConfig::Instance(); + auto pid = track.getPID(); + track = trkITS.getParamOut(); + track.setPID(pid); + auto nCl = trkITS.getNumberOfClusters(); + auto geom = o2::its::GeometryTGeo::Instance(); + auto prop = o2::base::Propagator::Instance(); + float bz = prop->getNominalBz(); + o2::track::TrackPar refLin{track}; + + for (int iCl = 0; iCl < nCl; iCl++) { // clusters are stored from outer to inner layers + const auto& cls = mITSClustersArray[itsClRefs[trkITS.getClusterEntry(iCl)]]; + auto alpha = geom->getSensorRefAlpha(cls.getSensorID()); + if (!(params.useStableRef ? track.rotate(alpha, refLin, bz) : track.rotate(alpha)) || + !prop->propagateTo(track, params.useStableRef ? &refLin : nullptr, cls.getX(), true)) { + LOGP(debug, "refitITStrack failed on propagation to cl#{}, alpha={}, x={} | {}", iCl, alpha, cls.getX(), track.asString()); + return false; + } + if (!track.update(cls)) { + LOGP(debug, "refitITStrack failed on update with cl#{}, | {}", iCl, track.asString()); + return false; + } + } + return true; +} + +void CheckResidSpec::endOfStream(EndOfStreamContext& ec) +{ + mDBGOut.reset(); +} + +void CheckResidSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + /* + if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { + return; + } + if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + return; + } + */ + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mMeanVtx = *(const o2::dataformats::MeanVertexObject*)obj; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } +} + +DataProcessorSpec getCheckResidSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + Options opts{ + {"nthreads", VariantType::Int, 1, {"number of threads"}}, + }; + // o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + // o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + + return DataProcessorSpec{ + "check-resid", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC /*, sclOpts*/)}, + opts}; +} + +} // namespace o2::checkresid diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx new file mode 100644 index 0000000000000..a754d1196017f --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx @@ -0,0 +1,14 @@ +// 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 "GlobalTrackingStudy/CheckResidConfig.h" + +O2ParamImpl(o2::checkresid::CheckResidConfig); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h index 6075429b0b16e..416820fc9aebb 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h +++ b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h @@ -43,4 +43,10 @@ #pragma link C++ class o2::trackstudy::ITSHitInfo + ; #pragma link C++ class std::vector < o2::trackstudy::ITSHitInfo> + ; +#pragma link C++ class o2::checkresid::Point + ; +#pragma link C++ class std::vector < o2::checkresid::Point> + ; +#pragma link C++ class o2::checkresid::Track + ; +#pragma link C++ class o2::checkresid::CheckResidConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::checkresid::CheckResidConfig> + ; + #endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx new file mode 100644 index 0000000000000..b8230b59405d8 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx @@ -0,0 +1,78 @@ +// 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 "GlobalTrackingStudy/CheckResid.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/CallbacksPolicy.h" +#include "DetectorsBase/DPLWorkflowUtils.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCWorkflow/TPCScalerSpec.h" + +using namespace o2::framework; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +// ------------------------------------------------------------------ +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"enable-mc", o2::framework::VariantType::Bool, false, {"enable MC propagation"}}, + {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, + {"cluster-sources", VariantType::String, "ITS", {"comma-separated list of cluster sources to use"}}, + {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + // o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); + o2::raw::HBFUtilsInitializer::addConfigOption(options); + std::swap(workflowOptions, options); +} + +// ------------------------------------------------------------------ + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) +{ + WorkflowSpec specs; + + GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS"); + + // Update the (declared) parameters if changed from the command line + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + // auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); + auto useMC = configcontext.options().get("enable-mc"); + + GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); + GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed + + specs.emplace_back(o2::checkresid::getCheckResidSpec(srcTrc, srcCls, useMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + + return std::move(specs); +}