diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt index 0e222a580b653..1166a7d432dfa 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt @@ -11,8 +11,13 @@ o2_add_library(ITSPostprocessing SOURCES src/ImpactParameter.cxx - src/K0sInvMass.cxx + src/AvgClusSize.cxx + src/ITSStudiesConfigParam.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers - O2::DataFormatsGlobalTracking) \ No newline at end of file + O2::DataFormatsGlobalTracking) + +o2_target_root_dictionary(ITSPostprocessing + HEADERS include/ITSStudies/ITSStudiesConfigParam.h + LINKDEF src/ITSStudiesLinkDef.h) \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h new file mode 100644 index 0000000000000..d2ba7e88be14e --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/AvgClusSize.h @@ -0,0 +1,151 @@ +// 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 AvgClusSize.h +/// \author Tucker Hwang mhwang@cern.ch + +#ifndef O2_AVGCLUSSIZE_STUDY_H +#define O2_AVGCLUSSIZE_STUDY_H + +#include "Framework/DataProcessorSpec.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ITStracking/IOUtils.h" +#include "DataFormatsITS/TrackITS.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "Framework/Task.h" +#include + +#include "ITSStudies/ITSStudiesConfigParam.h" + +#include +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using ITSCluster = o2::BaseCluster; +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +using MCLabel = o2::MCCompLabel; + +class AvgClusSizeStudy : public Task +{ + public: + AvgClusSizeStudy(std::shared_ptr dr, std::shared_ptr gr, bool isMC) : mDataRequest{dr}, mGGCCDBRequest(gr), mUseMC(isMC){}; + ~AvgClusSizeStudy() = default; + void init(InitContext& ic) final; + void run(ProcessingContext&) final; + void endOfStream(EndOfStreamContext&) final; + void finaliseCCDB(ConcreteDataMatcher&, void*) final; + void setClusterDictionary(const o2::itsmft::TopologyDictionary* d) { mDict = d; } + + private: + // Other functions + void process(o2::globaltracking::RecoContainer&); + void loadData(o2::globaltracking::RecoContainer&); + + // Helper functions + void prepareOutput(); + void setStyle(); + void updateTimeDependentParams(ProcessingContext& pc); + float getAverageClusterSize(o2::its::TrackITS*); + void getClusterSizes(std::vector&, const gsl::span, gsl::span::iterator&, const o2::itsmft::TopologyDictionary*); + void fitMassSpectrum(); + void saveHistograms(); + void plotHistograms(); + void fillEtaBin(double eta, double clusSize, int i); + + // Running options + bool mUseMC; + + // Data + std::shared_ptr mGGCCDBRequest; + std::shared_ptr mDataRequest; + std::vector mInputClusterSizes; + gsl::span mInputITSidxs; + std::vector mMCTracks; + const o2::itsmft::TopologyDictionary* mDict = nullptr; + + // Output plots + std::unique_ptr mDBGOut; + std::unique_ptr mOutputNtuple; + + std::unique_ptr mMassSpectrumFull{}; + std::unique_ptr mMassSpectrumFullNC{}; + std::unique_ptr mMassSpectrumFullC{}; + std::unique_ptr mMassSpectrumK0s{}; + std::unique_ptr mMassSpectrumK0sNC{}; + std::unique_ptr mMassSpectrumK0sC{}; + std::unique_ptr mAvgClusSize{}; + std::unique_ptr mAvgClusSizeNC{}; + std::unique_ptr mAvgClusSizeC{}; + std::unique_ptr mAvgClusSizeCEta{}; + std::vector> mAvgClusSizeCEtaVec{}; + std::unique_ptr mMCStackCosPA{}; + std::unique_ptr mStackDCA{}; + std::unique_ptr mStackR{}; + std::unique_ptr mStackPVDCA{}; + std::unique_ptr mCosPA{}; + std::unique_ptr mMCCosPAK0{}; + std::unique_ptr mMCCosPAnotK0{}; + std::unique_ptr mCosPAtrueK0{}; + std::unique_ptr mR{}; + std::unique_ptr mRK0{}; + std::unique_ptr mRnotK0{}; + std::unique_ptr mRtrueK0{}; + std::unique_ptr mDCA{}; + std::unique_ptr mDCAK0{}; + std::unique_ptr mDCAnotK0{}; + std::unique_ptr mDCAtrueK0{}; + std::unique_ptr mEtaNC{}; + std::unique_ptr mEtaC{}; + std::unique_ptr mMCMotherPDG{}; + std::unique_ptr mPVDCAK0{}; + std::unique_ptr mPVDCAnotK0{}; + + int globalNClusters = 0; + int globalNPixels = 0; + + std::vector mEtaBinUL; // upper edges for eta bins + + // Counters for K0s identification + int nNotValid = 0; + int nNullptrs = 0; + int nPiPi = 0; + int nIsPiPiNotK0s = 0; + int nIsPiPiIsK0s = 0; + int nIsNotPiPiIsK0s = 0; + int nMotherIDMismatch = 0; + int nEvIDMismatch = 0; + int nK0s = 0; + int nNotK0s = 0; + int nPionsInEtaRange = 0; + int nInvalidK0sMother = 0; + + const std::string mOutName{"o2standalone_cluster_size_study.root"}; + std::unique_ptr mMCKinReader; +}; + +o2::framework::DataProcessorSpec getAvgClusSizeStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC); +} // namespace study +} // namespace its +} // namespace o2 + +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h new file mode 100644 index 0000000000000..a2e4c2b3e1959 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/ITSStudiesConfigParam.h @@ -0,0 +1,56 @@ +// 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 ALICEO2_ITSDPLTRACKINGPARAM_H_ +// #define ALICEO2_ITSDPLTRACKINGPARAM_H_ + +#ifndef O2_AVGCLUSSIZE_STUDY_PARAM_H +#define O2_AVGCLUSSIZE_STUDY_PARAM_H + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace its +{ +namespace study +{ + +struct AvgClusSizeStudyParamConfig : public o2::conf::ConfigurableParamHelper { + + // K0s ID cuts + double Rmin = 0.5; // lower limit on V0 decay length + double Rmax = 5.4; // upper limit on V0 decay length + double cosPAmin = 0.995; // lower limit on cosine of pointing angle + double prongDCAmax = 0.2; // upper limit on DCA between two daughter prongs + double dauPVDCAmin = 0.2; // lower limit on DCA between prong and primary vertex + + // Plotting options + bool performFit = false; // determine if fit to K0s mass spectrum will be done (set to false in the case of low statistics) + bool generatePlots = true; // TODO: not yet tested + + // Average cluster size plot: eta binning parameters + double etaMin = -1.5; // lower edge of lowest bin for eta binning on average cluster size + double etaMax = 1.5; // upper edge for highest bin for eta binning on average cluster size + int etaNBins = 5; // number of eta bins + + // Average cluster size plot: cluster size binning parameters + double sizeMax = 15; // upper edge of highest bin for average cluster size + int sizeNBins = 20; // number of cluster size bins + + O2ParamDef(AvgClusSizeStudyParamConfig, "AvgClusSizeStudyParam"); +}; + +} // namespace study +} // namespace its +} // namespace o2 +#endif diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx new file mode 100644 index 0000000000000..a48d0b55a45d0 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/AvgClusSize.cxx @@ -0,0 +1,603 @@ +// 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 AvgClusSize.cxx +/// \brief Study to calculate average cluster size per track in the ITS +/// \author Tucker Hwang mhwang@cern.ch + +#include "ITSStudies/AvgClusSize.h" + +#include "DataFormatsITS/TrackITS.h" +#include "Framework/Task.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "ReconstructionDataFormats/V0.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "DataFormatsParameters/GRPObject.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "ITSBase/GeometryTGeo.h" +#include +#include "ReconstructionDataFormats/Track.h" +#include "ReconstructionDataFormats/DCA.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace its +{ +namespace study +{ +using namespace o2::framework; +using namespace o2::globaltracking; + +using GTrackID = o2::dataformats::GlobalTrackID; +using PVertex = o2::dataformats::PrimaryVertex; +using V0 = o2::dataformats::V0; +using ITSCluster = o2::BaseCluster; +using mask_t = o2::dataformats::GlobalTrackID::mask_t; +using AvgClusSizeStudy = o2::its::study::AvgClusSizeStudy; +using Track = o2::track::TrackParCov; +using TrackITS = o2::its::TrackITS; +using DCA = o2::dataformats::DCA; + +void AvgClusSizeStudy::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + LOGP(info, "Starting average cluster size study..."); + + prepareOutput(); + + if (mUseMC) { // for counting the missed K0shorts + mMCKinReader = std::make_unique("collisioncontext.root"); + for (int iEvent{0}; iEvent < mMCKinReader->getNEvents(0); iEvent++) { + auto mctrk = mMCKinReader->getTracks(0, iEvent); + mMCTracks.insert(mMCTracks.end(), mctrk.begin(), mctrk.end()); + } + } + + LOGP(important, "Cluster size study initialized."); +} + +void AvgClusSizeStudy::prepareOutput() +{ + auto& params = o2::its::study::AvgClusSizeStudyParamConfig::Instance(); + mDBGOut = std::make_unique(mOutName.c_str(), "recreate"); + mOutputNtuple = std::make_unique("v0_data", "v0 data", "d0ACS:d1ACS:mass:d01dca:cosPA:V0R:eta:d0pvDCA:d1pvDCA:isMCK0s"); + + mMassSpectrumFull = std::make_unique("V0", "V0 mass spectrum; MeV"); // auto-set axis ranges + mMassSpectrumFullNC = std::make_unique("V0nc", "no cuts; MeV", 100, 250, 1000); // auto-set axis ranges + mMassSpectrumFullC = std::make_unique("V0c", "cut; MeV", 100, 250, 1000); // auto-set axis ranges + mMassSpectrumK0s = std::make_unique("K0s", "'K0' mass spectrum; MeV"); // set axis ranges near K0short mass + mMassSpectrumK0sNC = std::make_unique("K0snc", "no cuts; MeV", 15, 475, 525); // set axis ranges near K0short mass + mMassSpectrumK0sC = std::make_unique("K0sc", "cut; MeV", 15, 475, 525); // set axis ranges near K0short mass + mAvgClusSize = std::make_unique("avgclussize", "Average cluster size per track; pixels / cluster / track"); // auto-set axis ranges + mAvgClusSizeNC = std::make_unique("avgclussizeNC", "no cuts", 40, 0, 15); // auto-set axis ranges + mAvgClusSizeC = std::make_unique("avgclussizeC", "cut", 40, 0, 15); // auto-set axis ranges + mMCStackCosPA = std::make_unique("CosPAstack", "CosPA"); // auto-set axis ranges + mStackDCA = std::make_unique("DCAstack", "DCA"); // auto-set axis ranges + mStackR = std::make_unique("Rstack", "R"); // auto-set axis ranges + mStackPVDCA = std::make_unique("PVDCAstack", "PV-DCA"); // auto-set axis ranges + mCosPA = std::make_unique("CosPA", "cos(PA)", 100, -1, 1); // auto-set axis ranges + mMCCosPAK0 = std::make_unique("CosPAK0", "cos(PA)", 100, -1, 1); // auto-set axis ranges + mMCCosPAnotK0 = std::make_unique("CosPAnotK0", "cos(PA)", 100, -1, 1); // auto-set axis ranges + mR = std::make_unique("R", "R", 40, 1, -1); // auto-set axis ranges + mRK0 = std::make_unique("RK0", "R", 40, 0, 20); // auto-set axis ranges + mRnotK0 = std::make_unique("RnotK0", "R", 40, 0, 20); // auto-set axis ranges + mDCA = std::make_unique("DCA", "DCA", 40, 1, -1); // auto-set axis ranges + mDCAK0 = std::make_unique("DCAK0", "DCA", 40, 0, 0.25); // auto-set axis ranges + mDCAnotK0 = std::make_unique("DCAnotK0", "DCA", 40, 0, 0.25); // auto-set axis ranges + mEtaNC = std::make_unique("etaNC", "no cuts", 30, 1, -1); // auto-set axis ranges + mEtaC = std::make_unique("etaC", "cut", 30, 1, -1); // auto-set axis ranges + mMCMotherPDG = std::make_unique("PID", "MC K0s mother PDG codes", 100, 1, -1); + mPVDCAK0 = std::make_unique("PVDCAK0", "Prong DCA to pVertex", 80, 0, 2); + mPVDCAnotK0 = std::make_unique("PVDCAnotK0", "Prong DCA to pVertex", 80, 0, 2); + + mAvgClusSizeCEta = std::make_unique("avgclussizeeta", "Average cluster size per track; pixels / cluster / track"); // auto-set axis ranges + double binWidth = (params.etaMax - params.etaMin) / (double)params.etaNBins; + mEtaBinUL.reserve(params.etaNBins); + for (int i = 0; i < params.etaNBins; i++) { + mEtaBinUL.emplace_back(params.etaMin + (binWidth * (i + 1))); + mAvgClusSizeCEtaVec.push_back(std::make_unique(Form("avgclussize%i", i), Form("%.2f < #eta < %.2f", mEtaBinUL[i] - binWidth, mEtaBinUL[i]), params.sizeNBins, 0, params.sizeMax)); + mAvgClusSizeCEtaVec[i]->SetDirectory(nullptr); + mAvgClusSizeCEta->Add(mAvgClusSizeCEtaVec[i].get()); + } + + mMassSpectrumFullNC->SetDirectory(nullptr); + mMassSpectrumFullC->SetDirectory(nullptr); + mMassSpectrumK0sNC->SetDirectory(nullptr); + mMassSpectrumK0sC->SetDirectory(nullptr); + mAvgClusSizeNC->SetDirectory(nullptr); + mAvgClusSizeC->SetDirectory(nullptr); + mCosPA->SetDirectory(nullptr); + mMCCosPAK0->SetDirectory(nullptr); + mMCCosPAnotK0->SetDirectory(nullptr); + mR->SetDirectory(nullptr); + mRK0->SetDirectory(nullptr); + mRnotK0->SetDirectory(nullptr); + mDCA->SetDirectory(nullptr); + mDCAK0->SetDirectory(nullptr); + mDCAnotK0->SetDirectory(nullptr); + mEtaNC->SetDirectory(nullptr); + mEtaC->SetDirectory(nullptr); + mMCMotherPDG->SetDirectory(nullptr); + mPVDCAK0->SetDirectory(nullptr); + mPVDCAnotK0->SetDirectory(nullptr); + + mOutputNtuple->SetDirectory(nullptr); + + mMassSpectrumFull->Add(mMassSpectrumFullC.get()); + mMassSpectrumFull->Add(mMassSpectrumFullNC.get()); + mMassSpectrumK0s->Add(mMassSpectrumK0sC.get()); + mMassSpectrumK0s->Add(mMassSpectrumK0sNC.get()); + mAvgClusSize->Add(mAvgClusSizeC.get()); + mAvgClusSize->Add(mAvgClusSizeNC.get()); + mMCStackCosPA->Add(mMCCosPAK0.get()); + mMCStackCosPA->Add(mMCCosPAnotK0.get()); + mStackDCA->Add(mDCAK0.get()); + mStackDCA->Add(mDCAnotK0.get()); + mStackR->Add(mRK0.get()); + mStackR->Add(mRnotK0.get()); + mStackPVDCA->Add(mPVDCAK0.get()); + mStackPVDCA->Add(mPVDCAnotK0.get()); +} + +void AvgClusSizeStudy::setStyle() +{ + gStyle->SetPalette(kRainbow); + + for (int i = 0; i < mAvgClusSizeCEtaVec.size(); i++) { + mAvgClusSizeCEtaVec[i]->SetMarkerStyle(20 + i); + mAvgClusSizeCEtaVec[i]->SetMarkerColor(i + 1); + mAvgClusSizeCEtaVec[i]->SetLineColor(i + 1); + } + + mAvgClusSizeC->SetLineColor(kRed); + mMassSpectrumFullC->SetLineColor(kRed); + mMassSpectrumK0sC->SetLineColor(kRed); + mMCCosPAK0->SetLineColor(kRed); + mDCAK0->SetLineColor(kRed); + mRK0->SetLineColor(kRed); + mPVDCAK0->SetLineColor(kRed); +} + +void AvgClusSizeStudy::run(ProcessingContext& pc) +{ + // auto geom = o2::its::GeometryTGeo::Instance(); + o2::globaltracking::RecoContainer recoData; + recoData.collectData(pc, *mDataRequest.get()); + updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions + process(recoData); +} + +void AvgClusSizeStudy::getClusterSizes(std::vector& clusSizeVec, const gsl::span ITSclus, gsl::span::iterator& pattIt, const o2::itsmft::TopologyDictionary* mdict) +{ + for (unsigned int iClus{0}; iClus < ITSclus.size(); ++iClus) { + auto& clus = ITSclus[iClus]; + auto pattID = clus.getPatternID(); + int npix; + o2::itsmft::ClusterPattern patt; + + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mdict->isGroup(pattID)) { + patt.acquirePattern(pattIt); + npix = patt.getNPixels(); + } else { + npix = mdict->getNpixels(pattID); + patt = mdict->getPattern(pattID); + } + clusSizeVec[iClus] = npix; + } +} + +void AvgClusSizeStudy::loadData(o2::globaltracking::RecoContainer& recoData) +{ + mInputITSidxs = recoData.getITSTracksClusterRefs(); + auto compClus = recoData.getITSClusters(); + auto clusPatt = recoData.getITSClustersPatterns(); + // auto pattIt = clusPatt.begin(); // NOTE: possibly these are not needed; looks like it just finds the 3D spacepoint of the cluster? + // std::vector inputITSclusters; + // inputITSclusters.reserve(compClus.size()); + // o2::its::ioutils::convertCompactClusters(compClus, pattIt, inputITSclusters, mDict); + mInputClusterSizes.resize(compClus.size()); + auto pattIt2 = clusPatt.begin(); + getClusterSizes(mInputClusterSizes, compClus, pattIt2, mDict); +} + +void AvgClusSizeStudy::process(o2::globaltracking::RecoContainer& recoData) +{ + // to get ITS-TPC tracks, we would call recoData.getTPCITSTracks() (line 534) of RecoContainer.h + // Alternatively, we should just use the track masks to do this in general... but how? + // I feel like recoData should already contain only the tracks we need, given that we applied the masks at DataRequest time... + // but clearly that is not the case? or maybe i'm an idiot + auto& params = o2::its::study::AvgClusSizeStudyParamConfig::Instance(); + float d0ACS, d1ACS, v0InvMass, d01DCA, cosPA, v0R, eta, d0pvDCA, d1pvDCA; + bool isMCK0s = false; + TrackITS d0recoTrk, d1recoTrk; // daughter ITS tracks + DCA d0DCA, d1DCA; // DCA object for prong to primary vertex (DCA = o2::dataformats::DCA) + float b = 5.; // Magnetic field in kG (?) + PVertex pv; + + // Variables for MC analysis + int totalK0sInDataset = 0; + gsl::span mcLabels; + MCLabel d0lab, d1lab; + const o2::MCTrack *V0mcTrk, *d0mcTrk, *d1mcTrk; + int V0PdgCode, d0PdgCode, d1PdgCode, m0TrkId, m1TrkId; + + loadData(recoData); + auto V0s = recoData.getV0s(); + auto trks = recoData.getITSTracks(); + + LOGP(info, "Found {} reconstructed V0s.", V0s.size()); + LOGP(info, "Found {} ITS tracks.", trks.size()); + LOGP(info, "Found {} ROFs.", recoData.getITSTracksROFRecords().size()); + if (mUseMC) { + mcLabels = recoData.getITSTracksMCLabels(); + LOGP(info, "Found {} labels.", mcLabels.size()); + } + + for (V0 v0 : V0s) { + d0recoTrk = recoData.getITSTrack(v0.getProngID(0)); + d1recoTrk = recoData.getITSTrack(v0.getProngID(1)); + + pv = recoData.getPrimaryVertex(v0.getVertexID()); // extract primary vertex + d0recoTrk.propagateToDCA(pv, b, &d0DCA); // calculate and store DCA objects for both prongs + d1recoTrk.propagateToDCA(pv, b, &d1DCA); + d0pvDCA = std::sqrt(d0DCA.getR2()); // calculate DCA distance + d1pvDCA = std::sqrt(d1DCA.getR2()); + + eta = v0.getEta(); + d01DCA = v0.getDCA(); + cosPA = v0.getCosPA(); + v0InvMass = std::sqrt(v0.calcMass2()) * 1000; // convert mass to MeV + v0R = std::sqrt(v0.calcR2()); // gives distance from pvertex to origin? in centimeters (?) NOTE: unsure if this is to the primary vertex or to origin + + if (mUseMC) { // check whether V0 is a K0s in MC, and fill the cut validation plots + isMCK0s = false; + d0lab = mcLabels[v0.getProngID(0)]; // extract MC label for the prongs + d1lab = mcLabels[v0.getProngID(1)]; + // Now we check validity, etc. for the labels (essentially strength of reco) to determine which reconstructed V0 are real K0s + if (d0lab.isValid() && d1lab.isValid()) { + d0mcTrk = mMCKinReader->getTrack(d0lab); + // LOGP(info, "Got d0 track"); + d1mcTrk = mMCKinReader->getTrack(d1lab); + // LOGP(info, "Got d1 track"); + if (d0mcTrk == nullptr || d1mcTrk == nullptr) { + // LOGP(info, "Nullptr found, skipping this V0"); + nNullptrs++; + } else { + // LOGP(info, "About to query Pdg codes"); + d0PdgCode = d0mcTrk->GetPdgCode(); + d1PdgCode = d1mcTrk->GetPdgCode(); + m0TrkId = d0mcTrk->getMotherTrackId(); + m1TrkId = d1mcTrk->getMotherTrackId(); + // LOGP(info, "pdgcodes are {} and {}", d0PdgCode, d1PdgCode); + + if (m0TrkId == m1TrkId && m0TrkId != -1 && m1TrkId != -1) { + if (d1lab.getEventID() == d0lab.getEventID()) { + V0mcTrk = mMCKinReader->getTrack(d1lab.getEventID(), m0TrkId); // assume daughter MCTracks are in same event as V0 MCTrack + V0PdgCode = V0mcTrk->GetPdgCode(); + if (V0PdgCode == 310) { + isMCK0s = true; + nK0s++; + if (abs(d0PdgCode) == 211 && d0PdgCode / d1PdgCode == -1) { + nIsPiPiIsK0s++; + } else { + nIsNotPiPiIsK0s++; + } + } else { + if (abs(d0PdgCode) == 211 && d0PdgCode / d1PdgCode == -1) { + nIsPiPiNotK0s++; + } + nNotK0s++; + } + if (abs(d0PdgCode) == 211 && d0PdgCode / d1PdgCode == -1) { + nPiPi++; + } + } else { + nEvIDMismatch++; + } + } else { + nMotherIDMismatch++; + } + } + } else { + nNotValid++; + } + if (isMCK0s) { + mMCCosPAK0->Fill(cosPA); + mDCAK0->Fill(d01DCA); + mRK0->Fill(v0R); + mPVDCAK0->Fill(d0pvDCA); + mPVDCAK0->Fill(d1pvDCA); + } else { + mMCCosPAnotK0->Fill(cosPA); + mDCAnotK0->Fill(d01DCA); + mRnotK0->Fill(v0R); + mPVDCAnotK0->Fill(d0pvDCA); + mPVDCAnotK0->Fill(d1pvDCA); + } + } + + d0ACS = getAverageClusterSize(&d0recoTrk); + d1ACS = getAverageClusterSize(&d1recoTrk); + mAvgClusSizeNC->Fill(d0ACS); + mAvgClusSizeNC->Fill(d1ACS); + + mOutputNtuple->Fill(d0ACS, d1ACS, v0InvMass, d01DCA, cosPA, v0R, eta, d0pvDCA, d1pvDCA, (float)isMCK0s); + + mCosPA->Fill(cosPA); + mMassSpectrumFullNC->Fill(v0InvMass); + mMassSpectrumK0sNC->Fill(v0InvMass); + mEtaNC->Fill(eta); + mR->Fill(v0R); + mDCA->Fill(d01DCA); + // innermost layer of ITS lies at 2.3cm + if (cosPA > params.cosPAmin && v0R < params.Rmax && v0R > params.Rmin && d01DCA < params.prongDCAmax && d0pvDCA > params.dauPVDCAmin && d1pvDCA > params.dauPVDCAmin) { + mMassSpectrumK0sC->Fill(v0InvMass); + mMassSpectrumFullC->Fill(v0InvMass); + mAvgClusSizeC->Fill(d0ACS); + mAvgClusSizeC->Fill(d1ACS); + if (eta > params.etaMin && eta < params.etaMax) { + nPionsInEtaRange++; + fillEtaBin(eta, d0ACS, 0); + fillEtaBin(eta, d1ACS, 0); + } + mEtaC->Fill(eta); + } + } + + if (mUseMC) { + o2::MCTrack V0MotherMCTrk; + LOGP(info, "OVERALL STATISTICS: {} nonvalid daughter pairs, {} nullptrs, {} motherID mismatches, {} evID mismatches, {} K0-shorts, {} not-K0s, {} pion pairs, out of {} V0s", nNotValid, nNullptrs, nMotherIDMismatch, nEvIDMismatch, nK0s, nNotK0s, nPiPi, V0s.size()); + LOGP(info, "OVERALL STATISTICS: {} Pi Y K0s N, {} Pi Y K0s Y, {} Pi N K0s Y", nIsPiPiNotK0s, nIsPiPiIsK0s, nIsNotPiPiIsK0s); + LOGP(info, "OVERALL STATISTICS: {} Pions in eta range", nPionsInEtaRange); + int nK0sisPrimary = 0; + int totalK0sMotherMinus1 = 0; + for (auto mcTrk : mMCTracks) { // search through all MC tracks to find K0s, whether reconstructed or not + if (mcTrk.GetPdgCode() == 310 && mcTrk.getMotherTrackId() == -1) { + totalK0sMotherMinus1++; + } + if (mcTrk.GetPdgCode() == 310 && mcTrk.isPrimary()) { + nK0sisPrimary++; + // V0MotherMCTrk = mMCTracks[mcTrk.getMotherTrackId()]; + // mMCMotherPDG->Fill(V0MotherMCTrk.GetPdgCode()); + // LOGP(info, "K0s is primary, motherID is {} and motherPDG is {}", mcTrk.getMotherTrackId(), V0MotherMCTrk.GetPdgCode()); + } + } + LOGP(info, "OVERALL STATISTICS: {} K0s (mother==-1) found in MC tracks out of {} total", totalK0sMotherMinus1, mMCTracks.size()); + LOGP(info, "OVERALL STATISTICS: {} K0s (isPrimary) found in MC tracks out of {} total", nK0sisPrimary, mMCTracks.size()); + // LOGP(info, "OVERALL STATISTICS: {} primary K0s found in MC tracks out of {} total", totalK0sInDataset, mMCTracks.size()); + } + + // TODO: implement 7 cluster track cut for daughters; if we don't have enough statistics, we can cut even harsher on cosPA and inject more statistics + // TODO: print the cut on the graph +} + +float AvgClusSizeStudy::getAverageClusterSize(TrackITS* daughter) +{ + int totalSize{0}; + auto firstClus = daughter->getFirstClusterEntry(); + auto ncl = daughter->getNumberOfClusters(); + for (int icl = 0; icl < ncl; icl++) { + totalSize += mInputClusterSizes[mInputITSidxs[firstClus + icl]]; + globalNPixels += mInputClusterSizes[mInputITSidxs[firstClus + icl]]; + } + globalNClusters += ncl; + return (float)totalSize / (float)ncl; +} + +void AvgClusSizeStudy::fillEtaBin(double eta, double clusSize, int i) +{ + if (eta < mEtaBinUL[i]) { // FIXME: there is a problem if eta is outside the full range (< etaMin or > etaMax)... + mAvgClusSizeCEtaVec[i]->Fill(clusSize); + } else { + fillEtaBin(eta, clusSize, i + 1); + } +} + +void AvgClusSizeStudy::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + o2::its::GeometryTGeo* geom = o2::its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::T2GRot, o2::math_utils::TransformType::T2G)); + } +} + +void AvgClusSizeStudy::saveHistograms() +{ + mDBGOut.reset(); + TFile fout(mOutName.c_str(), "RECREATE"); + + fout.WriteTObject(mOutputNtuple.get()); + + fout.WriteTObject(mMassSpectrumFullNC.get()); + fout.WriteTObject(mMassSpectrumFullC.get()); + fout.WriteTObject(mMassSpectrumK0sNC.get()); + fout.WriteTObject(mMassSpectrumK0sC.get()); + fout.WriteTObject(mAvgClusSize.get()); + fout.WriteTObject(mAvgClusSizeCEta.get()); // NOTE: storing the THStack does work, but it's a little more complicated to extract the individual histograms + fout.WriteTObject(mCosPA.get()); + fout.WriteTObject(mR.get()); + fout.WriteTObject(mDCA.get()); + fout.WriteTObject(mEtaNC.get()); + fout.WriteTObject(mEtaC.get()); + fout.WriteTObject(mMCMotherPDG.get()); + fout.WriteTObject(mMCCosPAK0.get()); + fout.WriteTObject(mMCCosPAnotK0.get()); + fout.WriteTObject(mRK0.get()); + fout.WriteTObject(mRnotK0.get()); + fout.WriteTObject(mDCAK0.get()); + fout.WriteTObject(mDCAnotK0.get()); + fout.WriteTObject(mPVDCAK0.get()); + fout.WriteTObject(mPVDCAnotK0.get()); + fout.Close(); + + // mMCStackCosPA->Add(mMCCosPAK0.get()); + // mMCStackCosPA->Add(mMCCosPAnotK0.get()); + // mStackDCA->Add(mDCAK0.get()); + // mStackDCA->Add(mDCAnotK0.get()); + // mStackR->Add(mRK0.get()); + // mStackR->Add(mRnotK0.get()); + + LOGP(important, "Stored histograms into {}", mOutName.c_str()); +} + +void AvgClusSizeStudy::plotHistograms() +{ + auto& params = o2::its::study::AvgClusSizeStudyParamConfig::Instance(); + double globalAvgClusSize = (double)globalNPixels / (double)globalNClusters; + + TCanvas* c1 = new TCanvas(); + mMassSpectrumFull->Draw("nostack"); + c1->Print("massSpectrumFull.png"); + + TCanvas* c2 = new TCanvas(); + mMassSpectrumK0s->Draw("nostack E"); + c2->BuildLegend(); + c2->Print("massSpectrumK0s.png"); + + TCanvas* c3 = new TCanvas(); + mAvgClusSize->Draw("nostack"); + TLine* globalAvg = new TLine(globalAvgClusSize, 0, globalAvgClusSize, mAvgClusSizeNC->GetMaximum()); + globalAvg->Draw(); + c3->Print("clusSize.png"); + + TCanvas* c4 = new TCanvas(); + mCosPA->Draw(); + c4->Print("cosPA.png"); + + TCanvas* c6 = new TCanvas(); + mR->Draw(); + c6->Print("mR.png"); + + TCanvas* c7 = new TCanvas(); + mDCA->Draw(); + c7->Print("mDCA.png"); + + TCanvas* c8 = new TCanvas(); + mEtaNC->Draw(); + c8->Print("mEtaNC.png"); + + TCanvas* c9 = new TCanvas(); + mEtaC->Draw(); + c9->Print("mEtaC.png"); + + TCanvas* c10 = new TCanvas(); + mAvgClusSizeCEta->Draw("P NOSTACK"); + c10->BuildLegend(0.6, 0.6, 0.8, 0.8); + c10->Print("clusSizeEta.png"); + for (int i = 0; i < params.etaNBins; i++) { + mAvgClusSizeCEtaVec[i]->Scale(1. / mAvgClusSizeCEtaVec[i]->Integral("width")); + } + + TCanvas* c11 = new TCanvas(); + mAvgClusSizeCEta->Draw("P L NOSTACK HIST"); + mAvgClusSizeCEta->SetTitle("Average cluster size per track (normed)"); + c11->BuildLegend(0.6, 0.6, 0.8, 0.8); + c11->Print("clusSizeEtaNormed.png"); + + if (mUseMC) { + TCanvas* c12 = new TCanvas(); + mMCMotherPDG->Draw(); + c12->Print("MCMotherPDG.png"); + + TCanvas* c13 = new TCanvas(); + mMCStackCosPA->Draw("nostack"); + c13->Print("MCCosPA.png"); + + TCanvas* c14 = new TCanvas(); + mStackDCA->Draw("nostack"); + c14->Print("MCDCA.png"); + + TCanvas* c15 = new TCanvas(); + mStackR->Draw("nostack"); + c15->Print("MCR.png"); + + TCanvas* c16 = new TCanvas(); + mStackPVDCA->Draw("nostack"); + c16->Print("MCPVDCA.png"); + } +} + +void AvgClusSizeStudy::fitMassSpectrum() +{ + TF1* gaus = new TF1("gaus", "gaus", 485, 505); + TFitResultPtr fit = mMassSpectrumK0sC->Fit("gaus", "S", "", 480, 510); + fit->Print(); +} + +void AvgClusSizeStudy::endOfStream(EndOfStreamContext& ec) +{ + auto& params = o2::its::study::AvgClusSizeStudyParamConfig::Instance(); + if (params.performFit) { + fitMassSpectrum(); + } + if (params.generatePlots) { + saveHistograms(); + setStyle(); + plotHistograms(); + } +} + +void AvgClusSizeStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + // o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); + return; + } +} + +DataProcessorSpec getAvgClusSizeStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracksMask, useMC); + dataRequest->requestClusters(srcClustersMask, useMC); + dataRequest->requestSecondaryVertices(useMC); + dataRequest->requestPrimaryVertertices(useMC); // NOTE: may be necessary to use requestPrimaryVerterticesTMP()... + // dataRequest->requestPrimaryVerterticesTMP(useMC); + + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + return DataProcessorSpec{ + "its-study-AvgClusSize", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, + Options{}}; +} +} // namespace study +} // namespace its +} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/K0sInvMass.h b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx similarity index 67% rename from Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/K0sInvMass.h rename to Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx index 4edb05a99d641..1627a9eda05fa 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/K0sInvMass.h +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx @@ -9,11 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_K0SINVMASS_STUDY_H -#define O2_K0SINVMASS_STUDY_H - -#include "Framework/DataProcessorSpec.h" -#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "ITSStudies/ITSStudiesConfigParam.h" namespace o2 { @@ -21,11 +17,11 @@ namespace its { namespace study { -using mask_t = o2::dataformats::GlobalTrackID::mask_t; -o2::framework::DataProcessorSpec getK0sInvMassStudy(mask_t srcTracksMask, bool useMC = false); +static auto& sAvgClusSizeParam = o2::its::study::AvgClusSizeStudyParamConfig::Instance(); // modeled on TrackingConfigParam.cxx + +O2ParamImpl(o2::its::study::AvgClusSizeStudyParamConfig); + } // namespace study } // namespace its } // namespace o2 - -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h index e5b45bbcd779e..a5d4d63125a9a 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesLinkDef.h @@ -15,4 +15,7 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::its::study::AvgClusSizeStudyParamConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::study::AvgClusSizeStudyParamConfig> + ; + #endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/K0sInvMass.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/K0sInvMass.cxx deleted file mode 100644 index 64692d3ef0f6f..0000000000000 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/K0sInvMass.cxx +++ /dev/null @@ -1,94 +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. - -#include "ITSStudies/K0sInvMass.h" - -#include "Framework/CCDBParamSpec.h" -#include "Framework/Task.h" -#include "DetectorsCommonDataFormats/DetID.h" -#include "DataFormatsGlobalTracking/RecoContainer.h" - -namespace o2 -{ -namespace its -{ -namespace study -{ -using namespace o2::framework; -using namespace o2::globaltracking; - -using GTrackID = o2::dataformats::GlobalTrackID; - -class K0InvMassStudy : public Task -{ - public: - K0InvMassStudy(std::shared_ptr dr, mask_t src) : mDataRequest(dr), mTracksSrc(src){}; - ~K0InvMassStudy() final = default; - void run(ProcessingContext&) final; - void endOfStream(EndOfStreamContext&) final; - void finaliseCCDB(ConcreteDataMatcher&, void*) final; - void process(o2::globaltracking::RecoContainer&); - - private: - void updateTimeDependentParams(ProcessingContext& pc); - GTrackID::mask_t mTracksSrc{}; - - // Data - std::shared_ptr mDataRequest; -}; - -void K0InvMassStudy::run(ProcessingContext& pc) -{ - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest.get()); - updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions - process(recoData); -} - -void K0InvMassStudy::process(o2::globaltracking::RecoContainer& recoData) -{ - auto v0 = recoData.getV0s(); - LOGP(info, " ====> got {} v0s", v0.size()); -} - -void K0InvMassStudy::updateTimeDependentParams(ProcessingContext& pc) -{ - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - } -} - -void K0InvMassStudy::endOfStream(EndOfStreamContext& ec) -{ -} - -void K0InvMassStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ -} - -DataProcessorSpec getK0sInvMassStudy(mask_t srcTracksMask, bool useMC) -{ - std::vector outputs; - auto dataRequest = std::make_shared(); - dataRequest->requestTracks(srcTracksMask, useMC); - dataRequest->requestSecondaryVertices(useMC); - - return DataProcessorSpec{ - "its-study-k0sinvmass", - dataRequest->inputs, - outputs, - AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask)}, - Options{}}; -} -} // namespace study -} // namespace its -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx index bb58690cacd4e..8ca32c2688a82 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -15,7 +15,7 @@ // Include studies hereafter #include "ITSStudies/ImpactParameter.h" -#include "ITSStudies/K0sInvMass.h" +#include "ITSStudies/AvgClusSize.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -35,10 +35,9 @@ void customize(std::vector& workflowOptions) {"track-sources", VariantType::String, std::string{"ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD"}, {"comma-separated list of track sources to use"}}, {"cluster-sources", VariantType::String, std::string{"ITS"}, {"comma-separated list of cluster sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, - {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, - {"niceparam-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; - o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); + o2::raw::HBFUtilsInitializer::addConfigOption(options, "none"); // "o2_tfidinfo.root" instead of "none" std::swap(workflowOptions, options); } @@ -48,7 +47,6 @@ void customize(std::vector& workflowOptions) WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; - GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD"); GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS"); @@ -56,19 +54,22 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); auto useMC = !configcontext.options().get("disable-mc"); GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); - srcTrc |= GID::getSourcesMask("ITS"); + srcTrc |= GID::getSourcesMask("ITS"); // guarantee that we will at least use ITS tracks 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::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); // Declare specs related to studies hereafter - specs.emplace_back(o2::its::study::getImpactParameterStudy(srcTrc, srcCls, useMC)); - specs.emplace_back(o2::its::study::getK0sInvMassStudy(srcTrc, useMC)); + // specs.emplace_back(o2::its::study::getImpactParameterStudy(srcTrc, srcCls, useMC)); + specs.emplace_back(o2::its::study::getAvgClusSizeStudy(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); + // write the configuration used for the studies workflow + o2::conf::ConfigurableParam::writeINI("o2_its_standalone_configuration.ini"); + return std::move(specs); } \ No newline at end of file