diff --git a/CCDB/include/CCDB/CCDBDownloader.h b/CCDB/include/CCDB/CCDBDownloader.h index ff1d97d68802d..031f656452fe8 100644 --- a/CCDB/include/CCDB/CCDBDownloader.h +++ b/CCDB/include/CCDB/CCDBDownloader.h @@ -29,8 +29,6 @@ typedef struct uv_signal_s uv_signal_t; typedef struct uv_async_s uv_async_t; typedef struct uv_handle_s uv_handle_t; -using namespace std; - namespace o2::ccdb { diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index 8b63fe455b5db..0ae9099040c05 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -562,6 +562,7 @@ size_t header_map_callback(char* buffer, size_t size, size_t nitems, void* userd if (index != std::string::npos) { const auto key = boost::algorithm::trim_copy(header.substr(0, index)); const auto value = boost::algorithm::trim_copy(header.substr(index + 1)); + LOGP(debug, "Adding #{} {} -> {}", headers->size(), key, value); headers->insert(std::make_pair(key, value)); } return size * nitems; @@ -1539,12 +1540,10 @@ void CcdbApi::loadFileToMemory(o2::pmr::vector& dest, std::string const& p curl_slist* options_list = nullptr; initCurlHTTPHeaderOptionsForRetrieve(curl_handle, options_list, timestamp, headers, etag, createdNotAfter, createdNotBefore); - navigateURLsAndLoadFileToMemory(dest, curl_handle, fullUrl, headers); - for (size_t hostIndex = 1; hostIndex < hostsPool.size() && isMemoryFileInvalid(dest); hostIndex++) { fullUrl = getFullUrlForRetrieval(curl_handle, path, metadata, timestamp, hostIndex); - loadFileToMemory(dest, fullUrl, headers); // headers loaded from the file in case of the snapshot reading only + navigateURLsAndLoadFileToMemory(dest, curl_handle, fullUrl, headers); // headers loaded from the file in case of the snapshot reading only } curl_slist_free_all(options_list); curl_easy_cleanup(curl_handle); @@ -1586,9 +1585,17 @@ void CcdbApi::navigateURLsAndLoadFileToMemory(o2::pmr::vector& dest, CURL* if (url.find("alien:/", 0) != std::string::npos) { return loadFileToMemory(dest, url, nullptr); // headers loaded from the file in case of the snapshot reading only } + if ((url.find("file:/", 0) != std::string::npos)) { + std::string path = url.substr(7); + if (std::filesystem::exists(path)) { + return loadFileToMemory(dest, path, nullptr); + } else { + return; + } + } // otherwise make an HTTP/CURL request struct HeaderObjectPair_t { - std::map header; + std::multimap header; o2::pmr::vector* object = nullptr; int counter = 0; } hoPair{{}, &dest, 0}; diff --git a/CMakeLists.txt b/CMakeLists.txt index a5ff713f8d05b..ca431a8b95be2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ # Preamble -cmake_minimum_required(VERSION 3.19 FATAL_ERROR) +cmake_minimum_required(VERSION 3.21.1 FATAL_ERROR) # it's important to specify accurately the list of languages. for instance C and # C++ as we _do_ have some C files to compile explicitely as C (e.g. gl3w.c) @@ -93,6 +93,8 @@ include(O2DataFile) include(O2TargetManPage) include(O2AddWorkflow) include(O2SetROOTPCMDependencies) +include(O2AddHipifiedExecutable) +include(O2AddHipifiedLibrary) # Main targets of the project in various subdirectories. Order matters. add_subdirectory(Common) diff --git a/CODEOWNERS b/CODEOWNERS index 908640415ed89..b6a04d3ad052c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -34,14 +34,14 @@ /DataFormats/Detectors/GlobalTracking @shahor02 /DataFormats/Detectors/GlobalTrackingWorkflow @shahor02 /DataFormats/Detectors/HMPID @gvolpe79 -/DataFormats/Detectors/ITSMFT @iouribelikov @robincaron13 @mconcas @shahor02 +/DataFormats/Detectors/ITSMFT @mcoquet642 @mconcas @shahor02 /DataFormats/Detectors/MUON @AliceO2Group/muon-experts /DataFormats/Detectors/PHOS @peressounko @kharlov /DataFormats/Detectors/Passive @sawenzel @benedikt-voelkel /DataFormats/Detectors/TOF @noferini /DataFormats/Detectors/TPC @davidrohr @wiechula @shahor02 /DataFormats/Detectors/TRD @f3sch @bazinski @martenole -/DataFormats/Detectors/Upgrades @qgp @marcovanleeuwen @mconcas +/DataFormats/Detectors/Upgrades @mconcas /DataFormats/Detectors/Upgrades/ITS3 @fgrosa @arossi81 /DataFormats/Detectors/ZDC @coppedis @@ -65,14 +65,14 @@ /Detectors/GlobalTracking @shahor02 /Detectors/GlobalTrackingWorkflow @shahor02 /Detectors/HMPID @gvolpe79 -/Detectors/ITSMFT @iouribelikov @robincaron13 @mconcas @shahor02 +/Detectors/ITSMFT @mcoquet642 @mconcas @shahor02 /Detectors/MUON @AliceO2Group/muon-experts /Detectors/PHOS @peressounko @kharlov /Detectors/Passive @sawenzel @benedikt-voelkel /Detectors/TOF @noferini /Detectors/TPC @davidrohr @wiechula @shahor02 /Detectors/TRD @f3sch @bazinski @martenole -/Detectors/Upgrades @qgp @mconcas +/Detectors/Upgrades @mconcas /Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas /Detectors/ZDC @coppedis /Detectors/CTF @shahor02 diff --git a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index 00b5230390cd9..c7f6631de5abe 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -125,6 +125,9 @@ class DCAFitterN ///< check if propagation of tracks to candidate vertex was done bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[mOrder[cand]]; } + ///< check if propagation of tracks to candidate vertex was done + bool isPropagationFailure(int cand = 0) const { return mPropFailed[mOrder[cand]]; } + ///< track param propagated to V0 candidate (no check for the candidate validity) /// propagateTracksToVertex must be called in advance Track& getTrack(int i, int cand = 0) @@ -150,7 +153,7 @@ class DCAFitterN o2::track::TrackPar createParentTrackPar(int cand = 0, bool sectorAlpha = true) const; ///< calculate on the fly track param (no cov mat) at candidate, check isValid to make sure propagation was successful - o2::track::TrackPar getTrackParamAtPCA(int i, int cand = 0) const; + o2::track::TrackPar getTrackParamAtPCA(int i, int cand = 0); ///< recalculate PCA as a cov-matrix weighted mean, even if absDCA method was used bool recalculatePCAWithErrors(int cand = 0); @@ -232,8 +235,8 @@ class DCAFitterN bool minimizeChi2NoErr(); bool roughDZCut() const; bool closerToAlternative() const; - bool propagateToX(o2::track::TrackParCov& t, float x) const; - bool propagateParamToX(o2::track::TrackPar& t, float x) const; + bool propagateToX(o2::track::TrackParCov& t, float x); + bool propagateParamToX(o2::track::TrackPar& t, float x); static double getAbsMax(const VecND& v); ///< track param positions at V0 candidate (no check for the candidate validity) @@ -308,7 +311,8 @@ class DCAFitterN std::array mPCA; // PCA for each vertex candidate std::array mChi2 = {0}; // Chi2 at PCA candidate std::array mNIters; // number of iterations for each seed - std::array mTrPropDone; // Flag that the tracks are fully propagated to PCA + std::array mTrPropDone{}; // Flag that the tracks are fully propagated to PCA + std::array mPropFailed{}; // Flag that some propagation failed for this PCA candidate MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T std::array mOrder{0}; int mCurHyp = 0; @@ -354,6 +358,9 @@ int DCAFitterN::process(const Tr&... args) if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1], mMaxDXYIni)) { // even for N>2 it should be enough to test just 1 loop return 0; // no crossing } + for (int ih = 0; ih < MAXHYP; ih++) { + mPropFailed[ih] = false; + } if (mUseAbsDCA) { calcRMatrices(); // needed for fast residuals derivatives calculation in case of abs. distance minimization } @@ -825,7 +832,7 @@ bool DCAFitterN::propagateTracksToVertex(int icand) //___________________________________________________________________ template -inline o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int icand) const +inline o2::track::TrackPar DCAFitterN::getTrackParamAtPCA(int i, int icand) { // propagate tracks param only to current vertex (if not already done) int ord = mOrder[icand]; @@ -1058,24 +1065,34 @@ o2::track::TrackPar DCAFitterN::createParentTrackPar(int cand, bool //___________________________________________________________________ template -inline bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, float x) const +inline bool DCAFitterN::propagateParamToX(o2::track::TrackPar& t, float x) { + bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { - return o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); + res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); } else { - return t.propagateParamTo(x, mBz); + res = t.propagateParamTo(x, mBz); } + if (!res) { + mPropFailed[mCurHyp] = true; + } + return res; } //___________________________________________________________________ template -inline bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, float x) const +inline bool DCAFitterN::propagateToX(o2::track::TrackParCov& t, float x) { + bool res = true; if (mUsePropagator || mMatCorr != o2::base::Propagator::MatCorrType::USEMatCorrNONE) { - return o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); + res = o2::base::Propagator::Instance()->PropagateToXBxByBz(t, x, mMaxSnp, mMaxStep, mMatCorr); } else { - return t.propagateTo(x, mBz); + res = t.propagateTo(x, mBz); + } + if (!res) { + mPropFailed[mCurHyp] = true; } + return res; } using DCAFitter2 = DCAFitterN<2, o2::track::TrackParCov>; diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index a35019648abb9..f80a64e62220d 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -93,11 +93,12 @@ void SimConfig::determineActiveModules(std::vector const& inputargs #ifdef ENABLE_UPGRADES if (mIsRun5) { for (int d = DetID::First; d <= DetID::Last; ++d) { - if (d == DetID::IT3 || d == DetID::TRK || d == DetID::FT3 || d == DetID::FCT) { + if (d == DetID::TRK || d == DetID::FT3 || d == DetID::FCT) { activeModules.emplace_back(DetID::getName(d)); } } activeModules.emplace_back("A3IP"); + activeModules.emplace_back("A3ABSO"); } else { #endif // add passive components manually (make a PassiveDetID for them!) diff --git a/Common/Utils/include/CommonUtils/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 2d1984f4d9ef3..08356dc462de8 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -15,6 +15,7 @@ #define COMMON_SIMCONFIG_INCLUDE_SIMCONFIG_CONFIGURABLEPARAM_H_ #include +#include #include #include #include diff --git a/Common/Utils/include/CommonUtils/DebugStreamer.h b/Common/Utils/include/CommonUtils/DebugStreamer.h index 0c1ac9af6eef7..6464a5dde128b 100644 --- a/Common/Utils/include/CommonUtils/DebugStreamer.h +++ b/Common/Utils/include/CommonUtils/DebugStreamer.h @@ -30,15 +30,18 @@ namespace o2::utils /// struct defining the flags which can be used to check if a certain debug streamer is used enum StreamFlags { - streamdEdx = 1 << 0, ///< stream corrections and cluster properties used for the dE/dx - streamDigitFolding = 1 << 1, ///< stream ion tail and saturatio information - streamDigits = 1 << 2, ///< stream digit information - streamFastTransform = 1 << 3, ///< stream tpc fast transform - streamITCorr = 1 << 4, ///< stream ion tail correction information - streamDistortionsSC = 1 << 5, ///< stream distortions applied in the TPC space-charge class (used for example in the tpc digitizer) - streamUpdateTrack = 1 << 6, ///< stream update track informations - streamRejectCluster = 1 << 7, ///< stream cluster rejection informations - streamFlagsCount = 8 ///< total number of streamers + streamdEdx = 1 << 0, ///< stream corrections and cluster properties used for the dE/dx + streamDigitFolding = 1 << 1, ///< stream ion tail and saturatio information + streamDigits = 1 << 2, ///< stream digit information + streamFastTransform = 1 << 3, ///< stream tpc fast transform + streamITCorr = 1 << 4, ///< stream ion tail correction information + streamDistortionsSC = 1 << 5, ///< stream distortions applied in the TPC space-charge class (used for example in the tpc digitizer) + streamUpdateTrack = 1 << 6, ///< stream update track informations + streamRejectCluster = 1 << 7, ///< stream cluster rejection informations + streamMergeBorderTracksBest = 1 << 8, ///< stream MergeBorderTracks best track + streamTimeSeries = 1 << 9, ///< stream tpc DCA debug tree + streamMergeBorderTracksAll = 1 << 10, ///< stream MergeBorderTracks all tracks + streamFlagsCount = 11 ///< total number of streamers }; enum SamplingTypes { @@ -46,6 +49,8 @@ enum SamplingTypes { sampleRandom = 1, ///< sample randomly every n points sampleID = 2, ///< sample every n IDs (per example track) sampleIDGlobal = 3, ///< in case different streamers have access to the same IDs use this gloabl ID + sampleWeights = 4, ///< perform sampling on weights, defined where the streamer is called + sampleTsalis = 5, ///< perform sampling on tsalis pdf }; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -121,6 +126,9 @@ class DebugStreamer /// \return returns sampling type and sampling frequency for given streamer static std::pair getSamplingTypeFrequency(const StreamFlags streamFlag); + /// \return returns sampling type and sampling frequency for given streamer + static float getSamplingFrequency(const StreamFlags streamFlag) { return getSamplingTypeFrequency(streamFlag).second; } + ///< return returns unique ID for each CPU thread to give each thread an own output file static size_t getCPUID(); @@ -144,7 +152,8 @@ class DebugStreamer /// check if streamer for specific flag is enabled /// \param samplingID optional index of the data which is streamed in to perform sampling on this index - static bool checkStream(const StreamFlags streamFlag, const size_t samplingID = -1); + /// \param weight weight which can be used to perform some weightes sampling + static bool checkStream(const StreamFlags streamFlag, const size_t samplingID = -1, const float weight = 1); /// merge trees with the same content structure, but different naming /// \param inpFile input file containing several trees with the same content @@ -155,10 +164,34 @@ class DebugStreamer /// \return returns integer index for given streamer flag static int getIndex(const StreamFlags streamFlag); + /// Random downsampling trigger function using Tsalis/Hagedorn spectra fit (sqrt(s) = 62.4 GeV to 13 TeV) as in https://iopscience.iop.org/article/10.1088/2399-6528/aab00f/pdf + /// \return flat q/pt trigger + /// \param pt pat of particle + /// \param factorPt defines the sampling + /// \param sqrts centre of mass energy + /// \param weight weight which is internally calculated + /// \param rnd random value between (0->1) used to check for sampling + /// \param mass particles mass (use pion if not known) + static bool downsampleTsalisCharged(float pt, float factorPt, float sqrts, float& weight, float rnd, float mass = 0.13957); + + /// get random value between min and max + static float getRandom(float min = 0, float max = 1); + private: using StreamersPerFlag = tbb::concurrent_unordered_map>; StreamersPerFlag mTreeStreamer; ///< streamer which is used for the debugging + /// Tsalis/Hagedorn function describing charged pt spectra (m s = 62.4 GeV to 13 TeV) as in https://iopscience.iop.org/article/10.1088/2399-6528/aab00f/pdf + /// https://github.com/alisw/AliPhysics/blob/523f2dc8b45d913e9b7fda9b27e746819cbe5b09/PWGPP/AliAnalysisTaskFilteredTree.h#L145 + /// \param pt - transverse momentum + /// \param mass - mass of particle + /// \param sqrts - centre of mass energy + /// \return - invariant yields of the charged particle *pt + /// n(sqrts)= a + b/sqrt(s) - formula 6 + /// T(sqrts)= c + d/sqrt(s) - formula 7 + /// a = 6.81 ± 0.06 and b = 59.24 ± 3.53 GeV - for charged particles page 3 + /// c = 0.082 ± 0.002 GeV and d = 0.151 ± 0.048 (GeV) - for charged particles page 4 + static float tsalisCharged(float pt, float mass, float sqrts); #else // empty implementation of the class for GPU or when the debug streamer is not build for CPU diff --git a/Common/Utils/include/CommonUtils/ShmManager.h b/Common/Utils/include/CommonUtils/ShmManager.h index ff288675db642..e7174b909b82c 100644 --- a/Common/Utils/include/CommonUtils/ShmManager.h +++ b/Common/Utils/include/CommonUtils/ShmManager.h @@ -23,10 +23,12 @@ #include #include +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) #include #include +#endif -#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) && !defined(__APPLE__) // this shared mem mode is meant for compiled stuff in o2-sim; not for ROOT sessions #define USESHM 1 #endif @@ -116,8 +118,10 @@ class ShmManager void* tryAttach(bool& success); size_t getPointerOffset(void* ptr) const { return (size_t)((char*)ptr - (char*)mBufferPtr); } +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) boost::interprocess::wmanaged_external_buffer* boostmanagedbuffer; boost::interprocess::allocator* boostallocator; +#endif }; } // namespace utils diff --git a/Common/Utils/src/DebugStreamer.cxx b/Common/Utils/src/DebugStreamer.cxx index 502c9797924c4..5debffbfb73ff 100644 --- a/Common/Utils/src/DebugStreamer.cxx +++ b/Common/Utils/src/DebugStreamer.cxx @@ -55,7 +55,7 @@ void o2::utils::DebugStreamer::flush() } } -bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const size_t samplingID) +bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const size_t samplingID, const float weight) { const bool isStreamerSet = ((getStreamFlags() & streamFlag) == streamFlag); if (!isStreamerSet) { @@ -65,27 +65,22 @@ bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const s // check sampling frequency const auto sampling = getSamplingTypeFrequency(streamFlag); if (sampling.first != SamplingTypes::sampleAll) { - // init random number generator for each thread - static thread_local std::mt19937 generator(std::random_device{}()); - std::uniform_real_distribution<> distr(0, 1); - auto sampleTrack = [&]() { if (samplingID == -1) { LOGP(fatal, "Sampling type sampleID not supported for stream flag {}", streamFlag); } - std::uniform_real_distribution<> distr(0, 1); // sample on samplingID (e.g. track level) static thread_local std::unordered_map> idMap; // in case of first call samplingID in idMap is 0 and always false and first ID rejected if (idMap[streamFlag].first != samplingID) { - idMap[streamFlag] = std::pair{samplingID, (distr(generator) < sampling.second)}; + idMap[streamFlag] = std::pair{samplingID, (getRandom() < sampling.second)}; } return idMap[streamFlag].second; }; if (sampling.first == SamplingTypes::sampleRandom) { // just sample randomly - return (distr(generator) < sampling.second); + return (getRandom() < sampling.second); } else if (sampling.first == SamplingTypes::sampleID) { return sampleTrack(); } else if (sampling.first == SamplingTypes::sampleIDGlobal) { @@ -104,11 +99,46 @@ bool o2::utils::DebugStreamer::checkStream(const StreamFlags streamFlag, const s refIDs[index][samplingID] = storeTrk; return storeTrk; } + } else if (sampling.first == SamplingTypes::sampleWeights) { + // sample with weight + return (weight * getRandom() < sampling.second); } } return true; } +float o2::utils::DebugStreamer::tsalisCharged(float pt, float mass, float sqrts) +{ + const float a = 6.81; + const float b = 59.24; + const float c = 0.082; + const float d = 0.151; + const float mt = std::sqrt(mass * mass + pt * pt); + const float n = a + b / sqrts; + const float T = c + d / sqrts; + const float p0 = n * T; + const float result = std::pow((1. + mt / p0), -n) * pt; + return result; +} + +bool o2::utils::DebugStreamer::downsampleTsalisCharged(float pt, float factorPt, float sqrts, float& weight, float rnd, float mass) +{ + const float prob = tsalisCharged(pt, mass, sqrts); + const float probNorm = tsalisCharged(1., mass, sqrts); + weight = prob / probNorm; + const bool isSampled = (rnd * (weight * pt * pt)) < factorPt; + return isSampled; +} + +float o2::utils::DebugStreamer::getRandom(float min, float max) +{ + // init random number generator for each thread + static thread_local std::mt19937 generator(std::random_device{}()); + std::uniform_real_distribution<> distr(min, max); + const float rnd = distr(generator); + return rnd; +} + int o2::utils::DebugStreamer::getIndex(const StreamFlags streamFlag) { // see: https://stackoverflow.com/a/71539401 @@ -145,13 +175,15 @@ int o2::utils::DebugStreamer::getNTrees(const size_t id) const { return isStream void o2::utils::DebugStreamer::mergeTrees(const char* inpFile, const char* outFile, const char* option) { TFile fInp(inpFile, "READ"); - std::unordered_map lists; + std::unordered_map lists; for (TObject* keyAsObj : *fInp.GetListOfKeys()) { const auto key = dynamic_cast(keyAsObj); TTree* tree = (TTree*)fInp.Get(key->GetName()); // perform simple check on the number of entries to merge only TTree with same content (ToDo: Do check on name of branches) const int entries = tree->GetListOfBranches()->GetEntries(); - lists[entries].Add(tree); + const std::string brName = key->GetName(); + const std::string nameBr = brName.substr(0, brName.find_last_of("_")); + lists[nameBr].Add(tree); } TFile fOut(outFile, "RECREATE"); diff --git a/Common/Utils/src/FileFetcher.cxx b/Common/Utils/src/FileFetcher.cxx index 6492383d836f7..fd7d9d2be3c32 100644 --- a/Common/Utils/src/FileFetcher.cxx +++ b/Common/Utils/src/FileFetcher.cxx @@ -88,6 +88,15 @@ void FileFetcher::processInput(const std::vector& input) if (fs::is_directory(inp)) { processDirectory(inp); } else if (mSelRegex && !std::regex_match(inp, *mSelRegex.get())) { // provided selector does not match, treat as a txt file with list + // Avoid reading a multigiB data file as a list of inputs + // bringing down the system. + std::filesystem::path p(inp); + + if (std::filesystem::file_size(p) > 10000000) { + LOGP(error, "file list {} larger than 10MB. Is this a data file?", inp); + continue; + } + std::ifstream listFile(inp); if (!listFile.good()) { LOGP(error, "file {} pretends to be a list of inputs but does not exist", inp); diff --git a/Common/Utils/src/ShmManager.cxx b/Common/Utils/src/ShmManager.cxx index cb3af5dedf380..26b30be062220 100644 --- a/Common/Utils/src/ShmManager.cxx +++ b/Common/Utils/src/ShmManager.cxx @@ -119,8 +119,8 @@ bool ShmManager::createGlobalSegment(int nsegments) return false; } - LOG(info) << "CREATING SIM SHARED MEM SEGMENT FOR " << nsegments << " WORKERS"; #ifdef USESHM + LOG(info) << "CREATING SIM SHARED MEM SEGMENT FOR " << nsegments << " WORKERS"; // LOG(info) << "SIZEOF ShmMetaInfo " << sizeof(ShmMetaInfo); const auto totalsize = sizeof(ShmMetaInfo) + SHMPOOLSIZE * nsegments; if ((mShmID = shmget(IPC_PRIVATE, totalsize, IPC_CREAT | 0666)) == -1) { diff --git a/Common/Utils/src/TreeStreamRedirector.cxx b/Common/Utils/src/TreeStreamRedirector.cxx index 86e0eb5a37552..4c21fcd602543 100644 --- a/Common/Utils/src/TreeStreamRedirector.cxx +++ b/Common/Utils/src/TreeStreamRedirector.cxx @@ -111,7 +111,9 @@ TreeStream& TreeStreamRedirector::operator<<(const char* name) void TreeStreamRedirector::Close() { // flush and close - + if (!mDirectory) { + return; + } TDirectory* backup = gDirectory; mDirectory->cd(); for (auto& layout : mDataLayouts) { diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h index 8481db8f6d7e5..3635b8ede44db 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/CTF.h @@ -29,13 +29,16 @@ namespace ctp struct CTFHeader : public o2::ctf::CTFDictHeader { uint64_t lumiCounts = 0; /// FT0 Luminosity counts moving average over lumiNHBFs orbits uint64_t lumiCountsFV0 = 0; /// FV0 Luminosity counts moving average over lumiNHBFs orbits - uint32_t lumiNHBFs = 0; /// Number of HBFs over which lumi is integrated - uint32_t lumiOrbit = 0; /// 1st orbit of TF where lumi was updated, can be compared with firstOrbit - uint32_t nTriggers = 0; /// number of triggers - uint32_t firstOrbit = 0; /// orbit of 1st trigger - uint16_t firstBC = 0; /// bc of 1st trigger - - ClassDefNV(CTFHeader, 3); + uint32_t lumiNHBFs = 0; /// Number of HBFs over which lumi is integrated + uint32_t lumiNHBFsFV0 = 0; /// Number of FV0 HBFs over which lumi is integrated + uint32_t lumiOrbit = 0; /// 1st orbit of TF where lumi was updated, can be compared with firstOrbit + uint32_t nTriggers = 0; /// number of triggers + uint32_t firstOrbit = 0; /// orbit of 1st trigger + uint16_t firstBC = 0; /// bc of 1st trigger + uint16_t inp1 = 0; /// lumiCounts input ID + uint16_t inp2 = 0; /// lumiCountsFV0 input ID + + ClassDefNV(CTFHeader, 5); }; /// wrapper for the Entropy-encoded trigger inputs and classes of the TF diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h index 918f602bbd2d1..e9eb8ba497c17 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/LumiInfo.h @@ -32,8 +32,10 @@ struct LumiInfo { int inp2 = 6; // VBA float getLumi() const { return nHBFCounted > 0 ? float(counts / (nHBFCounted * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } float getLumiFV0() const { return nHBFCountedFV0 > 0 ? float(countsFV0 / (nHBFCountedFV0 * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } + float getLumiAlt() const { return getLumiFV0(); } float getLumiError() const { return nHBFCounted > 0 ? float(std::sqrt(counts) / (nHBFCounted * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } float getLumiFV0Error() const { return nHBFCountedFV0 > 0 ? float(std::sqrt(countsFV0) / (nHBFCountedFV0 * o2::constants::lhc::LHCOrbitMUS * 1e-6)) : 0.f; } + float getLumiAltError() const { return getLumiFV0Error(); } void printInputs() const; ClassDefNV(LumiInfo, 3); }; diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h index f71799edceac7..526e79f00f65d 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Scalers.h @@ -67,19 +67,21 @@ struct CTPScalerRecordRaw { o2::InteractionRecord intRecord; double_t epochTime; std::vector scalers; - std::vector scalersDets; + // std::vector scalersDets; + std::vector scalersInps; void printStream(std::ostream& stream) const; - ClassDefNV(CTPScalerRecordRaw, 3); + ClassDefNV(CTPScalerRecordRaw, 4); }; struct CTPScalerRecordO2 { CTPScalerRecordO2() = default; o2::InteractionRecord intRecord; double_t epochTime; std::vector scalers; - std::vector scalersDets; + // std::vector scalersDets; + std::vector scalersInps; void printStream(std::ostream& stream) const; void printFromZero(std::ostream& stream, CTPScalerRecordO2& record0) const; - ClassDefNV(CTPScalerRecordO2, 3); + ClassDefNV(CTPScalerRecordO2, 4); }; class CTPRunScalers { @@ -103,6 +105,8 @@ class CTPRunScalers uint32_t getRunNUmber() { return mRunNumber; }; int printRates(); int printIntegrals(); + int printInputRateAndIntegral(int inp); + int printClassBRateAndIntegral(int icls); // // static constexpr uint32_t NCOUNTERS = 1052; // v1 @@ -144,10 +148,10 @@ class CTPRunScalers std::vector mScalerRecordRaw; std::vector mScalerRecordO2; int processScalerLine(const std::string& line, int& level, int& nclasses); - int copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows); + int copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows, std::array& overflows); int updateOverflows(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, overflows_t& classesoverflows) const; int updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw& scal1, std::array& overflow) const; - + int updateOverflowsInps(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, std::array& overflow) const; ClassDefNV(CTPRunScalers, 2); }; } // namespace ctp diff --git a/DataFormats/Detectors/CTP/src/RunManager.cxx b/DataFormats/Detectors/CTP/src/RunManager.cxx index 8ed0b51bef3df..4f83c110c01ec 100644 --- a/DataFormats/Detectors/CTP/src/RunManager.cxx +++ b/DataFormats/Detectors/CTP/src/RunManager.cxx @@ -122,6 +122,7 @@ int CTPRunManager::addScalers(uint32_t irun, std::time_t time) } // detectors // std::vector detlist = mActiveRuns[irun]->cfg.getDetectorList(); + /* o2::detectors::DetID::mask_t detmask = mActiveRuns[irun]->cfg.getDetectorMask(); for (uint32_t i = 0; i < 32; i++) { o2::detectors::DetID::mask_t deti = 1ul << i; @@ -134,6 +135,14 @@ int CTPRunManager::addScalers(uint32_t irun, std::time_t time) // LOG(info) << "Scaler for detector:" << countername << ":" << detcount; } } + */ + int NINPS = 48; + int offset = 599; + for (uint32_t i = 0; i < NINPS; i++) { + uint32_t inpcount = mCounters[offset + i]; + scalrec.scalersInps.push_back(inpcount); + // LOG(info) << "Scaler for input:" << CTPRunScalers::scalerNames[offset+i] << ":" << inpcount; + } // if (mNew == 0) { scalrec.intRecord.orbit = mCounters[mScalerName2Position[orb]]; diff --git a/DataFormats/Detectors/CTP/src/Scalers.cxx b/DataFormats/Detectors/CTP/src/Scalers.cxx index 6f443fd5a77c4..ca313bccf6fdb 100644 --- a/DataFormats/Detectors/CTP/src/Scalers.cxx +++ b/DataFormats/Detectors/CTP/src/Scalers.cxx @@ -65,7 +65,8 @@ void CTPScalerRecordRaw::printStream(std::ostream& stream) const for (auto const& cnts : scalers) { cnts.printStream(stream); } - for (auto const& dets : scalersDets) { + std::cout << "Inputs:" << scalersInps.size() << std::endl; + for (auto const& dets : scalersInps) { stream << dets << " "; } stream << std::endl; @@ -77,7 +78,8 @@ void CTPScalerRecordO2::printStream(std::ostream& stream) const for (auto const& cnts : scalers) { cnts.printStream(stream); } - for (auto const& dets : scalersDets) { + std::cout << "Inputs:" << scalersInps.size() << std::endl; + for (auto const& dets : scalersInps) { stream << dets << " "; } stream << std::endl; @@ -273,10 +275,12 @@ int CTPRunScalers::convertRawToO2() overflows[i] = {0, 0, 0, 0, 0, 0}; } } + // Input overflows + std::array overflowsInputs = {48 * 0}; errorCounters eCnts; // 1st o2 rec is just copy CTPScalerRecordO2 o2rec; - copyRawToO2ScalerRecord(mScalerRecordRaw[0], o2rec, overflows); + copyRawToO2ScalerRecord(mScalerRecordRaw[0], o2rec, overflows, overflowsInputs); mScalerRecordO2.push_back(o2rec); int j = 1; for (uint32_t i = 1; i < mScalerRecordRaw.size(); i++) { @@ -285,7 +289,8 @@ int CTPRunScalers::convertRawToO2() // if (ret == 0) { CTPScalerRecordO2 o2rec; - copyRawToO2ScalerRecord(mScalerRecordRaw[i], o2rec, overflows); + ret = updateOverflowsInps(mScalerRecordRaw[i - 1], mScalerRecordRaw[i], overflowsInputs); + copyRawToO2ScalerRecord(mScalerRecordRaw[i], o2rec, overflows, overflowsInputs); mScalerRecordO2.push_back(o2rec); // Check consistency checkConsistency(mScalerRecordO2[j - 1], mScalerRecordO2[j], eCnts); @@ -295,7 +300,7 @@ int CTPRunScalers::convertRawToO2() eCnts.printStream(std::cout); return 0; } -int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows) +int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTPScalerRecordO2& o2rec, overflows_t& classesoverflows, std::array& overflows) { if (rawrec.scalers.size() != (mClassMask.count())) { LOG(error) << "Inconsistent scaler record size:" << rawrec.scalers.size() << " Expected:" << mClassMask.count(); @@ -313,6 +318,10 @@ int CTPRunScalers::copyRawToO2ScalerRecord(const CTPScalerRecordRaw& rawrec, CTP o2scal.createCTPScalerO2FromRaw(rawscal, classesoverflows[k]); o2rec.scalers.push_back(o2scal); } + for (int i = 0; i < rawrec.scalersInps.size(); i++) { + uint64_t inpo2 = (uint64_t)(rawrec.scalersInps[i]) + 0xffffffffull * (uint64_t)(overflows[i]); + o2rec.scalersInps.push_back(inpo2); + } return 0; } int CTPRunScalers::checkConsistency(const CTPScalerO2& scal0, const CTPScalerO2& scal1, errorCounters& eCnts) const @@ -452,6 +461,26 @@ int CTPRunScalers::updateOverflows(const CTPScalerRaw& scal0, const CTPScalerRaw //std::cout << std::endl; return 0; } +// +int CTPRunScalers::updateOverflowsInps(const CTPScalerRecordRaw& rec0, const CTPScalerRecordRaw& rec1, std::array& overflow) const +{ + int NINPS = 48; + if (rec0.scalersInps.size() < NINPS) { + LOG(error) << "updateOverflowsInps.size < 48:" << rec0.scalersInps.size(); + return 1; + } + if (rec1.scalersInps.size() < NINPS) { + LOG(error) << "updateOverflowsInps.size < 48:" << rec1.scalersInps.size(); + return 2; + } + for (int i = 0; i < NINPS; i++) { + if (rec0.scalersInps[i] > rec1.scalersInps[i]) { + overflow[i] += 1; + } + } + return 0; +} +// int CTPRunScalers::printRates() { if (mScalerRecordO2.size() == 0) { @@ -507,7 +536,42 @@ int CTPRunScalers::printIntegrals() } return 0; } - +// +// Input counting 1..48 +int CTPRunScalers::printInputRateAndIntegral(int inp) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalersInps[inp - 1] - mScalerRecordO2[0].scalersInps[inp - 1]; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Input " << inp << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + return 0; +} +// Prints class before counters for lumi +// Class counting 1..64 +int CTPRunScalers::printClassBRateAndIntegral(int icls) +{ + if (mScalerRecordO2.size() == 0) { + LOG(info) << "ScalerRecord is empty, doing nothing"; + return 1; + } + double_t time0 = mScalerRecordO2[0].epochTime; + double_t timeL = mScalerRecordO2[mScalerRecordO2.size() - 1].epochTime; + if (mScalerRecordO2[0].scalers.size() < icls) { + LOG(error) << "class number bigger than expected for this run:" << icls << "expexted smaller than:" << mScalerRecordO2[0].scalers.size(); + return 1; + } else { + int integral = mScalerRecordO2[mScalerRecordO2.size() - 1].scalers[icls - 1].lmBefore - mScalerRecordO2[0].scalers[icls - 1].lmBefore; + std::cout << "Scaler Integrals for run:" << mRunNumber << " duration:" << timeL - time0; + std::cout << " Class " << icls << " integral:" << integral << " rate:" << integral / (timeL - time0) << std::endl; + } + return 0; +} +// void CTPRunScalers::printLMBRateVsT() const { for (int i = 1; i < mScalerRecordO2.size(); i++) { // loop over time diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h index 4e3dd1f9f4004..a0853b8f14c73 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/EncodedBlocks.h @@ -29,17 +29,18 @@ #include "DetectorsCommonDataFormats/CTFDictHeader.h" #include "DetectorsCommonDataFormats/CTFIOSize.h" #include "DetectorsCommonDataFormats/ANSHeader.h" -#include "DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h" -#include "DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h" #include "DetectorsCommonDataFormats/internal/Packer.h" #include "DetectorsCommonDataFormats/Metadata.h" +#ifndef __CLING__ +#include "DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h" +#include "DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h" #include "rANS/compat.h" #include "rANS/histogram.h" #include "rANS/serialize.h" #include "rANS/factory.h" #include "rANS/metrics.h" -#include "rANS/serialize.h" #include "rANS/utils.h" +#endif namespace o2 { @@ -333,10 +334,15 @@ class EncodedBlocks public: typedef EncodedBlocks base; +#ifndef __CLING__ template using dictionaryType = std::variant, rans::RenormedDenseHistogram>; +#endif - void setHeader(const H& h) { mHeader = h; } + void setHeader(const H& h) + { + mHeader = h; + } const H& getHeader() const { return mHeader; } H& getHeader() { return mHeader; } std::shared_ptr cloneHeader() const { return std::shared_ptr(new H(mHeader)); } // for dictionary creation @@ -357,6 +363,7 @@ class EncodedBlocks return mBlocks[i]; } +#ifndef __CLING__ template dictionaryType getDictionary(int i, ANSHeader ansVersion = ANSVersionUnspecified) const { @@ -367,6 +374,26 @@ class EncodedBlocks assert(static_cast(std::numeric_limits::min()) <= static_cast(metadata.max)); assert(static_cast(std::numeric_limits::max()) >= static_cast(metadata.min)); + // check consistency of metadata and type + [&]() { + const int64_t sourceMin = std::numeric_limits::min(); + const int64_t sourceMax = std::numeric_limits::max(); + + auto view = rans::trim(rans::HistogramView{block.getDict(), block.getDict() + block.getNDict(), metadata.min}); + const int64_t dictMin = view.getMin(); + const int64_t dictMax = view.getMax(); + assert(dictMin >= metadata.min); + assert(dictMax <= metadata.max); + + if ((dictMin < sourceMin) || (dictMax > sourceMax)) { + if (ansVersion == ANSVersionCompat && mHeader.majorVersion == 1 && mHeader.minorVersion == 0 && mHeader.dictTimeStamp < 1653192000000) { + LOGP(warn, "value range of dictionary and target datatype are incompatible: target type [{},{}] vs dictionary [{},{}], tolerate in compat mode for old dictionaries", sourceMin, sourceMax, dictMin, dictMax); + } else { + throw std::runtime_error(fmt::format("value range of dictionary and target datatype are incompatible: target type [{},{}] vs dictionary [{},{}]", sourceMin, sourceMax, dictMin, dictMax)); + } + } + }(); + if (ansVersion == ANSVersionCompat) { rans::DenseHistogram histogram{block.getDict(), block.getDict() + block.getNDict(), metadata.min}; return rans::compat::renorm(std::move(histogram), metadata.probabilityBits); @@ -393,6 +420,7 @@ class EncodedBlocks throw std::runtime_error(fmt::format("Failed to load serialized Dictionary. Unsupported ANS Version: {}", static_cast(ansVersion))); } }; +#endif void setANSHeader(const ANSHeader& h) { @@ -480,8 +508,10 @@ class EncodedBlocks template , bool> = true> o2::ctf::CTFIOSize decode(D_IT dest, int slot, const std::any& decoderExt = {}) const; +#ifndef __CLING__ /// create a special EncodedBlocks containing only dictionaries made from provided vector of frequency tables static std::vector createDictionaryBlocks(const std::vector>& vfreq, const std::vector& prbits); +#endif /// print itself void print(const std::string& prefix = "", int verbosity = 1) const; @@ -567,6 +597,7 @@ class EncodedBlocks template o2::ctf::CTFIOSize encodeRANSV1Inplace(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr, double_t sizeEstimateSafetyFactor = 1); +#ifndef __CLING__ template o2::ctf::CTFIOSize pack(const input_IT srcBegin, const input_IT srcEnd, int slot, rans::Metrics::value_type> metrics, buffer_T* buffer = nullptr); @@ -587,6 +618,7 @@ class EncodedBlocks return pack(srcBegin, srcEnd, slot, metrics, buffer); } +#endif template o2::ctf::CTFIOSize store(const input_IT srcBegin, const input_IT srcEnd, int slot, Metadata::OptStore opt, buffer_T* buffer = nullptr); @@ -810,7 +842,7 @@ auto EncodedBlocks::getImage(const void* newHead) // we don't modify newHead, but still need to remove constness for relocation interface relocate(image.mRegistry.head, const_cast(reinterpret_cast(newHead)), reinterpret_cast(&image)); - return std::move(image); + return image; } ///_____________________________________________________________________________ @@ -913,13 +945,13 @@ CTFIOSize EncodedBlocks::decode(D_IT dest, // it } }; +#ifndef __CLING__ template template CTFIOSize EncodedBlocks::decodeCompatImpl(dst_IT dstBegin, int slot, const std::any& decoderExt) const { // get references to the right data - const auto& ansVersion = getANSHeader(); const auto& block = mBlocks[slot]; const auto& md = mMetadata[slot]; @@ -958,7 +990,6 @@ CTFIOSize EncodedBlocks::decodeRansV1Impl(dst_IT dstBegin, int slot, co { // get references to the right data - const auto& ansVersion = getANSHeader(); const auto& block = mBlocks[slot]; const auto& md = mMetadata[slot]; @@ -1479,6 +1510,7 @@ std::vector EncodedBlocks::createDictionaryBlocks(const std::vect } return vdict; } +#endif template void EncodedBlocks::dump(const std::string& prefix, int ncol) const diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h index b2cbed1cd27f9..5335514278e26 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/ExternalEntropyCoder.h @@ -35,7 +35,6 @@ class ExternalEntropyCoder public: using source_type = source_T; using encoder_type = typename rans::denseEncoder_type; - using metrics_type = rans::Metrics; ExternalEntropyCoder(const encoder_type& encoder); @@ -80,7 +79,7 @@ template constexpr size_t Overhead = 10 * rans::utils::pow2(10); // 10KB overhead safety margin const double_t RelativeSafetyFactor = 2.0 * safetyFactor; const size_t messageSizeB = nElements * sizeof(source_type); - return rans::utils::nBytesTo(std::ceil(safetyFactor * messageSizeB) + Overhead); + return rans::utils::nBytesTo(std::ceil(RelativeSafetyFactor * messageSizeB) + Overhead); } template diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h index 54302d51d2666..8601469b34142 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/InplaceEntropyCoder.h @@ -166,7 +166,6 @@ void InplaceEntropyCoder::makeEncoder() } const size_t rangeBits = rans::utils::getRangeBits(*mMetrics.getCoderProperties().min, *mMetrics.getCoderProperties().max); - const size_t nSamples = mMetrics.getDatasetProperties().numSamples; const size_t nUsedAlphabetSymbols = mMetrics.getDatasetProperties().nUsedAlphabetSymbols; if (rangeBits <= 18) { diff --git a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h index 72f62e3245797..fa11e9f6dac6d 100644 --- a/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h +++ b/DataFormats/Detectors/Common/include/DetectorsCommonDataFormats/internal/Packer.h @@ -16,8 +16,10 @@ #ifndef ALICEO2_PACKER_H_ #define ALICEO2_PACKER_H_ +#ifndef __CLING__ #include "rANS/pack.h" #include "rANS/metrics.h" +#endif namespace o2::ctf::internal { @@ -29,9 +31,10 @@ class Packer using source_type = source_T; Packer() = default; - +#ifndef __CLING__ explicit Packer(rans::Metrics& metrics) : mOffset{metrics.getDatasetProperties().min}, mPackingWidth{metrics.getDatasetProperties().alphabetRangeBits} {}; +#endif template Packer(source_IT srcBegin, source_IT srcEnd); @@ -58,6 +61,7 @@ template template Packer::Packer(source_IT srcBegin, source_IT srcEnd) { +#ifndef __CLING__ static_assert(rans::utils::isCompatibleIter_v); if (srcBegin != srcEnd) { @@ -73,13 +77,18 @@ Packer::Packer(source_IT srcBegin, source_IT srcEnd) mOffset = min; mPackingWidth = rans::utils::getRangeBits(min, max); } +#endif }; template template [[nodiscard]] inline size_t Packer::getPackingBufferSize(size_t messageLength) const noexcept { +#ifndef __CLING__ return rans::computePackingBufferSize(messageLength, mPackingWidth); +#else + return 0; +#endif }; template @@ -99,12 +108,15 @@ template if (extent == 0) { return dstBegin; } - +#ifndef __CLING__ rans::BitPtr packEnd = rans::pack(srcBegin, extent, dstBegin, mPackingWidth, mOffset); auto* end = packEnd.toPtr(); ++end; // one past end iterator; rans::utils::checkBounds(end, dstEnd); return end; +#else + return nullptr; +#endif }; } // namespace o2::ctf::internal diff --git a/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx b/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx index 3471a6c9e4dff..6b688fcebc7f7 100644 --- a/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx +++ b/DataFormats/Detectors/Common/test/testCTFEntropyCoder.cxx @@ -126,7 +126,7 @@ void encodeInplace(source_IT begin, source_IT end) std::vector dictBuffer(sizeEstimate.getCompressedDictionarySize(), 0); auto encoderEnd = entropyCoder.encode(begin, end, encodeBuffer.data(), encodeBuffer.data() + encodeBuffer.size()); - auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); + [[maybe_unused]] auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); auto dictEnd = entropyCoder.writeDictionary(dictBuffer.data(), dictBuffer.data() + dictBuffer.size()); // decode const auto& coderProperties = metrics.getCoderProperties(); @@ -303,7 +303,7 @@ void encodeExternal(source_IT begin, source_IT end) auto encoderEnd = entropyCoder.encode(begin, end, encodeBuffer.data(), encodeBuffer.data() + encodeBuffer.size()); std::vector literalSymbolsBuffer(entropyCoder.template computePackedIncompressibleSize(), 0); - auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); + [[maybe_unused]] auto literalsEnd = entropyCoder.writeIncompressible(literalSymbolsBuffer.data(), literalSymbolsBuffer.data() + literalSymbolsBuffer.size()); // decode auto decoder = ExternalEncoders.getDecoder(); diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h index 202615f318a7b..cf29f1d79381c 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Cell.h @@ -60,7 +60,8 @@ class Cell public: enum class EncoderVersion { EncodingV0, - EncodingV1 + EncodingV1, + EncodingV2 }; /// \brief Default constructor Cell() = default; @@ -202,7 +203,7 @@ class Cell /// the limits is provided the energy is /// set to the limits (0 in case of negative /// energy, 250. in case of energies > 250 GeV) - uint16_t getEnergyEncoded(EncoderVersion version = EncoderVersion::EncodingV1) const; + uint16_t getEnergyEncoded(EncoderVersion version = EncoderVersion::EncodingV2) const; /// \brief Get encoded bit representation of cell type (for CTF) /// \return Encoded bit representation @@ -218,10 +219,14 @@ class Cell static uint16_t encodeTime(float timestamp); static uint16_t encodeEnergyV0(float energy); static uint16_t encodeEnergyV1(float energy, ChannelType_t celltype); + static uint16_t encodeEnergyV2(float energy, ChannelType_t celltype); static uint16_t V0toV1(uint16_t energybits, ChannelType_t celltype); + static uint16_t V0toV2(uint16_t energybits, ChannelType_t celltype); + static uint16_t V1toV2(uint16_t energybits, ChannelType_t celltype); static float decodeTime(uint16_t timestampBits); static float decodeEnergyV0(uint16_t energybits); static float decodeEnergyV1(uint16_t energybits, ChannelType_t celltype); + static float decodeEnergyV2(uint16_t energybits, ChannelType_t celltype); private: /// \brief Set cell energy from encoded bit representation (from CTF) diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h index 103a9419b5e08..72eb2a9e272b9 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/Constants.h @@ -103,6 +103,11 @@ constexpr int MAX_RANGE_ADC = 0x3FF; ///< Dynamic range of the ADCs (1 constexpr double EMCAL_TRU_ADCENERGY = 0.0786; ///< resolution of the TRU digitizer, @TODO check exact value } // namespace constants +namespace triggerbits +{ +constexpr uint32_t Inc = 0x1 << 20; ///< trigger bit marking incomplete event +} + enum FitAlgorithm { Standard = 0, ///< Standard raw fitter Gamma2 = 1, ///< Gamma2 raw fitter diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h index a7e1f72ca1baf..1529eff104400 100644 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h +++ b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/TriggerRecord.h @@ -61,8 +61,9 @@ class TriggerRecord /// \enum TriggerBitsCoded_t /// \brief Position of trigger classes in compressed format enum TriggerBitsCoded_t { - PHYSTRIGGER, ///< Physics trigger - CALIBTRIGGER ///< Calib trigger + PHYSTRIGGER, ///< Physics trigger + CALIBTRIGGER, ///< Calib trigger + REJECTINCOMPLETE ///< Rejected as incomplete }; BCData mBCData; /// Bunch crossing data of the trigger DataRange mDataRange; /// Index of the triggering event (event index and first entry in the container) diff --git a/DataFormats/Detectors/EMCAL/src/Cell.cxx b/DataFormats/Detectors/EMCAL/src/Cell.cxx index 385c4e6fffece..261384d53ca2a 100644 --- a/DataFormats/Detectors/EMCAL/src/Cell.cxx +++ b/DataFormats/Detectors/EMCAL/src/Cell.cxx @@ -44,6 +44,21 @@ const float ENERGY_RESOLUTION_TRU = ENERGY_TRUNCATION / ENERGY_BITS, ENERGY_RESOLUTION_LEDMON = ENERGY_TRUNCATION / ENERGY_BITS; } + +namespace v2 +{ +const float + ENERGY_BITS = static_cast(0x3FFF), + SAFETYMARGIN = 0.2, + HGLGTRANSITION = o2::emcal::constants::OVERFLOWCUT * o2::emcal::constants::EMCAL_ADCENERGY, + OFFSET_LG = HGLGTRANSITION - SAFETYMARGIN, + ENERGY_TRUNCATION = 250., + ENERGY_RESOLUTION_LG = (ENERGY_TRUNCATION - OFFSET_LG) / ENERGY_BITS, + ENERGY_RESOLUTION_HG = HGLGTRANSITION / ENERGY_BITS, + ENERGY_RESOLUTION_TRU = ENERGY_TRUNCATION / ENERGY_BITS, + ENERGY_RESOLUTION_LEDMON = ENERGY_TRUNCATION / ENERGY_BITS; + +} } // namespace EnergyEncoding namespace DecodingV0 @@ -87,6 +102,10 @@ uint16_t Cell::getEnergyEncoded(EncoderVersion version) const case EncoderVersion::EncodingV1: energyBits = encodeEnergyV1(mEnergy, mChannelType); break; + + case EncoderVersion::EncodingV2: + energyBits = encodeEnergyV2(mEnergy, mChannelType); + break; } return energyBits; } @@ -105,6 +124,9 @@ void Cell::setEnergyEncoded(uint16_t energyBits, uint16_t channelTypeBits, Encod case EncoderVersion::EncodingV1: mEnergy = decodeEnergyV1(energyBits, static_cast(channelTypeBits)); break; + case EncoderVersion::EncodingV2: + mEnergy = decodeEnergyV2(energyBits, static_cast(channelTypeBits)); + break; } } @@ -214,12 +236,55 @@ uint16_t Cell::encodeEnergyV1(float energy, ChannelType_t celltype) return static_cast(std::round((truncatedEnergy - energyOffset) / resolutionApplied)); }; +uint16_t Cell::encodeEnergyV2(float energy, ChannelType_t celltype) +{ + double truncatedEnergy = energy; + if (truncatedEnergy < 0.) { + truncatedEnergy = 0.; + } else if (truncatedEnergy > EnergyEncoding::v2::ENERGY_TRUNCATION) { + truncatedEnergy = EnergyEncoding::v2::ENERGY_TRUNCATION; + } + float resolutionApplied = 0., energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v2::OFFSET_LG; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return static_cast(std::round((truncatedEnergy - energyOffset) / resolutionApplied)); +}; + uint16_t Cell::V0toV1(uint16_t energyBits, ChannelType_t celltype) { auto decodedEnergy = decodeEnergyV0(energyBits); return encodeEnergyV1(decodedEnergy, celltype); } +uint16_t Cell::V0toV2(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV0(energyBits); + return encodeEnergyV2(decodedEnergy, celltype); +} + +uint16_t Cell::V1toV2(uint16_t energyBits, ChannelType_t celltype) +{ + auto decodedEnergy = decodeEnergyV1(energyBits, celltype); + return encodeEnergyV2(decodedEnergy, celltype); +} + float Cell::decodeTime(uint16_t timestampBits) { return (static_cast(timestampBits) * TimeEncoding::TIME_RESOLUTION) - TimeEncoding::TIME_SHIFT; @@ -256,6 +321,32 @@ float Cell::decodeEnergyV1(uint16_t energyBits, ChannelType_t celltype) return (static_cast(energyBits) * resolutionApplied) + energyOffset; } +float Cell::decodeEnergyV2(uint16_t energyBits, ChannelType_t celltype) +{ + float resolutionApplied = 0., + energyOffset = 0.; + switch (celltype) { + case ChannelType_t::HIGH_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_HG; + break; + } + case ChannelType_t::LOW_GAIN: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LG; + energyOffset = EnergyEncoding::v2::OFFSET_LG; + break; + } + case ChannelType_t::TRU: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_TRU; + break; + } + case ChannelType_t::LEDMON: { + resolutionApplied = EnergyEncoding::v2::ENERGY_RESOLUTION_LEDMON; + break; + } + } + return (static_cast(energyBits) * resolutionApplied) + energyOffset; +} + void Cell::PrintStream(std::ostream& stream) const { stream << "EMCAL Cell: Type " << getType() << ", Energy " << getEnergy() << ", Time " << getTimeStamp() << ", Tower " << getTower(); diff --git a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx index f380b944f807d..7ca716dbb4ea2 100644 --- a/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx +++ b/DataFormats/Detectors/EMCAL/src/TriggerRecord.cxx @@ -13,6 +13,7 @@ #include #include "DataFormatsEMCAL/TriggerRecord.h" #include "CommonConstants/Triggers.h" +#include "DataFormatsEMCAL/Constants.h" namespace o2 { @@ -29,6 +30,9 @@ uint16_t TriggerRecord::getTriggerBitsCompressed() const if (mTriggerBits & o2::trigger::Cal) { result |= 1 << TriggerBitsCoded_t::CALIBTRIGGER; } + if (mTriggerBits & o2::emcal::triggerbits::Inc) { + result |= 1 << TriggerBitsCoded_t::REJECTINCOMPLETE; + } return result; } @@ -41,6 +45,9 @@ void TriggerRecord::setTriggerBitsCompressed(uint16_t triggerbits) if (triggerbits & (1 << TriggerBitsCoded_t::CALIBTRIGGER)) { mTriggerBits |= o2::trigger::Cal; } + if (triggerbits & (1 << TriggerBitsCoded_t::REJECTINCOMPLETE)) { + mTriggerBits |= o2::emcal::triggerbits::Inc; + } } void TriggerRecord::printStream(std::ostream& stream) const diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h index 36dcf85a528b5..a766b7d72bd9d 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainer.h @@ -37,6 +37,7 @@ namespace o2::tpc class TrackTPC; using TPCClRefElem = uint32_t; struct ClusterNativeAccess; +struct TriggerInfoDLBZS; namespace internal { struct getWorkflowTPCInput_ret; @@ -224,6 +225,7 @@ struct DataRequest { void requestITSClusters(bool mc); void requestMFTClusters(bool mc); void requestTPCClusters(bool mc); + void requestTPCTriggers(); void requestTOFClusters(bool mc); void requestTRDTracklets(bool mc); void requestMCHClusters(bool mc); @@ -370,6 +372,7 @@ struct RecoContainer { void addITSClusters(o2::framework::ProcessingContext& pc, bool mc); void addMFTClusters(o2::framework::ProcessingContext& pc, bool mc); void addTPCClusters(o2::framework::ProcessingContext& pc, bool mc, bool shmap); + void addTPCTriggers(o2::framework::ProcessingContext& pc); void addTOFClusters(o2::framework::ProcessingContext& pc, bool mc); void addHMPClusters(o2::framework::ProcessingContext& pc, bool mc); void addTRDTracklets(o2::framework::ProcessingContext& pc, bool mc); @@ -535,6 +538,7 @@ struct RecoContainer { auto getTPCTrackMCLabel(GTrackID id) const { return getObject(id, MCLABELS); } const o2::tpc::ClusterNativeAccess& getTPCClusters() const; const o2::dataformats::ConstMCTruthContainerView* getTPCClustersMCLabels() const; + auto getTPCTriggers() const { return getSpan(GTrackID::TPC, MATCHES); } // ITS-TPC const o2::dataformats::TrackTPCITS& getTPCITSTrack(GTrackID gid) const { return getTrack(gid); } diff --git a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h index 9730f8c7ac506..4bbe355a781c7 100644 --- a/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h +++ b/DataFormats/Detectors/GlobalTracking/include/DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h @@ -17,6 +17,7 @@ #include "Framework/InputSpec.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsTPC/WorkflowHelper.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTRD/RecoInputContainer.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITS/TrackITS.h" @@ -195,12 +196,11 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID flagUsed(trc.getRefGlobalTrackId()); // flag seeding ITS-TPC track continue; } - float t0 = t0Trig, t0Err = 5.e-3; // 5ns nominal error - if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t0 += trc.getPileUpTimeShiftMUS(); + float t0Err = 5.e-3; // 5ns nominal error + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time t0Err += trc.getPileUpTimeErrorMUS(); } - if (creator(trc, {i, currentSource}, t0, t0Err)) { // assign 1ns error to BC + if (creator(trc, {i, currentSource}, t0Trig, t0Err)) { // assign 1ns error to BC flagUsed(trc.getRefGlobalTrackId()); // flag seeding ITS-TPC track } } @@ -248,12 +248,11 @@ void o2::globaltracking::RecoContainer::createTracksVariadic(T creator, GTrackID flagUsed(trc.getRefGlobalTrackId()); // flag seeding TPC track continue; } - float t0 = t0Trig, t0Err = 5.e-3; // 5ns nominal error - if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t0 += trc.getPileUpTimeShiftMUS(); + float t0Err = 5.e-3; // 5ns nominal error + if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time t0Err += trc.getPileUpTimeErrorMUS(); } - if (creator(trc, {i, currentSource}, t0, t0Err)) { // assign 1ns error to BC + if (creator(trc, {i, currentSource}, t0Trig, t0Err)) { // assign 1ns error to BC flagUsed(trc.getRefGlobalTrackId()); // flag seeding TPC track } } diff --git a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx index 18b4815a0856d..fdfdf1e18fd96 100644 --- a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx @@ -250,6 +250,9 @@ void DataRequest::requestMFTClusters(bool mc) void DataRequest::requestTPCClusters(bool mc) { addInput({"clusTPC", ConcreteDataTypeMatcher{"TPC", "CLUSTERNATIVE"}, Lifetime::Timeframe}); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + requestTPCTriggers(); + } if (requestMap.find("trackTPC") != requestMap.end()) { addInput({"clusTPCshmap", "TPC", "CLSHAREDMAP", 0, Lifetime::Timeframe}); } @@ -259,6 +262,12 @@ void DataRequest::requestTPCClusters(bool mc) requestMap["clusTPC"] = mc; } +void DataRequest::requestTPCTriggers() +{ + addInput({"trigTPC", "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe}); + requestMap["trigTPC"] = false; +} + void DataRequest::requestTOFClusters(bool mc) { addInput({"tofcluster", "TOF", "CLUSTERS", 0, Lifetime::Timeframe}); @@ -672,6 +681,11 @@ void RecoContainer::collectData(ProcessingContext& pc, const DataRequest& reques addTPCClusters(pc, req->second, reqMap.find("trackTPC") != reqMap.end()); } + req = reqMap.find("trigTPC"); + if (req != reqMap.end()) { + addTPCTriggers(pc); + } + req = reqMap.find("clusTOF"); if (req != reqMap.end()) { addTOFClusters(pc, req->second); @@ -1054,6 +1068,12 @@ void RecoContainer::addTPCClusters(ProcessingContext& pc, bool mc, bool shmap) } } +//__________________________________________________________ +void RecoContainer::addTPCTriggers(ProcessingContext& pc) +{ + commonPool[GTrackID::TPC].registerContainer(pc.inputs().get>("trigTPC"), MATCHES); +} + //__________________________________________________________ void RecoContainer::addTRDTracklets(ProcessingContext& pc, bool mc) { @@ -1102,7 +1122,9 @@ void RecoContainer::addMIDClusters(ProcessingContext& pc, bool mc) void RecoContainer::addCTPDigits(ProcessingContext& pc, bool mc) { commonPool[GTrackID::CTP].registerContainer(pc.inputs().get>("CTPDigits"), CLUSTERS); - mCTPLumi = pc.inputs().get("CTPLumi"); + if (pc.inputs().get>("CTPLumi").size() == sizeof(o2::ctp::LumiInfo)) { + mCTPLumi = pc.inputs().get("CTPLumi"); + } if (mc) { // pc.inputs().get*>("CTPDigitsMC"); } @@ -1499,9 +1521,9 @@ void RecoContainer::getTrackTimeITSTPCTRD(GTrackID gid, float& t, float& tErr) c tErr = 5.e-3; const auto& trc = getITSTPCTRDTracks()[gid]; if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t += trc.getPileUpTimeShiftMUS(); tErr += trc.getPileUpTimeErrorMUS(); } + return; } } } @@ -1517,9 +1539,9 @@ void RecoContainer::getTrackTimeTPCTRD(GTrackID gid, float& t, float& tErr) cons tErr = 5.e-3; const auto& trc = getTPCTRDTracks()[gid]; if (trc.hasPileUpInfo()) { // distance to farthest collision within the pileup integration time - t += trc.getPileUpTimeShiftMUS(); tErr += trc.getPileUpTimeErrorMUS(); } + return; } } } @@ -1548,6 +1570,7 @@ void RecoContainer::getTrackTimeITS(GTrackID gid, float& t, float& tErr) const if (gid.getIndex() < rof.getFirstEntry() + rof.getNEntries()) { t = rof.getBCData().differenceInBC(startIR) * o2::constants::lhc::LHCBunchSpacingMUS; tErr = 0.5; + return; } } } diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index 3cef7f45a9fb1..709e8aa3ee329 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h @@ -122,13 +122,39 @@ class TrackITS : public o2::track::TrackParCov void setNextROFbit(bool toggle = true) { setUserField((getUserField() & ~kNextROF) | (-toggle & kNextROF)); } bool hasHitInNextROF() const { return getUserField() & kNextROF; } + void setClusterSize(int l, int size) + { + if (l >= 8) { + return; + } + if (size > 15) { + size = 15; + } + mClusterSizes &= ~(0xf << (l * 4)); + mClusterSizes |= (size << (l * 4)); + } + + int getClusterSize(int l) + { + if (l >= 8) { + return 0; + } + return (mClusterSizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return mClusterSizes; + } + private: o2::track::TrackParCov mParamOut; ///< parameter at largest radius ClusRefs mClusRef; ///< references on clusters float mChi2 = 0.; ///< Chi2 for this track uint32_t mPattern = 0; ///< layers pattern + unsigned int mClusterSizes = 0u; - ClassDefNV(TrackITS, 5); + ClassDefNV(TrackITS, 6); }; class TrackITSExt : public TrackITS diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h index 164fd850fd539..7a027c2a2156c 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/TrkClusRef.h @@ -25,12 +25,35 @@ namespace itsmft // can refer to max 15 indices in the vector of total length <268435456, i.e. 17895697 tracks in worst case struct TrkClusRef : public o2::dataformats::RangeRefComp<4> { using o2::dataformats::RangeRefComp<4>::RangeRefComp; + uint32_t clsizes = 0; ///< cluster sizes for each layer uint16_t pattern = 0; ///< layers pattern GPUd() int getNClusters() const { return getEntries(); } bool hasHitOnLayer(int i) { return pattern & (0x1 << i); } - ClassDefNV(TrkClusRef, 1); + void setClusterSize(int l, int size) + { + if (l >= 8) + return; + if (size > 15) + size = 15; + clsizes &= ~(0xf << (l * 4)); + clsizes |= (size << (l * 4)); + } + + int getClusterSize(int l) + { + if (l >= 8) + return 0; + return (clsizes >> (l * 4)) & 0xf; + } + + int getClusterSizes() const + { + return clsizes; + } + + ClassDefNV(TrkClusRef, 2); }; } // namespace itsmft diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h index a31ece315d408..881f87ff88971 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/CTF.h @@ -64,7 +64,7 @@ struct CompressedInfos { // Hit data std::vector timeFrameInc; /// time increment with respect of previous digit in TimeFrame units - std::vector timeTDCInc; /// time increment with respect of previous digit in TDC channel (about 24.4 ps) within timeframe + std::vector timeTDCInc; /// time increment with respect of previous digit in TDC channel (about 24.4 ps) within timeframe std::vector stripID; /// increment of stripID wrt that of prev. strip std::vector chanInStrip; /// channel in strip 0-95 (ordered in time) std::vector tot; /// Time-Over-Threshold in TOF channel (about 48.8 ps) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h index 1e57c9654932c..1f4f08db9d1b9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CTF.h @@ -27,14 +27,15 @@ namespace tpc struct CTFHeader : public ctf::CTFDictHeader, public CompressedClustersCounters { enum : uint32_t { CombinedColumns = 0x1 }; uint32_t flags = 0; - - ClassDefNV(CTFHeader, 2); + uint32_t firstOrbitTrig = 0; /// orbit of 1st trigger + uint16_t nTriggers = 0; /// number of triggers + ClassDefNV(CTFHeader, 3); }; /// wrapper for the Entropy-encoded clusters of the TF -struct CTF : public o2::ctf::EncodedBlocks { +struct CTF : public o2::ctf::EncodedBlocks { - using container_t = o2::ctf::EncodedBlocks; + using container_t = o2::ctf::EncodedBlocks; static constexpr size_t N = getNBlocks(); static constexpr int NBitsQTot = 16; @@ -66,9 +67,14 @@ struct CTF : public o2::ctf::EncodedBlocks { BLCsigmaPadU, BLCsigmaTimeU, // can be combined with BLCsigmaPadU BLCnTrackClusters, - BLCnSliceRowClusters }; + BLCnSliceRowClusters, + // trigger info + BLCTrigOrbitInc, + BLCTrigBCInc, + BLCTrigType + }; - ClassDefNV(CTF, 2); + ClassDefNV(CTF, 4); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h index f5a3626294eb9..17378df332775 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/LtrCalibData.h @@ -35,13 +35,42 @@ struct LtrCalibData { float dvOffsetA{}; ///< drift velocity trigger offset A-Side float dvOffsetC{}; ///< drift velocity trigger offset C-Side float refVDrift{}; ///< reference vdrift for which factor was extracted + float refTimeOffset{0.}; ///< additive time offset reference (\mus) + float timeOffsetCorr{0.}; ///< additive time offset correction (\mus) uint16_t nTracksA{}; ///< number of tracks used for A-Side fit uint16_t nTracksC{}; ///< number of tracks used for C-Side fit std::vector matchedLtrIDs; ///< matched laser track IDs std::vector nTrackTF; ///< number of laser tracks per TF std::vector dEdx; ///< dE/dx of each track - float getDriftVCorrection() const { return 0.5f * (dvCorrectionA + dvCorrectionC); } + float getDriftVCorrection() const + { + float correction = 0; + int nCorr = 0; + // only allow +- 20% around reference correction + if (std::abs(dvCorrectionA - 1.f) < 0.2) { + correction += dvCorrectionA; + ++nCorr; + } else { + LOGP(warning, "abs(dvCorrectionA ({}) - 1) >= 0.2, not using for combined estimate", dvCorrectionA); + } + + if (std::abs(dvCorrectionC - 1.f) < 0.2) { + correction += dvCorrectionC; + ++nCorr; + } else { + LOGP(warning, "abs(dvCorrectionC ({}) - 1) >= 0.2, not using for combined estimate", dvCorrectionC); + } + + if (nCorr == 0) { + LOGP(error, "no valid drift velocity correction"); + return 1.f; + } + + return correction / nCorr; + } + + float getTimeOffset() const { return refTimeOffset + timeOffsetCorr; } // renormalize reference and correction either to provided new reference (if >0) or to correction 1 wrt current reference void normalize(float newVRef = 0.f) @@ -63,6 +92,20 @@ struct LtrCalibData { dvCorrectionC *= fact; } + // similarly, the time offset reference is set to provided newRefTimeOffset (if > -998) or modified to have timeOffsetCorr to + // be 0 otherwise + + void normalizeOffset(float newRefTimeOffset = -999.) + { + if (newRefTimeOffset > -999.) { + timeOffsetCorr = getTimeOffset() - newRefTimeOffset; + refTimeOffset = newRefTimeOffset; + } else { + refTimeOffset = getTimeOffset(); + timeOffsetCorr = 0.; + } + } + float getT0A() const { return (250.f * (1.f - dvCorrectionA) - dvOffsetA) / refVDrift; } float getT0C() const { return (250.f * (1.f - dvCorrectionC) + dvOffsetC) / refVDrift; } float getZOffsetA() const { return (250.f * (1.f - dvCorrectionA) - dvOffsetA) / dvCorrectionA; } @@ -81,12 +124,14 @@ struct LtrCalibData { nTracksA = 0; nTracksC = 0; refVDrift = 0; + refTimeOffset = 0; + timeOffsetCorr = 0; matchedLtrIDs.clear(); nTrackTF.clear(); dEdx.clear(); } - ClassDefNV(LtrCalibData, 3); + ClassDefNV(LtrCalibData, 4); }; } // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h index f89e790e36a87..b1df9445bcf42 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/ZeroSuppression.h @@ -19,6 +19,7 @@ #include // for size_t #endif #include "GPUCommonDef.h" +#include "GPUCommonRtypes.h" namespace o2 { @@ -101,6 +102,16 @@ struct TriggerWordDLBZS { uint16_t getTriggerBC(int entry = 0) const { return triggerEntries[entry] & 0xFFF; } uint16_t getTriggerType(int entry = 0) const { return (triggerEntries[entry] >> 12) & 0x7; } bool isValid(int entry = 0) const { return triggerEntries[entry] & 0x8000; } + + ClassDefNV(TriggerWordDLBZS, 1); +}; + +/// Trigger info including the orbit +struct TriggerInfoDLBZS { + TriggerWordDLBZS triggerWord{}; ///< trigger Word information + uint32_t orbit{}; ///< orbit of the trigger word + + ClassDefNV(TriggerInfoDLBZS, 1); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h index 63b64a50b63dc..bdf9c41aae421 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/dEdxInfo.h @@ -22,24 +22,24 @@ namespace o2 namespace tpc { struct dEdxInfo { - float dEdxTotIROC; - float dEdxTotOROC1; - float dEdxTotOROC2; - float dEdxTotOROC3; - float dEdxTotTPC; - float dEdxMaxIROC; - float dEdxMaxOROC1; - float dEdxMaxOROC2; - float dEdxMaxOROC3; - float dEdxMaxTPC; - unsigned char NHitsIROC; - unsigned char NHitsSubThresholdIROC; - unsigned char NHitsOROC1; - unsigned char NHitsSubThresholdOROC1; - unsigned char NHitsOROC2; - unsigned char NHitsSubThresholdOROC2; - unsigned char NHitsOROC3; - unsigned char NHitsSubThresholdOROC3; + float dEdxTotIROC = 0.f; + float dEdxTotOROC1 = 0.f; + float dEdxTotOROC2 = 0.f; + float dEdxTotOROC3 = 0.f; + float dEdxTotTPC = 0.f; + float dEdxMaxIROC = 0.f; + float dEdxMaxOROC1 = 0.f; + float dEdxMaxOROC2 = 0.f; + float dEdxMaxOROC3 = 0.f; + float dEdxMaxTPC = 0.f; + unsigned char NHitsIROC = 0; + unsigned char NHitsSubThresholdIROC = 0; + unsigned char NHitsOROC1 = 0; + unsigned char NHitsSubThresholdOROC1 = 0; + unsigned char NHitsOROC2 = 0; + unsigned char NHitsSubThresholdOROC2 = 0; + unsigned char NHitsOROC3 = 0; + unsigned char NHitsSubThresholdOROC3 = 0; ClassDefNV(dEdxInfo, 1); }; } // namespace tpc diff --git a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h index 023892f01b92a..676a4e0144be0 100644 --- a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h +++ b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h @@ -48,7 +48,7 @@ #pragma link C++ class o2::tpc::CompressedClustersROOT - ; #pragma link C++ class o2::tpc::CTF + ; #pragma link C++ class o2::tpc::CTFHeader + ; -#pragma link C++ class o2::ctf::EncodedBlocks < o2::tpc::CTFHeader, 23, uint32_t> + ; +#pragma link C++ class o2::ctf::EncodedBlocks < o2::tpc::CTFHeader, 26, uint32_t> + ; #pragma link C++ enum o2::tpc::StatisticsType; #pragma link C++ class o2::tpc::TrackCuts + ; #pragma link C++ class o2::tpc::KrCluster + ; @@ -70,5 +70,8 @@ #pragma link C++ class o2::tpc::dcs::DataPointVector < o2::tpc::dcs::HV::StackState> + ; #pragma link C++ class o2::tpc::dcs::Gas + ; #pragma link C++ class o2::tpc::PIDResponse + ; +#pragma link C++ class o2::tpc::TriggerWordDLBZS + ; +#pragma link C++ class o2::tpc::TriggerInfoDLBZS + ; +#pragma link C++ class std::vector < o2::tpc::TriggerInfoDLBZS> + ; #endif diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h index 038c2a5684d5f..69fc23646c912 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalT0.h @@ -63,7 +63,7 @@ class CalT0 std::array mT0{}; ///< calibrated T0 per TRD chamber float mT0av{-1}; ///< average T0 obtained from fitting the PH data from all chambers combined - ClassDefNV(CalT0, 1); + ClassDefNV(CalT0, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h index 9b0d3a25f91a6..03602d96e85da 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Digit.h @@ -91,6 +91,7 @@ class Digit ADC_t getADCsum() const { return std::accumulate(mADC.begin(), mADC.end(), (ADC_t)0); } // returns the max ADC value and sets idx to the time bin with the largest ADC value ADC_t getADCmax(int& idx) const; + ADC_t getADCval(int tb) const { return mADC[tb]; } bool operator==(const Digit& o) const { diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h index 4468528865d3b..ecab7f49b92a2 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/HelperMethods.h @@ -14,6 +14,8 @@ #include "DataFormatsTRD/Constants.h" #include +#include +#include namespace o2 { @@ -54,12 +56,17 @@ struct HelperMethods { printf("%02i_%i_%i\n", det / constants::NCHAMBERPERSEC, (det % constants::NCHAMBERPERSEC) / constants::NLAYER, det % constants::NLAYER); } - static void printSectorStackLayerSide(int hcid) + static std::string getSectorStackLayerSide(int hcid) { - // for a given half-chamber number prints SECTOR_STACK_LAYER_side int det = hcid / 2; std::string side = (hcid % 2 == 0) ? "A" : "B"; - printf("%02i_%i_%i%s\n", det / constants::NCHAMBERPERSEC, (det % constants::NCHAMBERPERSEC) / constants::NLAYER, det % constants::NLAYER, side.c_str()); + return fmt::format("{}_{}_{}{}", getSector(det), getStack(det), getLayer(det), side); + } + + static void printSectorStackLayerSide(int hcid) + { + // for a given half-chamber number prints SECTOR_STACK_LAYER_side + printf("%s\n", getSectorStackLayerSide(hcid).c_str()); } static int getPadColFromADC(int irob, int imcm, int iadc) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h index dbbc3161f9b02..b8873a5247d03 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/PHData.h @@ -42,7 +42,7 @@ class PHData void set(int adc, int det, int tb, int nb, int type) { - mData = (type << 30) | (nb << 27) | (tb << 22) | (det << 12) | adc; + mData = ((type & 0x3) << 30) | ((nb & 0x7) << 27) | ((tb & 0x1f) << 22) | ((det & 0x3ff) << 12) | (adc & 0xfff); } // the ADC sum for given time bin for up to three neighbours diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h index 53401d96c2d07..04ebd9c7eea3a 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/RawDataStats.h @@ -22,6 +22,7 @@ #include #include #include "DataFormatsTRD/Constants.h" +#include "CommonDataFormat/TFIDInfo.h" namespace o2::trd { @@ -149,6 +150,7 @@ class TRDDataCountersPerTimeFrame uint16_t mNTriggersCalib; // number of triggers with digit readout uint16_t mNTriggersTotal; // total number of triggers std::array mDataFormatRead{}; // We just keep the major version number + o2::dataformats::TFIDInfo mTFIDInfo; // keep track of TF ID void clear() { mLinkNoData.fill(0); @@ -163,9 +165,11 @@ class TRDDataCountersPerTimeFrame mTimeTakenForTracklets = 0; mDigitsFound = 0; mTrackletsFound = 0; + mNTriggersCalib = 0; + mNTriggersTotal = 0; mDataFormatRead.fill(0); }; - ClassDefNV(TRDDataCountersPerTimeFrame, 2); // primarily for serialisation so we can send this as a message in o2 + ClassDefNV(TRDDataCountersPerTimeFrame, 3); // primarily for serialisation so we can send this as a message in o2 }; } // namespace o2::trd diff --git a/DataFormats/Detectors/TRD/src/Tracklet64.cxx b/DataFormats/Detectors/TRD/src/Tracklet64.cxx index af4c8e2b9d9d2..9245165709979 100644 --- a/DataFormats/Detectors/TRD/src/Tracklet64.cxx +++ b/DataFormats/Detectors/TRD/src/Tracklet64.cxx @@ -22,7 +22,7 @@ namespace trd void Tracklet64::print() const { LOGF(info, "%02i_%i_%i, ROB(%i), MCM(%i), row(%i), col(%i), position(%i), slope(%i), pid(%i), q0(%i), q1(%i), q2(%i). Format(%i)", - HelperMethods::getSector(getDetector()), HelperMethods::getStack(getDetector()), HelperMethods::getLayer(getDetector()), getROB(), getMCM(), getPadRow(), getColumn(), getPosition(), getSlope(), getPID(), getQ0(), getQ1(), getQ2(), getFormat()); + HelperMethods::getSector(getDetector()), HelperMethods::getStack(getDetector()), HelperMethods::getLayer(getDetector()), getROB(), getMCM(), getPadRow(), getPadCol(), getPosition(), getSlope(), getPID(), getQ0(), getQ1(), getQ2(), getFormat()); } #ifndef GPUCA_GPUCODE_DEVICE diff --git a/DataFormats/Headers/include/Headers/Stack.h b/DataFormats/Headers/include/Headers/Stack.h index 184733e35c339..259a445f18cf8 100644 --- a/DataFormats/Headers/include/Headers/Stack.h +++ b/DataFormats/Headers/include/Headers/Stack.h @@ -16,6 +16,7 @@ namespace o2 { + namespace header { //__________________________________________________________________________________________________ @@ -103,7 +104,11 @@ struct Stack { bufferSize{calculateSize(std::forward(headers)...)}, buffer{static_cast(allocator.resource()->allocate(bufferSize, alignof(std::max_align_t))), freeobj{allocator.resource()}} { - inject(buffer.get(), std::forward(headers)...); + if constexpr (sizeof...(headers) > 1) { + injectAll(buffer.get(), std::forward(headers)...); + } else if (sizeof...(headers) == 1) { + injectBool(buffer.get(), std::forward(headers)..., false); + } } //______________________________________________________________________________________________ @@ -144,7 +149,7 @@ struct Stack { //______________________________________________________________________________________________ template - static std::byte* inject(std::byte* here, T&& h, bool more = false) noexcept + static std::byte* injectBool(std::byte* here, T&& h, bool more) noexcept { using headerType = typename std::remove_cv::type>::type; if (here == nullptr) { @@ -189,11 +194,15 @@ struct Stack { //______________________________________________________________________________________________ template - static std::byte* inject(std::byte* here, T&& h, Args&&... args) noexcept + static std::byte* injectAll(std::byte* here, T&& h, Args&&... args) noexcept { bool more = hasNonEmptyArg(args...); - auto alsohere = inject(here, h, more); - return inject(alsohere, args...); + auto alsohere = injectBool(here, h, more); + if constexpr (sizeof...(args) > 1) { + return injectAll(alsohere, args...); + } else { + return injectBool(alsohere, args..., false); + } } //______________________________________________________________________________________________ diff --git a/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h b/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h index 7ac6cd6b00133..ddcda8f66507c 100644 --- a/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h +++ b/DataFormats/Parameters/include/DataFormatsParameters/GRPLHCIFData.h @@ -41,6 +41,7 @@ class GRPLHCIFData std::pair getBeamEnergyPerZWithTime() const { return mBeamEnergyPerZ; } int32_t getBeamEnergyPerZ() const { return mBeamEnergyPerZ.second; } + float getBeamEnergyPerZinGeV() const { return mBeamEnergyPerZ.second * 0.12; } long getBeamEnergyPerZTime() const { return mBeamEnergyPerZ.first; } void setBeamEnergyPerZWithTime(std::pair p) { mBeamEnergyPerZ = p; } void setBeamEnergyPerZWithTime(long t, int32_t v) { mBeamEnergyPerZ = std::make_pair(t, v); } @@ -84,12 +85,18 @@ class GRPLHCIFData /// getters/setters for given beam A and Z info, encoded as A<<16+Z int getBeamZ(beamDirection beam) const { return mBeamAZ[static_cast(beam)] & 0xffff; } int getBeamA(beamDirection beam) const { return mBeamAZ[static_cast(beam)] >> 16; } + int getBeamZ(int beam) const { return mBeamAZ[beam] & 0xffff; } + int getBeamA(int beam) const { return mBeamAZ[beam] >> 16; } float getBeamZoverA(beamDirection beam) const; + float getBeamZoverA(int beam) const; void setBeamAZ(int a, int z, beamDirection beam) { mBeamAZ[static_cast(beam)] = (a << 16) + z; } void setBeamAZ(beamDirection beam); void setBeamAZ(); /// getters/setters for beam energy per charge and per nucleon float getBeamEnergyPerNucleon(beamDirection beam) const { return mBeamEnergyPerZ.second * getBeamZoverA(beam); } + float getBeamEnergyPerNucleon(int beam) const { return mBeamEnergyPerZ.second * getBeamZoverA(beam); } + float getBeamEnergyPerNucleonInGeV(beamDirection beam) const { return getBeamEnergyPerZinGeV() * getBeamZoverA(beam); } + float getBeamEnergyPerNucleonInGeV(int beam) const { return getBeamEnergyPerZinGeV() * getBeamZoverA(beam); } /// calculate center of mass energy per nucleon collision float getSqrtS() const; /// helper function for BunchFilling @@ -119,6 +126,14 @@ inline float GRPLHCIFData::getBeamZoverA(beamDirection b) const return a ? getBeamZ(b) / static_cast(a) : 0.f; } +//______________________________________________ +inline float GRPLHCIFData::getBeamZoverA(int b) const +{ + // Z/A of beam 0 or 1 + int a = getBeamA(b); + return a ? getBeamZ(b) / static_cast(a) : 0.f; +} + } // namespace parameters } // namespace o2 #endif diff --git a/DataFormats/Parameters/src/GRPLHCIFData.cxx b/DataFormats/Parameters/src/GRPLHCIFData.cxx index dcbca0932cf69..8e779ef452191 100644 --- a/DataFormats/Parameters/src/GRPLHCIFData.cxx +++ b/DataFormats/Parameters/src/GRPLHCIFData.cxx @@ -60,8 +60,8 @@ void GRPLHCIFData::setBeamAZ() float GRPLHCIFData::getSqrtS() const { // get center of mass energy - double e0 = getBeamEnergyPerNucleon(BeamC); - double e1 = getBeamEnergyPerNucleon(BeamA); + double e0 = getBeamEnergyPerNucleonInGeV(BeamC); + double e1 = getBeamEnergyPerNucleonInGeV(BeamA); if (e0 <= MassProton || e1 <= MassProton) { return 0.f; } diff --git a/DataFormats/Reconstruction/CMakeLists.txt b/DataFormats/Reconstruction/CMakeLists.txt index 57c0b29a8f04f..86c0831d2134e 100644 --- a/DataFormats/Reconstruction/CMakeLists.txt +++ b/DataFormats/Reconstruction/CMakeLists.txt @@ -17,6 +17,7 @@ o2_add_library(ReconstructionDataFormats src/TrackTPCITS.cxx src/Vertex.cxx src/PrimaryVertex.cxx + src/PrimaryVertexExt.cxx src/MatchInfoTOF.cxx src/MatchInfoTOFReco.cxx src/TrackLTIntegral.cxx @@ -51,6 +52,7 @@ o2_target_root_dictionary( include/ReconstructionDataFormats/GlobalFwdTrack.h include/ReconstructionDataFormats/Vertex.h include/ReconstructionDataFormats/PrimaryVertex.h + include/ReconstructionDataFormats/PrimaryVertexExt.h include/ReconstructionDataFormats/MatchInfoTOF.h include/ReconstructionDataFormats/MatchInfoTOFReco.h include/ReconstructionDataFormats/TrackLTIntegral.h diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h new file mode 100644 index 0000000000000..80a47c5ce6405 --- /dev/null +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/PrimaryVertexExt.h @@ -0,0 +1,60 @@ +// 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_PRIMARYVERTEX_EXT_H +#define ALICEO2_PRIMARYVERTEX_EXT_H + +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/GlobalTrackID.h" + +namespace o2 +{ +namespace dataformats +{ + +// extended primary vertex info + +struct PrimaryVertexExt : public PrimaryVertex { + using PrimaryVertex::PrimaryVertex; + std::array nSrc{}; // N contributors for each source type + int VtxID = -1; // original vtx ID + float FT0Amp = -1; // amplitude of closest FT0 trigger + float FT0A = -1; // amplitude of the A side + float FT0Time = -1.; // time of closest FT0 trigger + + int getNSrc(int i) const { return nSrc[i]; } + +#ifndef GPUCA_ALIGPUCODE + void print() const; + std::string asString() const; +#endif + + ClassDefNV(PrimaryVertexExt, 2); +}; + +#ifndef GPUCA_ALIGPUCODE +std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertexExt& v); +#endif + +} // namespace dataformats + +/// Defining PrimaryVertexExt explicitly as messageable +namespace framework +{ +template +struct is_messageable; +template <> +struct is_messageable : std::true_type { +}; +} // namespace framework + +} // namespace o2 +#endif diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h index fd7ea85b7bbbe..bbc2c01359276 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h @@ -178,9 +178,6 @@ template <> struct is_messageable>> : std::true_type { }; template <> -struct is_messageable>> : std::true_type { -}; -template <> struct is_messageable>> : std::true_type { }; } // namespace framework diff --git a/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx b/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx new file mode 100644 index 0000000000000..3d8ec6c5fa7c2 --- /dev/null +++ b/DataFormats/Reconstruction/src/PrimaryVertexExt.cxx @@ -0,0 +1,51 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ReconstructionDataFormats/PrimaryVertexExt.h" +#include +#include +#include "CommonUtils/StringUtils.h" + +namespace o2 +{ +namespace dataformats +{ + +#ifndef GPUCA_ALIGPUCODE +using GTrackID = o2::dataformats::GlobalTrackID; + +std::string PrimaryVertexExt::asString() const +{ + auto str = o2::utils::Str::concat_string(PrimaryVertex::asString(), fmt::format("VtxID={} FT0A/C={}/{} FT0T={}", VtxID, FT0A, FT0Amp - FT0A, FT0Time)); + for (int i = 0; i < GTrackID::Source::NSources; i++) { + if (getNSrc(i) > 0) { + str += fmt::format(" {}={}", GTrackID::getSourceName(i), getNSrc(i)); + } + } + return str; +} + +std::ostream& operator<<(std::ostream& os, const o2::dataformats::PrimaryVertexExt& v) +{ + // stream itself + os << v.asString(); + return os; +} + +void PrimaryVertexExt::print() const +{ + std::cout << *this << std::endl; +} + +#endif + +} // namespace dataformats +} // namespace o2 diff --git a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h index 29b8156caf872..dba8990e169ea 100644 --- a/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h +++ b/DataFormats/Reconstruction/src/ReconstructionDataFormatsLinkDef.h @@ -73,10 +73,12 @@ #pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStamp < int>> + ; #pragma link C++ class o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < float, float>> + ; #pragma link C++ class o2::dataformats::PrimaryVertex + ; +#pragma link C++ class o2::dataformats::PrimaryVertexExt + ; #pragma link C++ class std::vector < o2::dataformats::Vertex < o2::dataformats::TimeStamp < int>>> + ; #pragma link C++ class std::vector < o2::dataformats::Vertex < o2::dataformats::TimeStampWithError < float, float>>> + ; #pragma link C++ class std::vector < o2::dataformats::PrimaryVertex> + ; +#pragma link C++ class std::vector < o2::dataformats::PrimaryVertexExt> + ; #pragma link C++ class o2::dataformats::GlobalTrackID + ; #pragma link C++ class std::vector < o2::dataformats::GlobalTrackID> + ; diff --git a/DataFormats/Reconstruction/src/Vertex.cxx b/DataFormats/Reconstruction/src/Vertex.cxx index dfbc5eac281e8..b902e9972a13d 100644 --- a/DataFormats/Reconstruction/src/Vertex.cxx +++ b/DataFormats/Reconstruction/src/Vertex.cxx @@ -56,5 +56,8 @@ bool VertexBase::operator==(const VertexBase& other) const #endif +template class o2::dataformats::Vertex>; +template class o2::dataformats::Vertex>; + } // namespace dataformats } // namespace o2 diff --git a/DataFormats/common/CMakeLists.txt b/DataFormats/common/CMakeLists.txt index 0e0e4076eb000..6b0081a920c8d 100644 --- a/DataFormats/common/CMakeLists.txt +++ b/DataFormats/common/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(CommonDataFormat src/FlatHisto2D.cxx src/AbstractRefAccessor.cxx src/TFIDInfo.cxx + src/TimeStamp.cxx PUBLIC_LINK_LIBRARIES O2::CommonConstants O2::GPUCommon ROOT::Core O2::MathUtils Microsoft.GSL::GSL) diff --git a/DataFormats/common/src/TimeStamp.cxx b/DataFormats/common/src/TimeStamp.cxx new file mode 100644 index 0000000000000..2681c9721ba42 --- /dev/null +++ b/DataFormats/common/src/TimeStamp.cxx @@ -0,0 +1,19 @@ +// 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 "CommonDataFormat/TimeStamp.h" + +template class o2::dataformats::TimeStamp; +template class o2::dataformats::TimeStamp; +template class o2::dataformats::TimeStamp; +template class o2::dataformats::TimeStampWithError; +template class o2::dataformats::TimeStampWithError; +template class o2::dataformats::TimeStampWithError; diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h index cf323e197392d..b0dcd6891bb9c 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h +++ b/DataFormats/simulation/include/SimulationDataFormat/MCEventHeader.h @@ -27,6 +27,67 @@ namespace dataformats class GeneratorHeader; +/** Common keys for information in MC event header */ +struct MCInfoKeys { + /** @{ +@name HepMC3 heavy-ion fields */ + static constexpr const char* impactParameter = "Bimpact"; + static constexpr const char* nPart = "Npart"; + static constexpr const char* nPartProjectile = "Npart_proj"; + static constexpr const char* nPartTarget = "Npart_targ"; + static constexpr const char* nColl = "Ncoll"; + static constexpr const char* nCollHard = "Ncoll_hard"; + static constexpr const char* nCollNNWounded = "NColl_NNw"; + static constexpr const char* nCollNWoundedN = "NColl_NwN"; + static constexpr const char* nCollNWoundedNwounded = "NColl_NwNW"; + static constexpr const char* planeAngle = "eventPsi"; + static constexpr const char* sigmaInelNN = "sigmaInelNN"; + static constexpr const char* centrality = "centrality"; + static constexpr const char* nSpecProjectileProton = "Nspec_proj_p"; + static constexpr const char* nSpecProjectileNeutron = "Nspec_proj_n"; + static constexpr const char* nSpecTargetProton = "Nspec_targ_p"; + static constexpr const char* nSpecTargetNeutron = "Nspec_targ_n"; + /** @} */ + /** @{ +@name HepMC3 PDF information + +In principle a header can have many of these. In that case, +each set should be prefixed with "_" where "" is a +serial number. + */ + static constexpr const char* pdfParton1Id = "pdf_parton_1_id"; + static constexpr const char* pdfParton2Id = "pdf_parton_2_id"; + static constexpr const char* pdfX1 = "pdf_x1"; + static constexpr const char* pdfX2 = "pdf_x2"; + static constexpr const char* pdfScale = "pdf_scale"; + static constexpr const char* pdfXF1 = "pdf_par_x1"; + static constexpr const char* pdfXF2 = "pdf_par_x2"; + static constexpr const char* pdfCode1 = "pdf_lhc_1_id"; + static constexpr const char* pdfCode2 = "pdf_lhc_2_id"; + /** @} */ + /** @{ +@name HepMC3 cross-section information + +In principle we can have one cross section per weight. In that +case, each should be post-fixed by "_" where "" is a +serial number. These should then matcht possible names of +weights. + */ + static constexpr const char* acceptedEvents = "accepted_events"; + static constexpr const char* attemptedEvents = "attempted_events"; + static constexpr const char* xSection = "cross_section"; + static constexpr const char* xSectionError = "cross_section_error"; + /** @} */ + /** @{ +@name Common fields */ + static constexpr const char* generator = "generator"; + static constexpr const char* generatorVersion = "version"; + static constexpr const char* processName = "processName"; + static constexpr const char* processCode = "processCode"; + static constexpr const char* weight = "weight"; + /** @} */ +}; + /*****************************************************************/ /*****************************************************************/ @@ -86,6 +147,9 @@ class MCEventHeader : public FairMCEventHeader MCEventStats& getMCEventStats() { return mEventStats; } + void setDetId2HitBitLUT(std::vector const& v) { mDetId2HitBitIndex = v; } + std::vector const& getDetId2HitBitLUT() const { return mDetId2HitBitIndex; } + /// create a standalone ROOT file/tree with only the MCHeader branch static void extractFileFromKinematics(std::string_view kinefilename, std::string_view targetfilename); @@ -96,9 +160,10 @@ class MCEventHeader : public FairMCEventHeader // store a few global properties that this event // had in the current simulation (which can be used quick filtering/searching) MCEventStats mEventStats{}; + std::vector mDetId2HitBitIndex; o2::utils::RootSerializableKeyValueStore mEventInfo; - ClassDefOverride(MCEventHeader, 3); + ClassDefOverride(MCEventHeader, 4); }; /** class MCEventHeader **/ diff --git a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h index a08a12327bc59..4ebf5fe0c579c 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h +++ b/DataFormats/simulation/include/SimulationDataFormat/MCTrack.h @@ -27,6 +27,7 @@ #include "TParticle.h" #include "TParticlePDG.h" #include "TVector3.h" +#include namespace o2 { @@ -45,6 +46,8 @@ template class MCTrackT { public: + static constexpr int NHITBITS = 22; // do not modify this + /// Default constructor MCTrackT(); @@ -158,24 +161,40 @@ class MCTrackT void SetSecondMotherTrackId(Int_t id) { mSecondMotherTrackId = id; } void SetFirstDaughterTrackId(Int_t id) { mFirstDaughterTrackId = id; } void SetLastDaughterTrackId(Int_t id) { mLastDaughterTrackId = id; } + // set bit indicating that this track - // left a hit in detector with id iDet - void setHit(Int_t iDet) + // left a hit in detector that corresponds to bit iDetBit. This bit is found in + // a lookup table. + void setHit(Int_t iDetBit) { - assert(0 <= iDet && iDet < o2::detectors::DetID::nDetectors); + assert(0 <= iDetBit && iDetBit < o2::detectors::DetID::nDetectors); auto prop = ((PropEncoding)mProp); - prop.hitmask |= 1 << iDet; + prop.hitmask |= 1 << iDetBit; mProp = prop.i; } - // did detector iDet see this track? - bool leftTrace(Int_t iDet) const { return (((PropEncoding)mProp).hitmask & (1 << iDet)) > 0; } + bool leftTraceGivenBitField(int bit) const + { + return (((PropEncoding)mProp).hitmask & (1 << bit)) > 0; + } + + /// Returns of detector with DetID iDet has seen this track. + /// Needs lookup table detIDToBit mapping detectorIDs to bitfields (which is persistified in MCEventHeaders). + bool leftTrace(Int_t iDet, std::vector const& detIDtoBit) const + { + auto bit = detIDtoBit[iDet]; + if (bit != -1) { + return leftTraceGivenBitField(bit); + } + return false; + } + // determine how many detectors "saw" this track int getNumDet() const { int count = 0; - for (auto i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) { - if (leftTrace(i)) { + for (auto i = 0; i < NHITBITS; ++i) { + if (leftTraceGivenBitField(i)) { count++; } } @@ -260,7 +279,8 @@ class MCTrackT struct { int storage : 1; // encoding whether to store this track to the output unsigned int process : 6; // encoding process that created this track (enough to store TMCProcess from ROOT) - int hitmask : 22; // encoding hits per detector + int hitmask : NHITBITS; // encoding hits per detector + static_assert(o2::detectors::DetID::nDetectors <= 22); // ensure that all known detectors can be encoded here by a bit int reserved1 : 1; // bit reserved for possible future purposes int inhibited : 1; // whether tracking of this was inhibited int toBeDone : 1; // whether this (still) needs tracking --> we might more complete information to cover full ParticleStatus space diff --git a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h index 89494127ee7b7..3225f3536f1cf 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h +++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h @@ -300,14 +300,14 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) // Lithium 4 ground state ionCode = 1000030040; if (!db->GetParticle(ionCode)) { - db->AddParticle("Lithium4", "Lithium4", 3.74976, kFALSE, - 0.005, 9, "Ion", ionCode); + db->AddParticle("Lithium4", "Lithium4", 3.7513, kFALSE, + 0.003, 9, "Ion", ionCode); } // anti Lithium 4 ground state ionCode = -1000030040; if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiLithium4", "AntiLithium4", 3.74976, kFALSE, - 0.005, 9, "Ion", ionCode); + db->AddParticle("AntiLithium4", "AntiLithium4", 3.7513, kFALSE, + 0.003, 9, "Ion", ionCode); } ionCode = 1010020050; diff --git a/DataFormats/simulation/test/MCTrack.cxx b/DataFormats/simulation/test/MCTrack.cxx index 699adc89abb8c..e857e2b88da41 100644 --- a/DataFormats/simulation/test/MCTrack.cxx +++ b/DataFormats/simulation/test/MCTrack.cxx @@ -24,10 +24,17 @@ using namespace o2; BOOST_AUTO_TEST_CASE(MCTrack_test) { MCTrack track; + + // auxiliary lookup table needed to fetch and set hit properties + std::vector hitLUT(o2::detectors::DetID::nDetectors, -1); + // in this test we have a single fictional detector 1, which we map to + // the first bit + hitLUT[1] = 0; + // check properties on default constructed object BOOST_CHECK(track.getStore() == false); for (auto i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) { - BOOST_CHECK(track.leftTrace(i) == false); + BOOST_CHECK(track.leftTrace(i, hitLUT) == false); } BOOST_CHECK(track.getNumDet() == 0); BOOST_CHECK(track.hasHits() == false); @@ -41,10 +48,10 @@ BOOST_AUTO_TEST_CASE(MCTrack_test) BOOST_CHECK(track.getStore() == true); // set hit for first detector - BOOST_CHECK(track.leftTrace(1) == false); - track.setHit(1); + BOOST_CHECK(track.leftTrace(1, hitLUT) == false); + track.setHit(hitLUT[1]); BOOST_CHECK(track.hasHits() == true); - BOOST_CHECK(track.leftTrace(1) == true); + BOOST_CHECK(track.leftTrace(1, hitLUT) == true); BOOST_CHECK(track.getNumDet() == 1); // check process encoding diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 10c70ad2e77eb..854b487cbe78e 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -356,6 +356,7 @@ class AODProducerWorkflowDPL : public Task struct TrackExtraInfo { float tpcInnerParam = 0.f; uint32_t flags = 0; + uint32_t itsClusterSizes = 0u; uint8_t itsClusterMap = 0; uint8_t tpcNClsFindable = 0; int8_t tpcNClsFindableMinusFound = 0; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 287dfcab179a0..9c2f1d13eefaf 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -2111,7 +2111,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) fillStrangenessTrackingTables(recoData, trackedV0Cursor, trackedCascadeCursor, tracked3BodyCurs); // helper map for fast search of a corresponding class mask for a bc - std::unordered_map> bcToClassMask; + std::unordered_map> bcToClassMask; if (mInputSources[GID::CTP]) { LOG(debug) << "CTP input available"; for (auto& ctpDigit : ctpDigits) { @@ -2124,22 +2124,20 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } // filling BC table - std::vector masks; bcCursor.reserve(bcsMap.size()); for (auto& item : bcsMap) { uint64_t bc = item.first; + std::pair masks{0, 0}; if (mInputSources[GID::CTP]) { auto bcClassPair = bcToClassMask.find(bc); if (bcClassPair != bcToClassMask.end()) { masks = bcClassPair->second; - } else { - masks = {0, 0}; } } bcCursor(runNumber, bc, - masks[0], - masks[1]); + masks.first, + masks.second); } bcToClassMask.clear(); @@ -2731,6 +2729,9 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo dataRequest->requestStrangeTracks(useMC); LOGF(info, "requestStrangeTracks Finish"); } + if (src[GID::ITS]) { + dataRequest->requestClusters(GIndex::getSourcesMask("ITS"), false); + } if (src[GID::TPC]) { dataRequest->requestClusters(GIndex::getSourcesMask("TPC"), false); // no need to ask for TOF clusters as they are requested with TOF tracks } diff --git a/Detectors/AOD/src/aod-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx index 855f84e3031b3..1f39f11218be3 100644 --- a/Detectors/AOD/src/aod-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -36,7 +36,7 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, - {"disable-strangeness-tracking", o2::framework::VariantType::Bool, false, {"disable filling strangeness tracking"}}, + {"disable-strangeness-tracker", o2::framework::VariantType::Bool, false, {"disable filling strangeness tracking"}}, {"info-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}, @@ -52,7 +52,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); auto useMC = !configcontext.options().get("disable-mc"); bool enableSV = !configcontext.options().get("disable-secondary-vertices"); - bool enableST = !configcontext.options().get("disable-strangeness-tracking"); + bool enableST = !configcontext.options().get("disable-strangeness-tracker"); bool ctpcfgperrun = !configcontext.options().get("ctpconfig-run-independent"); GID::mask_t allowedSrc = GID::getSourcesMask("ITS,MFT,MCH,MID,MCH-MID,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,FT0,FV0,FDD,ZDC,EMC,CTP,PHS,CPV,HMP"); diff --git a/Detectors/Base/include/DetectorsBase/Detector.h b/Detectors/Base/include/DetectorsBase/Detector.h index 73764e8b7021b..6acfa4f5cc46c 100644 --- a/Detectors/Base/include/DetectorsBase/Detector.h +++ b/Detectors/Base/include/DetectorsBase/Detector.h @@ -216,6 +216,10 @@ class Detector : public FairDetector // which is required for media creation static void initFieldTrackingParams(int& mode, float& maxfield); + /// set the DetID to HitBitIndex mapping. Succeeds if not already set. + static void setDetId2HitBitIndex(std::vector const& v) { Detector::sDetId2HitBitIndex = v; } + static std::vector const& getDetId2HitBitIndex() { return Detector::sDetId2HitBitIndex; } + protected: Detector(const Detector& origin); @@ -233,6 +237,7 @@ class Detector : public FairDetector static Float_t mDensityFactor; //! factor that is multiplied to all material densities (ONLY for // systematic studies) + static std::vector sDetId2HitBitIndex; //! global lookup table keeping mapping of DetID to index in hit bit field (used in MCTrack) ClassDefOverride(Detector, 1); // Base class for ALICE Modules }; @@ -738,6 +743,7 @@ class DetImpl : public o2::base::Detector int mInitialized = false; char* mHitCollectorBufferPtr = nullptr; //! pointer to hit (collector) buffer location (strictly internal) + ClassDefOverride(DetImpl, 0); }; } // namespace base diff --git a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h index c88b1dd9e4947..3ffa7424ee61e 100644 --- a/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h +++ b/Detectors/Base/include/DetectorsBase/MatLayerCylSet.h @@ -79,6 +79,9 @@ class MatLayerCylSet : public o2::gpu::FlatObject static MatLayerCylSet* loadFromFile(const std::string& inpFName = "matbud.root"); static MatLayerCylSet* rectifyPtrFromFile(MatLayerCylSet* ptr); + // initializes internal voxel lookup + void initLayerVoxelLU(); + void flatten(); MatLayerCyl& getLayer(int i) { return get()->mLayers[i]; } @@ -98,6 +101,9 @@ class MatLayerCylSet : public o2::gpu::FlatObject GPUd() int searchSegment(float val, int low = -1, int high = -1) const; + /// searches a layer based on r2 input, using a lookup table + GPUd() int searchLayerFast(float r2, int low = -1, int high = -1) const; + #ifndef GPUCA_GPUCODE //----------------------------------------------------------- std::size_t estimateFlatBufferSize() const; @@ -118,6 +124,14 @@ class MatLayerCylSet : public o2::gpu::FlatObject static constexpr size_t getBufferAlignmentBytes() { return 8; } #endif // !GPUCA_GPUCODE + static constexpr float LayerRMax = 500; // maximum value of R lookup (corresponds to last layer of MatLUT) + static constexpr float VoxelRDelta = 0.05; // voxel spacing for layer lookup; seems a natural choice - corresponding ~ to smallest spacing + static constexpr float InvVoxelRDelta = 1.f / VoxelRDelta; + static constexpr int NumVoxels = int(LayerRMax / VoxelRDelta); + + uint16_t mLayerVoxelLU[2 * NumVoxels]; //! helper structure to lookup a layer based on known radius (static dimension for easy copy to GPU) + bool mInitializedLayerVoxelLU = false; //! if the voxels have been initialized + ClassDefNV(MatLayerCylSet, 1); }; diff --git a/Detectors/Base/include/DetectorsBase/Propagator.h b/Detectors/Base/include/DetectorsBase/Propagator.h index 84cc4b5f30732..2227f2156c401 100644 --- a/Detectors/Base/include/DetectorsBase/Propagator.h +++ b/Detectors/Base/include/DetectorsBase/Propagator.h @@ -34,7 +34,7 @@ namespace parameters { class GRPObject; class GRPMagField; -} +} // namespace parameters namespace dataformats { @@ -45,7 +45,7 @@ namespace field { class MagFieldFast; class MagneticField; -} +} // namespace field namespace gpu { @@ -134,6 +134,7 @@ class PropagatorImpl GPUd() void setGPUField(const o2::gpu::GPUTPCGMPolynomialField* field) { mGPUField = field; } GPUd() const o2::gpu::GPUTPCGMPolynomialField* getGPUField() const { return mGPUField; } GPUd() void setBz(value_type bz) { mBz = bz; } + GPUd() bool hasMagFieldSet() const { return mField != nullptr; } GPUd() void estimateLTFast(o2::track::TrackLTIntegral& lt, const o2::track::TrackParametrization& trc) const; diff --git a/Detectors/Base/src/Detector.cxx b/Detectors/Base/src/Detector.cxx index e93295f1fc3f6..3168e0e84e1f2 100644 --- a/Detectors/Base/src/Detector.cxx +++ b/Detectors/Base/src/Detector.cxx @@ -30,6 +30,7 @@ using namespace o2::base; using namespace o2::detectors; Float_t Detector::mDensityFactor = 1.0; +std::vector o2::base::Detector::sDetId2HitBitIndex{}; // initialize empty vector Detector::Detector() : FairDetector(), mMapMaterial(), mMapMedium() {} Detector::Detector(const char* name, Bool_t Active) diff --git a/Detectors/Base/src/MatLayerCylSet.cxx b/Detectors/Base/src/MatLayerCylSet.cxx index a0f8e60708de5..fb2e720427016 100644 --- a/Detectors/Base/src/MatLayerCylSet.cxx +++ b/Detectors/Base/src/MatLayerCylSet.cxx @@ -8,7 +8,6 @@ // 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 MatLayerCylSet.cxx /// \brief Implementation of the wrapper for the set of cylindrical material layers @@ -16,7 +15,6 @@ #include "CommonConstants/MathConstants.h" #ifndef GPUCA_ALIGPUCODE // this part is unvisible on GPU version - #include "GPUCommonLogger.h" #include #include "CommonUtils/TreeStreamRedirector.h" @@ -177,6 +175,29 @@ void MatLayerCylSet::writeToFile(const std::string& outFName) outf.Close(); } +void MatLayerCylSet::initLayerVoxelLU() +{ + if (mInitializedLayerVoxelLU) { + LOG(info) << "Layer voxel already initialized; Aborting"; + return; + } + LOG(info) << "Initializing voxel layer lookup"; + // do some check if voxels are dimensioned correctly + if (LayerRMax < get()->mRMax) { + LOG(fatal) << "Cannot initialized layer voxel lookup due to dimension problem (fix constants in MatLayerCylSet.h)"; + } + for (int voxel = 0; voxel < NumVoxels; ++voxel) { + // check the 2 extremes of this voxel "covering" + const auto lowerR = voxel * VoxelRDelta; + const auto upperR = lowerR + VoxelRDelta; + const auto lowerSegment = searchSegment(lowerR * lowerR); + const auto upperSegment = searchSegment(upperR * upperR); + mLayerVoxelLU[2 * voxel] = lowerSegment; + mLayerVoxelLU[2 * voxel + 1] = upperSegment; + } + mInitializedLayerVoxelLU = true; +} + //________________________________________________________________________________ MatLayerCylSet* MatLayerCylSet::loadFromFile(const std::string& inpFName) { @@ -190,7 +211,8 @@ MatLayerCylSet* MatLayerCylSet::loadFromFile(const std::string& inpFName) LOG(error) << "Failed to load mat.LUT from " << inpFName; return nullptr; } - return rectifyPtrFromFile(mb); + auto rptr = rectifyPtrFromFile(mb); + return rptr; } //________________________________________________________________________________ @@ -200,6 +222,7 @@ MatLayerCylSet* MatLayerCylSet::rectifyPtrFromFile(MatLayerCylSet* ptr) if (ptr && !ptr->get()) { ptr->fixPointers(); } + ptr->initLayerVoxelLU(); return ptr; } @@ -267,10 +290,11 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa while (lrID >= lmin) { // go from outside to inside const auto& lr = getLayer(lrID); int nphiSlices = lr.getNPhiSlices(); - int nc = ray.crossLayer(lr); + int nc = ray.crossLayer(lr); // determines how many crossings this ray has with this tubular layer for (int ic = nc; ic--;) { float cross1, cross2; ray.getCrossParams(ic, cross1, cross2); // tmax,tmin of crossing the layer + auto phi0 = ray.getPhi(cross1), phi1 = ray.getPhi(cross2), dPhi = phi0 - phi1; auto phiID = lr.getPhiSliceID(phi0), phiIDLast = lr.getPhiSliceID(phi1); // account for eventual wrapping around 0 @@ -283,6 +307,7 @@ GPUd() MatBudget MatLayerCylSet::getMatBudget(float x0, float y0, float z0, floa phiID += nphiSlices; } } + int stepPhiID = phiID > phiIDLast ? -1 : 1; bool checkMorePhi = true; auto tStartPhi = cross1, tEndPhi = 0.f; @@ -390,21 +415,40 @@ GPUd() bool MatLayerCylSet::getLayersRange(const Ray& ray, short& lmin, short& l return false; } int lmxInt, lmnInt; - lmxInt = rmax2 < getRMax2() ? searchSegment(rmax2, 0) : get()->mNRIntervals - 2; - lmnInt = rmin2 >= getRMin2() ? searchSegment(rmin2, 0, lmxInt + 1) : 0; + if (!mInitializedLayerVoxelLU) { + lmxInt = rmax2 < getRMax2() ? searchSegment(rmax2, 0) : get()->mNRIntervals - 2; + lmnInt = rmin2 >= getRMin2() ? searchSegment(rmin2, 0, lmxInt + 1) : 0; + } else { + lmxInt = rmax2 < getRMax2() ? searchLayerFast(rmax2, 0) : get()->mNRIntervals - 2; + lmnInt = rmin2 >= getRMin2() ? searchLayerFast(rmin2, 0, lmxInt + 1) : 0; + } + const auto* interval2LrID = get()->mInterval2LrID; lmax = interval2LrID[lmxInt]; lmin = interval2LrID[lmnInt]; // make sure lmnInt and/or lmxInt are not in the gap if (lmax < 0) { - lmax = interval2LrID[--lmxInt]; // rmax2 is in the gap, take highest layer below rmax2 + lmax = interval2LrID[lmxInt - 1]; // rmax2 is in the gap, take highest layer below rmax2 } if (lmin < 0) { - lmin = interval2LrID[++lmnInt]; // rmin2 is in the gap, take lowest layer above rmin2 + lmin = interval2LrID[lmnInt + 1]; // rmin2 is in the gap, take lowest layer above rmin2 } return lmin <= lmax; // valid if both are not in the same gap } +GPUd() int MatLayerCylSet::searchLayerFast(float r2, int low, int high) const +{ + // we can avoid the sqrt .. at the cost of more memory in the lookup + const auto index = 2 * int(o2::gpu::CAMath::Sqrt(r2) * InvVoxelRDelta); + const auto layersfirst = mLayerVoxelLU[index]; + const auto layerslast = mLayerVoxelLU[index + 1]; + if (layersfirst != layerslast) { + // this means the voxel is undecided and we revert to search + return searchSegment(r2, layersfirst, layerslast + 1); + } + return layersfirst; +} + GPUd() int MatLayerCylSet::searchSegment(float val, int low, int high) const { ///< search segment val belongs to. The val MUST be within the boundaries @@ -424,6 +468,7 @@ GPUd() int MatLayerCylSet::searchSegment(float val, int low, int high) const } mid = (low + high) >> 1; } + return mid; } diff --git a/Detectors/Base/src/Stack.cxx b/Detectors/Base/src/Stack.cxx index b5873165a0e46..64daf2eccefc5 100644 --- a/Detectors/Base/src/Stack.cxx +++ b/Detectors/Base/src/Stack.cxx @@ -638,14 +638,17 @@ void Stack::Print(Option_t* option) const void Stack::addHit(int iDet) { + // translate detector ID to bit id + const auto bitID = o2::base::Detector::getDetId2HitBitIndex()[iDet]; + if (mIndexOfCurrentTrack < mNumberOfPrimaryParticles) { auto& part = mTracks->at(mIndexOfCurrentTrack); - part.setHit(iDet); + part.setHit(bitID); } else { Int_t iTrack = mTrackIDtoParticlesEntry[mIndexOfCurrentTrack]; auto& part = mParticles[iTrack]; - part.setHit(iDet); + part.setHit(bitID); } mCurrentParticle.SetBit(ParticleStatus::kHasHits, 1); mHitCounter++; @@ -654,7 +657,9 @@ void Stack::addHit(int iDet, Int_t iTrack) { mHitCounter++; auto& part = mParticles[iTrack]; - part.setHit(iDet); + // fetch the bit encoding for hits + const auto bitID = o2::base::Detector::getDetId2HitBitIndex()[iDet]; + part.setHit(bitID); mCurrentParticle.SetBit(ParticleStatus::kHasHits, 1); } diff --git a/Detectors/CPV/workflow/CMakeLists.txt b/Detectors/CPV/workflow/CMakeLists.txt index 04e4b075a7ec4..ba21452393b99 100644 --- a/Detectors/CPV/workflow/CMakeLists.txt +++ b/Detectors/CPV/workflow/CMakeLists.txt @@ -12,6 +12,8 @@ o2_add_library(CPVWorkflow SOURCES src/RecoWorkflow.cxx src/ReaderSpec.cxx + src/DigitReaderSpec.cxx + src/ClusterReaderSpec.cxx src/WriterSpec.cxx src/ClusterizerSpec.cxx src/DigitsPrinterSpec.cxx @@ -41,3 +43,13 @@ o2_add_executable(cluster-writer-workflow COMPONENT_NAME cpv SOURCES src/cluster-writer-workflow.cxx PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) + +o2_add_executable(digit-reader-workflow + COMPONENT_NAME cpv + SOURCES src/digit-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) + +o2_add_executable(cluster-reader-workflow + COMPONENT_NAME cpv + SOURCES src/cluster-reader-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::CPVWorkflow) diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h new file mode 100644 index 0000000000000..ee1c195b0aec5 --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/ClusterReaderSpec.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterReaderSpec.h + +#ifndef O2_CPV_CLUSTERREADER +#define O2_CPV_CLUSTERREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsCPV/Cluster.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2 +{ +namespace cpv +{ + +class ClusterReader : public o2::framework::Task +{ + public: + ClusterReader(bool useMC = true); + ~ClusterReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mClusters, *mClustersInp = &mClusters; + std::vector mTRs, *mTRsInp = &mTRs; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginCPV; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mClusterTreeName = "o2sim"; + std::string mClusterBranchName = "CPVCluster"; + std::string mTRBranchName = "CPVClusterTrigRec"; + std::string mClusterMCTruthBranchName = "CPVClusterTrueMC"; +}; + +/// create a processor spec +/// read CPV Cluster data from a root file +framework::DataProcessorSpec getCPVClusterReaderSpec(bool useMC = true); + +} // namespace cpv +} // namespace o2 + +#endif /* O2_CPV_CLUSTERREADER */ diff --git a/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h b/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h new file mode 100644 index 0000000000000..00fce64f89125 --- /dev/null +++ b/Detectors/CPV/workflow/include/CPVWorkflow/DigitReaderSpec.h @@ -0,0 +1,69 @@ +// 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 DigitReaderSpec.h + +#ifndef O2_CPV_DIGITREADER +#define O2_CPV_DIGITREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsCPV/Digit.h" +#include "DataFormatsCPV/TriggerRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +namespace o2 +{ +namespace cpv +{ + +class DigitReader : public o2::framework::Task +{ + public: + DigitReader(bool useMC = true); + ~DigitReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mDigits, *mDigitsInp = &mDigits; + std::vector mTRs, *mTRsInp = &mTRs; + // std::vector mMCTruth, *mMCTruthInp = &mMCTruth; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginCPV; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mDigitTreeName = "o2sim"; + std::string mDigitBranchName = "CPVDigit"; + std::string mTRBranchName = "CPVDigitTrigRecords"; + std::string mDigitMCTruthBranchName = "CPVDigitMCTruth"; +}; + +/// create a processor spec +/// read CPV Digit data from a root file +framework::DataProcessorSpec getCPVDigitReaderSpec(bool useMC = true); + +} // namespace cpv +} // namespace o2 + +#endif /* O2_CPV_DIGITREADER */ diff --git a/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx new file mode 100644 index 0000000000000..f9d82eb23bf68 --- /dev/null +++ b/Detectors/CPV/workflow/src/ClusterReaderSpec.cxx @@ -0,0 +1,100 @@ +// 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 ClusterReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/ClusterReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::cpv; + +namespace o2 +{ +namespace cpv +{ + +ClusterReader::ClusterReader(bool useMC) +{ + mUseMC = useMC; +} + +void ClusterReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("cpv-clusters-infile")); + connectTree(mInputFileName); +} + +void ClusterReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mClusters.size() << " Clusters in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "CLUSTERS", 0, Lifetime::Timeframe}, mClusters); + pc.outputs().snapshot(Output{mOrigin, "CLUSTERTRIGRECS", 0, Lifetime::Timeframe}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "CLUSTERTRUEMC", 0, Lifetime::Timeframe}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void ClusterReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mClusterTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mClusterBranchName.c_str(), &mClustersInp); + if (mUseMC) { + if (mTree->GetBranch(mClusterMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mClusterMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getCPVClusterReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("CPV", "CLUSTERS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("CPV", "CLUSTERTRIGRECS", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("CPV", "CLUSTERTRUEMC", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "cpv-cluster-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"cpv-clusters-infile", VariantType::String, "cpvclusters.root", {"Name of the input Cluster file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/DigitReaderSpec.cxx b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx new file mode 100644 index 0000000000000..ba74cb88b10d2 --- /dev/null +++ b/Detectors/CPV/workflow/src/DigitReaderSpec.cxx @@ -0,0 +1,100 @@ +// 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 DigitReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CPVWorkflow/DigitReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::cpv; + +namespace o2 +{ +namespace cpv +{ + +DigitReader::DigitReader(bool useMC) +{ + mUseMC = useMC; +} + +void DigitReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("cpv-digits-infile")); + connectTree(mInputFileName); +} + +void DigitReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mDigits.size() << " Digits in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0, Lifetime::Timeframe}, mDigits); + pc.outputs().snapshot(Output{mOrigin, "DIGITTRIGREC", 0, Lifetime::Timeframe}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "DIGITSMCTR", 0, Lifetime::Timeframe}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void DigitReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mDigitTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mDigitBranchName.c_str(), &mDigitsInp); + if (mUseMC) { + if (mTree->GetBranch(mDigitMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getCPVDigitReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("CPV", "DIGITS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("CPV", "DIGITTRIGREC", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("CPV", "DIGITSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "cpv-digit-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"cpv-digits-infile", VariantType::String, "cpvdigits.root", {"Name of the input Digit file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace cpv +} // namespace o2 diff --git a/Detectors/CPV/workflow/src/RecoWorkflow.cxx b/Detectors/CPV/workflow/src/RecoWorkflow.cxx index da4ffee8aa6e8..66b5f3dbe588a 100644 --- a/Detectors/CPV/workflow/src/RecoWorkflow.cxx +++ b/Detectors/CPV/workflow/src/RecoWorkflow.cxx @@ -24,6 +24,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsCPV/TriggerRecord.h" #include "CPVWorkflow/RecoWorkflow.h" +#include "CPVWorkflow/DigitReaderSpec.h" #include "CPVWorkflow/ClusterizerSpec.h" #include "CPVWorkflow/ReaderSpec.h" #include "CPVWorkflow/WriterSpec.h" @@ -107,7 +108,8 @@ o2::framework::WorkflowSpec getWorkflow(bool disableRootInp, // Digits to .... if (inputType == InputType::Digits) { if (!disableRootInp) { - specs.emplace_back(o2::cpv::getDigitsReaderSpec(propagateMC)); + specs.emplace_back(o2::cpv::getCPVDigitReaderSpec(propagateMC)); + // specs.emplace_back(o2::cpv::getDigitsReaderSpec(propagateMC)); } if (isEnabled(OutputType::Clusters)) { diff --git a/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx b/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx new file mode 100644 index 0000000000000..33f389f15f9da --- /dev/null +++ b/Detectors/CPV/workflow/src/cluster-reader-workflow.cxx @@ -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. + +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "with-mc", + o2::framework::VariantType::Bool, + false, + {"propagate MC labels"}}); + workflowOptions.push_back( + ConfigParamSpec{ + "configKeyValues", + VariantType::String, + "", + {"Semicolon separated key=value strings"}}); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); +} + +#include "Framework/runDataProcessing.h" +#include "CPVWorkflow/ClusterReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cc) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(cc.options().get("configKeyValues")); + auto withMC = cc.options().get("with-mc"); + + specs.emplace_back(o2::cpv::getCPVClusterReaderSpec(withMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(cc, specs); + + return specs; +} diff --git a/Detectors/CPV/workflow/src/digit-reader-workflow.cxx b/Detectors/CPV/workflow/src/digit-reader-workflow.cxx new file mode 100644 index 0000000000000..dc068573b1218 --- /dev/null +++ b/Detectors/CPV/workflow/src/digit-reader-workflow.cxx @@ -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. + +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "Framework/CallbacksPolicy.h" + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& workflowOptions) +{ + workflowOptions.push_back( + ConfigParamSpec{ + "with-mc", + o2::framework::VariantType::Bool, + false, + {"propagate MC labels"}}); + workflowOptions.push_back( + ConfigParamSpec{ + "configKeyValues", + VariantType::String, + "", + {"Semicolon separated key=value strings"}}); + o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); +} + +#include "Framework/runDataProcessing.h" +#include "CPVWorkflow/DigitReaderSpec.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cc) +{ + WorkflowSpec specs; + o2::conf::ConfigurableParam::updateFromString(cc.options().get("configKeyValues")); + auto withMC = cc.options().get("with-mc"); + + specs.emplace_back(o2::cpv::getCPVDigitReaderSpec(withMC)); + + // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit + o2::raw::HBFUtilsInitializer hbfIni(cc, specs); + + return specs; +} diff --git a/Detectors/CTF/test/test_ctf_io_tpc.cxx b/Detectors/CTF/test/test_ctf_io_tpc.cxx index 210ab0af105bd..cb087e6f2a38f 100644 --- a/Detectors/CTF/test/test_ctf_io_tpc.cxx +++ b/Detectors/CTF/test/test_ctf_io_tpc.cxx @@ -20,6 +20,7 @@ #include #include #include "DataFormatsTPC/CompressedClusters.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "DataFormatsTPC/CTF.h" #include "CommonUtils/NameConf.h" #include "TPCReconstruction/CTFCoder.h" @@ -37,12 +38,21 @@ inline std::vector CombineColumns(true, false); BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions) ^ boost_data::make(CombineColumns), ansVersion, combineColumns) { + std::vector triggers, triggersR; CompressedClusters c; c.nAttachedClusters = 99; c.nUnattachedClusters = 88; c.nAttachedClustersReduced = 77; c.nTracks = 66; + triggers.emplace_back(); + triggers.back().orbit = 1234; + triggers.back().triggerWord.triggerEntries[0] = (10 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::PhT & 0x7) << 12) | 0x8000; + triggers.back().triggerWord.triggerEntries[1] = (30 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::PP & 0x7) << 12) | 0x8000; + triggers.emplace_back(); + triggers.back().orbit = 1236; + triggers.back().triggerWord.triggerEntries[0] = (40 & 0xFFF) | ((o2::tpc::TriggerWordDLBZS::TriggerType::Cal & 0x7) << 12) | 0x8000; + std::vector bVec; CompressedClustersFlat* ccFlat = nullptr; size_t sizeCFlatBody = CTFCoder::alignSize(ccFlat); @@ -96,10 +106,43 @@ BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions) ^ boost_data::make(C sw.Start(); std::vector vecIO; { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder); + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder); coder.setCombineColumns(combineColumns); coder.setANSVersion(ansVersion); - coder.encode(vecIO, c, c); // compress + // prepare trigger info + o2::tpc::detail::TriggerInfo trigComp; + for (const auto& trig : triggers) { + for (int it = 0; it < o2::tpc::TriggerWordDLBZS::MaxTriggerEntries; it++) { + if (trig.triggerWord.isValid(it)) { + trigComp.deltaOrbit.push_back(trig.orbit); + trigComp.deltaBC.push_back(trig.triggerWord.getTriggerBC(it)); + trigComp.triggerType.push_back(trig.triggerWord.getTriggerType(it)); + } else { + break; + } + } + } + // transform trigger info to differential form + uint32_t prevOrbit = -1; + uint16_t prevBC = -1; + if (trigComp.triggerType.size()) { + prevOrbit = trigComp.firstOrbit = trigComp.deltaOrbit[0]; + prevBC = trigComp.deltaBC[0]; + trigComp.deltaOrbit[0] = 0; + for (size_t it = 1; it < trigComp.triggerType.size(); it++) { + if (trigComp.deltaOrbit[it] == prevOrbit) { + auto bc = trigComp.deltaBC[it]; + trigComp.deltaBC[it] -= prevBC; + prevBC = bc; + trigComp.deltaOrbit[it] = 0; + } else { + auto orb = trigComp.deltaOrbit[it]; + trigComp.deltaOrbit[it] -= prevOrbit; + prevOrbit = orb; + } + } + } + coder.encode(vecIO, c, c, trigComp); // compress } sw.Stop(); LOG(info) << "Compressed in " << sw.CpuTime() << " s"; @@ -135,7 +178,7 @@ BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions) ^ boost_data::make(C { CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder); coder.setCombineColumns(true); - coder.decode(ctfImage, vecIn); // decompress + coder.decode(ctfImage, vecIn, triggersR); // decompress } sw.Stop(); LOG(info) << "Decompressed in " << sw.CpuTime() << " s"; @@ -153,4 +196,6 @@ BOOST_DATA_TEST_CASE(CTFTest, boost_data::make(ANSVersions) ^ boost_data::make(C BOOST_CHECK(countOrig->solenoidBz == countDeco->solenoidBz); BOOST_CHECK(countOrig->maxTimeBin == countDeco->maxTimeBin); BOOST_CHECK(memcmp(vecIn.data() + sizeof(o2::tpc::CompressedClustersCounters), bVec.data() + sizeof(o2::tpc::CompressedClustersCounters), bVec.size() - sizeof(o2::tpc::CompressedClustersCounters)) == 0); + BOOST_CHECK(triggers.size() == triggersR.size()); + BOOST_CHECK(memcmp(triggers.data(), triggersR.data(), triggers.size() * sizeof(o2::tpc::TriggerInfoDLBZS)) == 0); } diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index e105dabc3b58c..681547f9a814c 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -100,6 +100,8 @@ class CTFReaderSpec : public o2::framework::Task int mNFailedFiles = 0; int mFilesRead = 0; int mTFLength = 128; + int mNWaits = 0; + long mTotalWaitTime = 0; long mLastSendTime = 0L; long mCurrTreeEntry = 0L; long mImposeRunStartMS = 0L; @@ -127,8 +129,8 @@ void CTFReaderSpec::stopReader() return; } LOGP(info, "CTFReader stops processing, {} files read, {} files failed", mFilesRead - mNFailedFiles, mNFailedFiles); - LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs in {} loops", - mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mFileFetcher->getNLoops()); + LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs in {} loops, spent {:.2} s in {} data waiting states", + mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); mRunning = false; mFileFetcher->stop(); mFileFetcher.reset(); @@ -195,10 +197,8 @@ void CTFReaderSpec::run(ProcessingContext& pc) mInput.tfRateLimit = std::stoi(pc.services().get().device()->fConfig->GetValue("timeframes-rate-limit")); } std::string tfFileName; - if (mCTFCounter >= mInput.maxTFs || (!mInput.ctfIDs.empty() && mSelIDEntry >= mInput.ctfIDs.size())) { // done - LOG(info) << "All CTFs from selected range were injected, stopping"; - mRunning = false; - } + bool waitAcknowledged = false; + long startWait = 0; while (mRunning) { if (mCTFTree) { // there is a tree open with multiple CTF @@ -222,12 +222,32 @@ void CTFReaderSpec::run(ProcessingContext& pc) mRunning = false; break; } + if (!waitAcknowledged) { + startWait = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + waitAcknowledged = true; + } pc.services().get().waitFor(5); continue; } + if (waitAcknowledged) { + long waitTime = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - startWait; + mTotalWaitTime += waitTime; + if (++mNWaits > 1) { + LOGP(warn, "Resuming reading after waiting for data {:.2} s (accumulated {:.2} s delay in {} waits)", 1e-6 * waitTime, 1e-6 * mTotalWaitTime, mNWaits); + } + waitAcknowledged = false; + } LOG(info) << "Reading CTF input " << ' ' << tfFileName; openCTFFile(tfFileName); } + + if (mCTFCounter >= mInput.maxTFs || (!mInput.ctfIDs.empty() && mSelIDEntry >= mInput.ctfIDs.size())) { // done + LOGP(info, "All CTFs from selected range were injected, stopping"); + mRunning = false; + } else if (mRunning && !mCTFTree && mFileFetcher->getNextFileInQueue().empty() && !mFileFetcher->isRunning()) { // previous tree was done, can we read more? + mRunning = false; + } + if (!mRunning) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); diff --git a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx index aecb34abb08d9..44d726dd7e4f4 100644 --- a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx @@ -494,7 +494,7 @@ void CTFWriterSpec::run(ProcessingContext& pc) szCTF += processDet(pc, DetID::FDD, header, mCTFTreeOut.get()); szCTF += processDet(pc, DetID::CTP, header, mCTFTreeOut.get()); if (mReportInterval > 0 && (mTimingInfo.tfCounter % mReportInterval) == 0) { - LOGP(important, "CTF {} size report:{}", mTimingInfo.tfCounter, mSizeReport); + LOGP(important, "CTF {} size report:{} - Total:{}", mTimingInfo.tfCounter, mSizeReport, fmt::group_digits(szCTF)); } mTimer.Stop(); diff --git a/Detectors/CTP/macro/GetScalers.C b/Detectors/CTP/macro/GetScalers.C index 7cc13c03356eb..05fce0b657f5a 100644 --- a/Detectors/CTP/macro/GetScalers.C +++ b/Detectors/CTP/macro/GetScalers.C @@ -42,15 +42,22 @@ void GetScalers(std::string srun, long time, std::string ccdbHost = "http://ccdb // return; scl = mng.getScalersFromCCDB(time, srun, ok); if (ok == 1) { + scl.printStream(std::cout); scl.convertRawToO2(); - scl.printO2(std::cout); - scl.printFromZero(std::cout); - scl.printIntegrals(); - scl.printRates(); - // std::vector clsses; - // clsses = ctpcfg.getTriggerClassList(); - // std::cout << clsses.size() << std::endl; - // for(auto const& i : clsses) std::cout << i << std::endl; + // scl.printO2(std::cout); + // scl.printFromZero(std::cout); + // scl.printIntegrals(); + // scl.printRates(); + std::cout << "TVX,TSC,TCE,ZNC:" << std::endl; + scl.printInputRateAndIntegral(3); + scl.printInputRateAndIntegral(4); + scl.printInputRateAndIntegral(5); + scl.printInputRateAndIntegral(26); + std::cout << " TVX,TVX&TCE,TVX&TSC,TVX&VCH:" << std::endl; + scl.printClassBRateAndIntegral(3); + scl.printClassBRateAndIntegral(4); + scl.printClassBRateAndIntegral(5); + scl.printClassBRateAndIntegral(6); } else { std::cout << "Can not find run, please, check parameters" << std::endl; } diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index deef6149e0d0e..6ffb3575207e5 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -145,8 +145,12 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l std::map digitsMap; o2::InteractionRecord ir(header.firstBC, header.firstOrbit); lumi.nHBFCounted = header.lumiNHBFs; + lumi.nHBFCountedFV0 = header.lumiNHBFsFV0 ? header.lumiNHBFsFV0 : header.lumiNHBFs; lumi.counts = header.lumiCounts; + lumi.countsFV0 = header.lumiCountsFV0; lumi.orbit = header.lumiOrbit; + lumi.inp1 = int(header.inp1); + lumi.inp2 = int(header.inp2); auto itInp = bytesInput.begin(); auto itCls = bytesClass.begin(); bool checkIROK = (mBCShift == 0); // need to check if CTP offset correction does not make the local time negative ? diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h index 89859e8cdbb3a..34228e3acc55f 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFHelper.h @@ -38,8 +38,8 @@ class CTFHelper CTFHeader createHeader(const LumiInfo& lumi) { CTFHeader h{o2::detectors::DetID::CTP, 0, 1, 0, // dummy timestamp, version 1.0 - lumi.counts, lumi.countsFV0, lumi.nHBFCounted, lumi.orbit, - uint32_t(mData.size()), 0, 0}; + lumi.counts, lumi.countsFV0, lumi.nHBFCounted, lumi.nHBFCountedFV0, lumi.orbit, + uint32_t(mData.size()), 0, 0, uint16_t(lumi.inp1), uint16_t(lumi.inp2)}; if (mData.size()) { h.firstOrbit = mData[0].intRecord.orbit; h.firstBC = mData[0].intRecord.bc; diff --git a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx index 0e71d05af5815..8f3da5f439f80 100644 --- a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx @@ -44,7 +44,7 @@ void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matche void EntropyDecoderSpec::init(o2::framework::InitContext& ic) { mCTFCoder.init(ic); - bool decodeinps = ic.options().get("ctpinputs-decoding-ctf"); + bool decodeinps = !ic.options().get("ignore-ctpinputs-decoding-ctf"); mCTFCoder.setDecodeInps(decodeinps); LOG(info) << "Decode inputs:" << decodeinps; } @@ -95,7 +95,7 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) outputs, AlgorithmSpec{adaptFromTask(verbosity)}, Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ctpinputs-decoding-ctf", VariantType::Bool, false, {"Inputs alignment: true - CTF decoder - has to be compatible with reco: allowed options: 10,01,00"}}, + {"ignore-ctpinputs-decoding-ctf", VariantType::Bool, false, {"Inputs alignment: false - CTF decoder - has to be compatible with reco: allowed options: 10,01,00"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } diff --git a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx index 3e51226b687c4..7ad4f821c12a9 100644 --- a/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyEncoderSpec.cxx @@ -50,9 +50,19 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mTimer.Start(false); mCTFCoder.updateTimeDependentParams(pc, true); auto digits = pc.inputs().get>("digits"); + static LumiInfo lumiPrev; + const int maxDumRep = 5; + int dumRep = 0; LumiInfo lumi{}; if (!mNoLumi) { - lumi = pc.inputs().get("CTPLumi"); + if (pc.inputs().get>("CTPLumi").size() == sizeof(LumiInfo)) { + lumiPrev = lumi = pc.inputs().get("CTPLumi"); + } else { + if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { + LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); + } + lumi = lumiPrev; + } } auto& buffer = pc.outputs().make>(Output{"CTP", "CTFDATA", 0, Lifetime::Timeframe}); auto iosize = mCTFCoder.encode(buffer, digits, lumi); diff --git a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx index fef2bd9e4deab..3634df6d30bda 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-proxy.cxx @@ -29,6 +29,7 @@ #include "Framework/WorkflowSpec.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" +#include "Framework/RawDeviceService.h" #include "Framework/ControlService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" @@ -51,7 +52,7 @@ InjectorFunction dcs2dpl(std::string& ccdbhost) auto runMgr = std::make_shared(); runMgr->setCCDBHost(ccdbhost); runMgr->init(); - return [runMgr](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + return [runMgr](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) -> bool { // FIXME: Why isn't this function using the timeslice index? // make sure just 2 messages received // if (parts.Size() != 2) { @@ -63,6 +64,7 @@ InjectorFunction dcs2dpl(std::string& ccdbhost) std::string messageData{static_cast(parts.At(1)->GetData()), parts.At(1)->GetSize()}; LOG(info) << "received message " << messageHeader << " of size " << dataSize << " # parts:" << parts.Size(); // << " Payload:" << messageData; runMgr->processMessage(messageHeader, messageData); + return true; }; } diff --git a/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx b/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx index 28f5a416ef8ad..2372ba07a28f7 100644 --- a/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx +++ b/Detectors/CTP/workflowScalers/src/ctp-qc-proxy.cxx @@ -28,6 +28,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ControlService.h" +#include "Framework/RawDeviceService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" #include "Framework/ConfigParamSpec.h" @@ -45,7 +46,8 @@ using DetID = o2::detectors::DetID; InjectorFunction dcs2dpl() // InjectorFunction dcs2dpl() { - return [](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { + return [](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) -> bool { + auto *device = services.get().device(); std::string messageHeader{static_cast(parts.At(0)->GetData()), parts.At(0)->GetSize()}; size_t dataSize = parts.At(1)->GetSize(); std::string messageData{static_cast(parts.At(1)->GetData()), parts.At(1)->GetSize()}; @@ -55,7 +57,7 @@ InjectorFunction dcs2dpl() auto channel = channelRetriever(outsp, newTimesliceId); if (channel.empty()) { LOG(error) << "No output channel found for OutputSpec " << outsp; - return; + return false; } hdrF.tfCounter = newTimesliceId; // this also hdrF.payloadSerializationMethod = o2::header::gSerializationMethodNone; @@ -64,7 +66,7 @@ InjectorFunction dcs2dpl() hdrF.payloadSize = parts.At(0)->GetSize() + parts.At(1)->GetSize() + 1; hdrF.firstTForbit = 0; // this should be irrelevant for Counters ? Orbit is in payload - auto fmqFactory = device.GetChannel(channel).Transport(); + auto fmqFactory = device->GetChannel(channel).Transport(); o2::header::Stack headerStackF{hdrF, DataProcessingHeader{newTimesliceId, 1}}; auto hdMessageF = fmqFactory->CreateMessage(headerStackF.size(), fair::mq::Alignment{64}); @@ -87,8 +89,9 @@ InjectorFunction dcs2dpl() fair::mq::Parts outParts; outParts.AddPart(std::move(hdMessageF)); outParts.AddPart(std::move(plMessageF)); - sendOnChannel(device, outParts, channel, newTimesliceId); + sendOnChannel(*device, outParts, channel, newTimesliceId); LOG(info) << "Sent CTP counters DPL message" << std::flush; + return true; }; } diff --git a/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h b/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h index dbdd329e1ca3b..3b29cd3f00adb 100644 --- a/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h +++ b/Detectors/Calibration/include/DetectorsCalibration/IntegratedClusterCalibrator.h @@ -20,6 +20,8 @@ #include "DetectorsCalibration/TimeSlotCalibration.h" #include "DetectorsCalibration/TimeSlot.h" +class TTree; + namespace o2 { @@ -38,9 +40,11 @@ namespace tof struct ITOFC { std::vector mITOFCNCl; ///< integrated 1D TOF cluster currents std::vector mITOFCQ; ///< integrated 1D TOF qTot currents + long mTimeMS{}; ///< start time in ms bool areSameSize() const { return sameSize(mITOFCNCl, mITOFCQ); } ///< check if stored currents have same number of entries bool isEmpty() const { return mITOFCNCl.empty(); } ///< check if values are empty size_t getEntries() const { return mITOFCNCl.size(); } ///< \return returns number of values stored + void setStartTime(long timeMS) { mTimeMS = timeMS; } /// acummulate integrated currents at given index /// \param posIndex index where data will be copied to @@ -66,7 +70,7 @@ struct ITOFC { mITOFCQ.resize(nTotal); } - ClassDefNV(ITOFC, 1); + ClassDefNV(ITOFC, 2); }; } // end namespace tof @@ -81,6 +85,7 @@ struct ITPCC { std::vector mIQTotC; ///< integrated 1D-currents for QTot A-side std::vector mINClA; ///< integrated 1D-currents for NCl A-side std::vector mINClC; ///< integrated 1D-currents for NCl A-side + long mTimeMS{}; ///< start time in ms float compression(float value, const int nBits) const { @@ -104,6 +109,7 @@ struct ITPCC { bool areSameSize() const { return sameSize(mIQMaxA, mIQMaxC, mIQTotA, mIQTotC, mINClA, mINClC); } ///< check if stored currents have same number of entries bool isEmpty() const { return mIQMaxA.empty(); } ///< check if values are empty size_t getEntries() const { return mIQMaxA.size(); } ///< \return returns number of values stored + void setStartTime(long timeMS) { mTimeMS = timeMS; } /// acummulate integrated currents at given index /// \param posIndex index where data will be copied to @@ -163,7 +169,339 @@ struct ITPCC { std::transform(mINClC.begin(), mINClC.end(), mINClC.begin(), [factor](const float val) { return val * factor; }); } - ClassDefNV(ITPCC, 1); + ClassDefNV(ITPCC, 2); +}; + +/// struct containing time series values +struct TimeSeries { + std::vector mDCAr_A_Median; ///< integrated 1D DCAr for A-side median in phi/tgl slices + std::vector mDCAr_C_Median; ///< integrated 1D DCAr for C-side weighted mean in phi/tgl slices + std::vector mDCAr_A_WeightedMean; ///< integrated 1D DCAr for A-side weighted mean in phi/tgl slices + std::vector mDCAr_C_WeightedMean; ///< integrated 1D DCAr for C-side median in phi/tgl slices + std::vector mDCAr_A_RMS; ///< integrated 1D DCAr for A-side RMS in phi/tgl slices + std::vector mDCAr_C_RMS; ///< integrated 1D DCAr for C-side RMS in phi/tgl slices + std::vector mDCAr_A_NTracks; ///< number of tracks used to calculate the DCAs + std::vector mDCAr_C_NTracks; ///< number of tracks used to calculate the DCAs + std::vector mDCAz_A_Median; ///< integrated 1D DCAz for A-side median in phi/tgl slices + std::vector mDCAz_C_Median; ///< integrated 1D DCAz for C-side median in phi/tgl slices + std::vector mDCAz_A_WeightedMean; ///< integrated 1D DCAz for A-side weighted mean in phi/tgl slices + std::vector mDCAz_C_WeightedMean; ///< integrated 1D DCAz for C-side weighted mean in phi/tgl slices + std::vector mDCAz_A_RMS; ///< integrated 1D DCAz for A-side RMS in phi/tgl slices + std::vector mDCAz_C_RMS; ///< integrated 1D DCAz for C-side RMS in phi/tgl slices + std::vector mDCAz_A_NTracks; ///< number of tracks used to calculate the DCAs + std::vector mDCAz_C_NTracks; ///< number of tracks used to calculate the DCAs + std::vector mMIPdEdxRatioA; ///< ratio of MIP/dEdx + std::vector mMIPdEdxRatioC; ///< ratio of MIP/dEdx + std::vector mTPCChi2A; ///< Chi2 of TPC tracks + std::vector mTPCChi2C; ///< Chi2 of TPC tracks + std::vector mTPCNClA; ///< number of TPC cluster + std::vector mTPCNClC; ///< number of TPC cluster + + unsigned char mNBinsPhi{}; ///< number of tgl bins + unsigned char mNBinsTgl{}; ///< number of phi bins + unsigned char mNBinsqPt{}; ///< number of qPt bins + long mTimeMS{}; ///< start time in ms + + /// dump object to tree + /// \param outFileName name of the output file + /// \param nHBFPerTF number of orbits per TF + void dumpToTree(TTree& tree, const char* prefix = "", const int nHBFPerTF = 32) const; + void setStartTime(long timeMS) { mTimeMS = timeMS; } + bool areSameSize() const { return sameSize(mDCAr_A_WeightedMean, mDCAr_C_WeightedMean, mDCAz_A_WeightedMean, mDCAz_C_WeightedMean, mDCAr_A_Median, mDCAr_C_Median, mDCAr_A_RMS, mDCAr_C_RMS, mDCAz_A_Median, mDCAz_C_Median, mDCAz_A_RMS, mDCAz_C_RMS, mDCAz_C_NTracks, mDCAr_A_NTracks, mDCAz_A_NTracks, mDCAz_C_NTracks); } ///< check if stored currents have same number of entries + bool isEmpty() const { return mDCAr_A_Median.empty(); } ///< check if values are empty + + size_t getEntries() const { return mDCAr_A_Median.size(); } ///< \return returns number of values stored + /// \return returns total number of bins + int getNBins() const { return mNBinsTgl + mNBinsPhi + mNBinsqPt + 1; } + + /// \return returns index for given phi bin + int getIndexPhi(const int iPhi, int slice = 0) const { return iPhi + slice * getNBins(); } + + /// \return returns index for given tgl bin + int getIndexTgl(const int iTgl, int slice = 0) const { return mNBinsPhi + iTgl + slice * getNBins(); } + + /// \return returns index for given qPt bin + int getIndexqPt(const int iqPt, int slice = 0) const { return mNBinsPhi + mNBinsTgl + iqPt + slice * getNBins(); } + + /// \return returns index for integrated over all bins + int getIndexInt(int slice = 0) const { return getNBins() - 1 + slice * getNBins(); } + + /// acummulate integrated currents at given index + /// \param posIndex index where data will be copied to + /// \param data integrated currents which will be copied + void fill(const unsigned int posIndex, const TimeSeries& data) + { + mNBinsPhi = data.mNBinsPhi; + mNBinsTgl = data.mNBinsTgl; + mNBinsqPt = data.mNBinsqPt; + fill(data.mDCAr_A_Median, mDCAr_A_Median, posIndex); + fill(data.mDCAr_C_Median, mDCAr_C_Median, posIndex); + fill(data.mDCAr_A_RMS, mDCAr_A_RMS, posIndex); + fill(data.mDCAr_C_RMS, mDCAr_C_RMS, posIndex); + fill(data.mDCAz_A_Median, mDCAz_A_Median, posIndex); + fill(data.mDCAz_C_Median, mDCAz_C_Median, posIndex); + fill(data.mDCAz_A_RMS, mDCAz_A_RMS, posIndex); + fill(data.mDCAz_C_RMS, mDCAz_C_RMS, posIndex); + fill(data.mDCAr_C_NTracks, mDCAr_C_NTracks, posIndex); + fill(data.mDCAr_A_NTracks, mDCAr_A_NTracks, posIndex); + fill(data.mDCAz_A_NTracks, mDCAz_A_NTracks, posIndex); + fill(data.mDCAz_C_NTracks, mDCAz_C_NTracks, posIndex); + fill(data.mDCAr_A_WeightedMean, mDCAr_A_WeightedMean, posIndex); + fill(data.mDCAr_C_WeightedMean, mDCAr_C_WeightedMean, posIndex); + fill(data.mDCAz_A_WeightedMean, mDCAz_A_WeightedMean, posIndex); + fill(data.mDCAz_C_WeightedMean, mDCAz_C_WeightedMean, posIndex); + fill(data.mMIPdEdxRatioA, mMIPdEdxRatioA, posIndex); + fill(data.mMIPdEdxRatioC, mMIPdEdxRatioC, posIndex); + fill(data.mTPCChi2A, mTPCChi2A, posIndex); + fill(data.mTPCChi2C, mTPCChi2C, posIndex); + fill(data.mTPCNClA, mTPCNClA, posIndex); + fill(data.mTPCNClC, mTPCNClC, posIndex); + } + + void fill(const std::vector& vecFrom, std::vector& vecTo, const unsigned int posIndex) { std::copy(vecFrom.begin(), vecFrom.end(), vecTo.begin() + posIndex); } + + /// \param nDummyValues number of empty values which are inserted at the beginning of the accumulated integrated currents + void insert(const unsigned int nDummyValues) + { + std::vector vecTmp(nDummyValues, 0); + insert(mDCAr_A_Median, vecTmp); + insert(mDCAr_C_Median, vecTmp); + insert(mDCAr_A_RMS, vecTmp); + insert(mDCAr_C_RMS, vecTmp); + insert(mDCAz_A_Median, vecTmp); + insert(mDCAz_C_Median, vecTmp); + insert(mDCAz_A_RMS, vecTmp); + insert(mDCAz_C_RMS, vecTmp); + insert(mDCAr_C_NTracks, vecTmp); + insert(mDCAr_A_NTracks, vecTmp); + insert(mDCAz_A_NTracks, vecTmp); + insert(mDCAz_C_NTracks, vecTmp); + insert(mDCAr_A_WeightedMean, vecTmp); + insert(mDCAr_C_WeightedMean, vecTmp); + insert(mDCAz_A_WeightedMean, vecTmp); + insert(mDCAz_C_WeightedMean, vecTmp); + insert(mMIPdEdxRatioA, vecTmp); + insert(mMIPdEdxRatioC, vecTmp); + insert(mTPCChi2A, vecTmp); + insert(mTPCChi2C, vecTmp); + insert(mTPCNClA, vecTmp); + insert(mTPCNClC, vecTmp); + } + + void insert(std::vector& vec, const std::vector& vecTmp) { vec.insert(vec.begin(), vecTmp.begin(), vecTmp.end()); } + + /// resize buffer for accumulated currents + void resize(const unsigned int nTotal) + { + mDCAr_A_Median.resize(nTotal); + mDCAr_C_Median.resize(nTotal); + mDCAr_A_RMS.resize(nTotal); + mDCAr_C_RMS.resize(nTotal); + mDCAz_A_Median.resize(nTotal); + mDCAz_C_Median.resize(nTotal); + mDCAz_A_RMS.resize(nTotal); + mDCAz_C_RMS.resize(nTotal); + mDCAr_C_NTracks.resize(nTotal); + mDCAr_A_NTracks.resize(nTotal); + mDCAz_A_NTracks.resize(nTotal); + mDCAz_C_NTracks.resize(nTotal); + mDCAr_A_WeightedMean.resize(nTotal); + mDCAr_C_WeightedMean.resize(nTotal); + mDCAz_A_WeightedMean.resize(nTotal); + mDCAz_C_WeightedMean.resize(nTotal); + mMIPdEdxRatioA.resize(nTotal); + mMIPdEdxRatioC.resize(nTotal); + mTPCChi2A.resize(nTotal); + mTPCChi2C.resize(nTotal); + mTPCNClA.resize(nTotal); + mTPCNClC.resize(nTotal); + } + + ClassDefNV(TimeSeries, 1); +}; + +struct ITSTPC_Matching { + std::vector mITSTPC_A_MatchEff; ///< matching efficiency of ITS-TPC tracks A-side + std::vector mITSTPC_C_MatchEff; ///< matching efficiency of ITS-TPC tracks C-side + std::vector mITSTPC_A_Chi2Match; ///< ITS-TPC chi2 A-side + std::vector mITSTPC_C_Chi2Match; ///< ITS-TPC chi2 C-side + + void dumpToTree(TTree& tree, const char* prefix, const int nHBFPerTF, const TimeSeries& timeSeriesRef) const; + + /// acummulate integrated currents at given index + /// \param posIndex index where data will be copied to + /// \param data integrated currents which will be copied + void fill(const unsigned int posIndex, const ITSTPC_Matching& data) + { + std::copy(data.mITSTPC_A_MatchEff.begin(), data.mITSTPC_A_MatchEff.end(), mITSTPC_A_MatchEff.begin() + posIndex); + std::copy(data.mITSTPC_C_MatchEff.begin(), data.mITSTPC_C_MatchEff.end(), mITSTPC_C_MatchEff.begin() + posIndex); + std::copy(data.mITSTPC_A_Chi2Match.begin(), data.mITSTPC_A_Chi2Match.end(), mITSTPC_A_Chi2Match.begin() + posIndex); + std::copy(data.mITSTPC_C_Chi2Match.begin(), data.mITSTPC_C_Chi2Match.end(), mITSTPC_C_Chi2Match.begin() + posIndex); + } + + /// \param nDummyValues number of empty values which are inserted at the beginning of the accumulated integrated currents + void insert(const unsigned int nDummyValues) + { + std::vector vecTmp(nDummyValues, 0); + mITSTPC_A_MatchEff.insert(mITSTPC_A_MatchEff.begin(), vecTmp.begin(), vecTmp.end()); + mITSTPC_C_MatchEff.insert(mITSTPC_C_MatchEff.begin(), vecTmp.begin(), vecTmp.end()); + mITSTPC_A_Chi2Match.insert(mITSTPC_A_Chi2Match.begin(), vecTmp.begin(), vecTmp.end()); + mITSTPC_C_Chi2Match.insert(mITSTPC_C_Chi2Match.begin(), vecTmp.begin(), vecTmp.end()); + } + + /// resize buffer for accumulated currents + void resize(const unsigned int nTotal) + { + mITSTPC_A_MatchEff.resize(nTotal); + mITSTPC_C_MatchEff.resize(nTotal); + mITSTPC_A_Chi2Match.resize(nTotal); + mITSTPC_C_Chi2Match.resize(nTotal); + } + + ClassDefNV(ITSTPC_Matching, 1); +}; + +struct TimeSeriesITSTPC { + TimeSeries mTSTPC; ///< TPC standalone DCAs + TimeSeries mTSITSTPC; ///< ITS-TPC standalone DCAs + ITSTPC_Matching mITSTPCAll; ///< ITS-TPC matching efficiency for ITS standalone + afterburner + ITSTPC_Matching mITSTPCStandalone; ///< ITS-TPC matching efficiency for ITS standalone + ITSTPC_Matching mITSTPCAfterburner; ///< ITS-TPC matchin efficiency fir ITS afterburner + std::vector nPrimVertices; ///< number of primary vertices + std::vector nVertexContributors_Median; ///< number of primary vertices + std::vector nVertexContributors_RMS; ///< number of primary vertices + std::vector vertexX_Median; ///< vertex x position + std::vector vertexY_Median; ///< vertex y position + std::vector vertexZ_Median; ///< vertex z position + std::vector vertexX_RMS; ///< vertex x RMS + std::vector vertexY_RMS; ///< vertex y RMS + std::vector vertexZ_RMS; ///< vertex z RMS + std::vector mDCAr_comb_A_Median; ///< DCAr for ITS-TPC track - A-side + std::vector mDCAz_comb_A_Median; ///< DCAz for ITS-TPC track - A-side + std::vector mDCAr_comb_A_RMS; ///< DCAr RMS for ITS-TPC track - A-side + std::vector mDCAz_comb_A_RMS; ///< DCAz RMS for ITS-TPC track - A-side + std::vector mDCAr_comb_C_Median; ///< DCAr for ITS-TPC track - C-side + std::vector mDCAz_comb_C_Median; ///< DCAz for ITS-TPC track - C-side + std::vector mDCAr_comb_C_RMS; ///< DCAr RMS for ITS-TPC track - C-side + std::vector mDCAz_comb_C_RMS; ///< DCAz RMS for ITS-TPC track - C-side + + // dump this object to a tree + void dumpToTree(const char* outFileName, const int nHBFPerTF = 32); + + void setStartTime(long timeMS) + { + mTSTPC.setStartTime(timeMS); + mTSITSTPC.setStartTime(timeMS); + } + + void setBinning(const int nBinsPhi, const int nBinsTgl, const int qPtBins) + { + mTSTPC.mNBinsPhi = nBinsPhi; + mTSTPC.mNBinsTgl = nBinsTgl; + mTSTPC.mNBinsqPt = qPtBins; + mTSITSTPC.mNBinsPhi = nBinsPhi; + mTSITSTPC.mNBinsTgl = nBinsTgl; + mTSITSTPC.mNBinsqPt = qPtBins; + } + + bool areSameSize() const { return (mTSTPC.areSameSize() && mTSITSTPC.areSameSize()); } ///< check if stored currents have same number of entries + bool isEmpty() const { return mTSTPC.isEmpty(); } ///< check if values are empty + size_t getEntries() const { return mTSTPC.getEntries(); } ///< \return returns number of values stored + void fill(const std::vector& vecFrom, std::vector& vecTo, const unsigned int posIndex) { std::copy(vecFrom.begin(), vecFrom.end(), vecTo.begin() + posIndex); } + void insert(std::vector& vec, const std::vector& vecTmp) { vec.insert(vec.begin(), vecTmp.begin(), vecTmp.end()); } + + /// acummulate integrated currents at given index + /// \param posIndex index where data will be copied to + /// \param data integrated currents which will be copied + void fill(const unsigned int posIndex, const TimeSeriesITSTPC& data) + { + mTSTPC.fill(posIndex, data.mTSTPC); + mTSITSTPC.fill(posIndex, data.mTSITSTPC); + mITSTPCAll.fill(posIndex, data.mITSTPCAll); + mITSTPCStandalone.fill(posIndex, data.mITSTPCStandalone); + mITSTPCAfterburner.fill(posIndex, data.mITSTPCAfterburner); + fill(data.mDCAr_comb_A_Median, mDCAr_comb_A_Median, posIndex); + fill(data.mDCAz_comb_A_Median, mDCAz_comb_A_Median, posIndex); + fill(data.mDCAr_comb_A_RMS, mDCAr_comb_A_RMS, posIndex); + fill(data.mDCAz_comb_A_RMS, mDCAz_comb_A_RMS, posIndex); + fill(data.mDCAr_comb_C_Median, mDCAr_comb_C_Median, posIndex); + fill(data.mDCAz_comb_C_Median, mDCAz_comb_C_Median, posIndex); + fill(data.mDCAr_comb_C_RMS, mDCAr_comb_C_RMS, posIndex); + fill(data.mDCAz_comb_C_RMS, mDCAz_comb_C_RMS, posIndex); + + const int iTF = posIndex / mTSTPC.getNBins(); + nPrimVertices[iTF] = data.nPrimVertices.front(); + nVertexContributors_Median[iTF] = data.nVertexContributors_Median.front(); + nVertexContributors_RMS[iTF] = data.nVertexContributors_RMS.front(); + vertexX_Median[iTF] = data.vertexX_Median.front(); + vertexY_Median[iTF] = data.vertexY_Median.front(); + vertexZ_Median[iTF] = data.vertexZ_Median.front(); + vertexX_RMS[iTF] = data.vertexX_RMS.front(); + vertexY_RMS[iTF] = data.vertexY_RMS.front(); + vertexZ_RMS[iTF] = data.vertexZ_RMS.front(); + } + + /// \param nDummyValues number of empty values which are inserted at the beginning of the accumulated integrated currents + void insert(const unsigned int nDummyValues) + { + mTSTPC.insert(nDummyValues); + mTSITSTPC.insert(nDummyValues); + mITSTPCAll.insert(nDummyValues); + mITSTPCStandalone.insert(nDummyValues); + mITSTPCAfterburner.insert(nDummyValues); + std::vector vecTmp(nDummyValues, 0); + insert(mDCAr_comb_A_Median, vecTmp); + insert(mDCAz_comb_A_Median, vecTmp); + insert(mDCAr_comb_A_RMS, vecTmp); + insert(mDCAz_comb_A_RMS, vecTmp); + insert(mDCAr_comb_C_Median, vecTmp); + insert(mDCAz_comb_C_Median, vecTmp); + insert(mDCAr_comb_C_RMS, vecTmp); + insert(mDCAz_comb_C_RMS, vecTmp); + + const int nDummyValuesVtx = nDummyValues / mTSTPC.getNBins(); + std::vector vecTmpVtx(nDummyValuesVtx, 0); + insert(nPrimVertices, vecTmp); + insert(nVertexContributors_Median, vecTmp); + insert(nVertexContributors_RMS, vecTmp); + insert(vertexX_Median, vecTmp); + insert(vertexY_Median, vecTmp); + insert(vertexZ_Median, vecTmp); + insert(vertexX_RMS, vecTmp); + insert(vertexY_RMS, vecTmp); + insert(vertexZ_RMS, vecTmp); + } + + /// resize buffer for accumulated currents + void resize(const unsigned int nTotal) + { + mTSTPC.resize(nTotal); + mTSITSTPC.resize(nTotal); + mITSTPCAll.resize(nTotal); + mITSTPCStandalone.resize(nTotal); + mITSTPCAfterburner.resize(nTotal); + mDCAr_comb_A_Median.resize(nTotal); + mDCAz_comb_A_Median.resize(nTotal); + mDCAr_comb_A_RMS.resize(nTotal); + mDCAz_comb_A_RMS.resize(nTotal); + mDCAr_comb_C_Median.resize(nTotal); + mDCAz_comb_C_Median.resize(nTotal); + mDCAr_comb_C_RMS.resize(nTotal); + mDCAz_comb_C_RMS.resize(nTotal); + + const int nTotalVtx = nTotal / mTSTPC.getNBins(); + nPrimVertices.resize(nTotalVtx); + nVertexContributors_Median.resize(nTotalVtx); + nVertexContributors_RMS.resize(nTotalVtx); + vertexX_Median.resize(nTotalVtx); + vertexY_Median.resize(nTotalVtx); + vertexZ_Median.resize(nTotalVtx); + vertexX_RMS.resize(nTotalVtx); + vertexY_RMS.resize(nTotalVtx); + vertexZ_RMS.resize(nTotalVtx); + } + + ClassDefNV(TimeSeriesITSTPC, 2); }; } // end namespace tpc @@ -177,9 +515,11 @@ struct IFT0C { std::vector mINChanC; ///< integrated 1D FIT currents for NChan C std::vector mIAmplA; ///< integrated 1D FIT currents for Ampl A std::vector mIAmplC; ///< integrated 1D FIT currents for Ampl C + long mTimeMS{}; ///< start time in ms bool areSameSize() const { return sameSize(mINChanA, mINChanC, mIAmplA, mIAmplC); } ///< check if stored currents have same number of entries bool isEmpty() const { return mINChanA.empty(); } ///< check if values are empty size_t getEntries() const { return mINChanA.size(); } ///< \return returns number of values stored + void setStartTime(long timeMS) { mTimeMS = timeMS; } /// acummulate integrated currents at given index /// \param posIndex index where data will be copied to @@ -229,16 +569,18 @@ struct IFT0C { std::transform(mIAmplC.begin(), mIAmplC.end(), mIAmplC.begin(), [factor](const float val) { return val * factor; }); } - ClassDefNV(IFT0C, 1); + ClassDefNV(IFT0C, 2); }; /// struct containing the integrated FV0 currents struct IFV0C { std::vector mINChanA; ///< integrated 1D FIT currents for NChan A std::vector mIAmplA; ///< integrated 1D FIT currents for Ampl A + long mTimeMS{}; ///< start time in ms bool areSameSize() const { return sameSize(mINChanA, mIAmplA); } ///< check if stored currents have same number of entries bool isEmpty() const { return mINChanA.empty(); } ///< check if values are empty size_t getEntries() const { return mINChanA.size(); } ///< \return returns number of values stored + void setStartTime(long timeMS) { mTimeMS = timeMS; } /// acummulate integrated currents at given index /// \param posIndex index where data will be copied to @@ -278,7 +620,7 @@ struct IFV0C { std::transform(mIAmplA.begin(), mIAmplA.end(), mIAmplA.begin(), [factor](const float val) { return val * factor; }); } - ClassDefNV(IFV0C, 1); + ClassDefNV(IFV0C, 2); }; /// struct containing the integrated FDD currents @@ -287,9 +629,11 @@ struct IFDDC { std::vector mINChanC; ///< integrated 1D FIT currents for NChan C std::vector mIAmplA; ///< integrated 1D FIT currents for Ampl A std::vector mIAmplC; ///< integrated 1D FIT currents for Ampl C + long mTimeMS{}; ///< start time in ms bool areSameSize() const { return sameSize(mINChanA, mINChanC, mIAmplA, mIAmplC); } ///< check if stored currents have same number of entries bool isEmpty() const { return mINChanA.empty(); } ///< check if values are empty size_t getEntries() const { return mINChanA.size(); } ///< \return returns number of values stored + void setStartTime(long timeMS) { mTimeMS = timeMS; } /// acummulate integrated currents at given index /// \param posIndex index where data will be copied to @@ -339,7 +683,7 @@ struct IFDDC { std::transform(mIAmplC.begin(), mIAmplC.end(), mIAmplC.begin(), [factor](const float val) { return val * factor; }); } - ClassDefNV(IFDDC, 1); + ClassDefNV(IFDDC, 2); }; } // end namespace fit @@ -375,7 +719,7 @@ class IntegratedClusters void merge(const IntegratedClusters* prev); /// \return always return true. To specify the number of time slot intervals to wait for one should use the --max-delay option - bool hasEnoughData() const { return true; } + bool hasEnoughData() const { return (mRemainingData != -1); } /// \return returns accumulated currents const auto& getCurrents() const& { return mCurrents; } @@ -395,13 +739,16 @@ class IntegratedClusters /// \param outFileName name of the output file void dumpToTree(const char* outFileName = "ICTree.root"); + /// setting the start time + void setStartTime(long timeMS) { mCurrents.setStartTime(timeMS); } + private: - DataT mCurrents; ///< buffer for integrated currents - o2::calibration::TFType mTFFirst{}; ///< first TF of currents - o2::calibration::TFType mTFLast{}; ///< last TF of currents - o2::calibration::TFType mRemainingData{}; ///< counter for received data - unsigned int mNValuesPerTF{}; ///< number of expected currents per TF (estimated from first received data) - bool mInitialize{true}; ///< flag if this object will be initialized when fill method is called + DataT mCurrents; ///< buffer for integrated currents + o2::calibration::TFType mTFFirst{}; ///< first TF of currents + o2::calibration::TFType mTFLast{}; ///< last TF of currents + o2::calibration::TFType mRemainingData = -1; ///< counter for received data + unsigned int mNValuesPerTF{}; ///< number of expected currents per TF (estimated from first received data) + bool mInitialize{true}; ///< flag if this object will be initialized when fill method is called /// init member when first data is received /// \param valuesPerTF number of expected values per TF diff --git a/Detectors/Calibration/src/DetectorsCalibrationLinkDef.h b/Detectors/Calibration/src/DetectorsCalibrationLinkDef.h index f096891c2f8d7..8a9f6284d606c 100644 --- a/Detectors/Calibration/src/DetectorsCalibrationLinkDef.h +++ b/Detectors/Calibration/src/DetectorsCalibrationLinkDef.h @@ -53,4 +53,12 @@ #pragma link C++ class o2::calibration::TimeSlot < o2::calibration::IntegratedClusters < o2::fit::IFDDC>> + ; #pragma link C++ class o2::calibration::TimeSlotCalibration < o2::calibration::IntegratedClusters < o2::fit::IFDDC>> + ; +#pragma link C++ struct o2::tpc::TimeSeries + ; +#pragma link C++ struct o2::tpc::ITSTPC_Matching + ; +#pragma link C++ struct o2::tpc::TimeSeriesITSTPC + ; +#pragma link C++ class o2::calibration::IntegratedClusters < o2::tpc::TimeSeriesITSTPC> + ; +#pragma link C++ class o2::calibration::IntegratedClusterCalibrator < o2::tpc::TimeSeriesITSTPC> + ; +#pragma link C++ class o2::calibration::TimeSlot < o2::calibration::IntegratedClusters < o2::tpc::TimeSeriesITSTPC>> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::calibration::IntegratedClusters < o2::tpc::TimeSeriesITSTPC>> + ; + #endif diff --git a/Detectors/Calibration/src/IntegratedClusterCalibrator.cxx b/Detectors/Calibration/src/IntegratedClusterCalibrator.cxx index 66934ffd06115..b5f07c7304895 100644 --- a/Detectors/Calibration/src/IntegratedClusterCalibrator.cxx +++ b/Detectors/Calibration/src/IntegratedClusterCalibrator.cxx @@ -165,6 +165,7 @@ void IntegratedClusterCalibrator::finalizeSlot(o2::calibration::TimeSlot< LOGP(info, "Finalizing slot {} <= TF <= {}", startTF, endTF); auto& integratedClusters = *slot.getContainer(); + integratedClusters.setStartTime(slot.getStartTimeMS()); if (mDebug) { integratedClusters.dumpToFile(fmt::format("IntegratedClusters_TF_{}_{}_TS_{}_{}.root", startTF, endTF, slot.getStartTimeMS(), slot.getEndTimeMS()).data()); } @@ -196,8 +197,224 @@ template class IntegratedClusterCalibrator; template class IntegratedClusters; template class IntegratedClusterCalibrator; +template class IntegratedClusters; +template class IntegratedClusterCalibrator; + template class IntegratedClusters; template class IntegratedClusterCalibrator; } // end namespace calibration + +void tpc::TimeSeriesITSTPC::dumpToTree(const char* outFileName, const int nHBFPerTF) +{ + TFile f(outFileName, "RECREATE"); + TTree tree("timeSeries", "timeSeries"); + mTSTPC.dumpToTree(tree, "TPC_", nHBFPerTF); + mTSITSTPC.dumpToTree(tree, "ITSTPC_", nHBFPerTF); + mITSTPCAll.dumpToTree(tree, "ITSTPC_", nHBFPerTF, mTSTPC); + mITSTPCStandalone.dumpToTree(tree, "ITS_SA_", nHBFPerTF, mTSTPC); + mITSTPCAfterburner.dumpToTree(tree, "ITS_AB_", nHBFPerTF, mTSTPC); + f.WriteObject(&tree, "timeSeries"); +} + +void tpc::ITSTPC_Matching::dumpToTree(TTree& tree, const char* prefix, const int nHBFPerTF, const TimeSeries& timeSeriesRef) const +{ + // adding matching efficiency + const auto nValues = timeSeriesRef.mDCAr_A_Median.size(); + const int nPointsPerTF = timeSeriesRef.getNBins(); + const int nTotPoints = nValues / nPointsPerTF; + + const int nBinTypes = 4; + for (int type = 0; type < nBinTypes; ++type) { + std::array nBinsAllTypes{timeSeriesRef.mNBinsPhi, timeSeriesRef.mNBinsTgl, timeSeriesRef.mNBinsqPt, 1}; + std::array nBinsAllTypesNames{"phi", "tgl", "qPt", "int"}; + int nBins = nBinsAllTypes[type]; + + std::vector> matching_eff_A(nBins); + std::vector> matching_eff_C(nBins); + std::vector> chi2Match_A(nBins); + std::vector> chi2Match_C(nBins); + + std::vector listBranches; + listBranches.reserve(4 * nBins); + for (int i = 0; i < nBins; ++i) { + const std::string brSuf = (type == nBinTypes - 1) ? nBinsAllTypesNames[type] : fmt::format("{}_{}", nBinsAllTypesNames[type], i); + listBranches.emplace_back(tree.Branch(fmt::format("{}mITSTPC_A_MatchEff_{}", prefix, brSuf).data(), &matching_eff_A[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mITSTPC_C_MatchEff_{}", prefix, brSuf).data(), &matching_eff_C[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mITSTPC_A_Chi2Match_{}", prefix, brSuf).data(), &chi2Match_A[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mITSTPC_C_Chi2Match_{}", prefix, brSuf).data(), &chi2Match_C[i])); + } + + // alloc memory + for (int i = 0; i < nBins; ++i) { + matching_eff_A[i].resize(nTotPoints); + matching_eff_C[i].resize(nTotPoints); + chi2Match_A[i].resize(nTotPoints); + chi2Match_C[i].resize(nTotPoints); + } + + for (int j = 0; j < nTotPoints; ++j) { + for (int i = 0; i < nBins; ++i) { + int idx = timeSeriesRef.getIndexPhi(i, j); // phi bins + if (type == 1) { + idx = timeSeriesRef.getIndexTgl(i, j); // tgl bins + } else if (type == 2) { + idx = timeSeriesRef.getIndexqPt(i, j); // qPt + } else if (type == 3) { + idx = timeSeriesRef.getIndexInt(j); + } + matching_eff_A[i][j] = mITSTPC_A_MatchEff[idx]; + matching_eff_C[i][j] = mITSTPC_C_MatchEff[idx]; + chi2Match_A[i][j] = mITSTPC_A_Chi2Match[idx]; + chi2Match_C[i][j] = mITSTPC_C_Chi2Match[idx]; + } + } + // fill branches + for (auto br : listBranches) { + br->Fill(); + } + } + tree.SetEntries(1); +} + +void tpc::TimeSeries::dumpToTree(TTree& tree, const char* prefix, const int nHBFPerTF) const +{ + const auto nValues = mDCAr_A_Median.size(); + const int nPointsPerTF = getNBins(); + const int nTotPoints = nValues / nPointsPerTF; + + const int nBinTypes = 4; + for (int type = 0; type < nBinTypes; ++type) { + std::array nBinsAllTypes{mNBinsPhi, mNBinsTgl, mNBinsqPt, 1}; + std::array nBinsAllTypesNames{"phi", "tgl", "qPt", "int"}; + int nBins = nBinsAllTypes[type]; + + std::vector> dcar_A_Median(nBins); + std::vector> dcar_C_Median(nBins); + std::vector> dcar_A_WeightedMean(nBins); + std::vector> dcar_C_WeightedMean(nBins); + std::vector> dcar_A_RMS(nBins); + std::vector> dcar_C_RMS(nBins); + std::vector> dcaz_A_Median(nBins); + std::vector> dcaz_C_Median(nBins); + std::vector> dcaz_A_WeightedMean(nBins); + std::vector> dcaz_C_WeightedMean(nBins); + std::vector> dcaz_A_RMS(nBins); + std::vector> dcaz_C_RMS(nBins); + std::vector> nTracksDCAr_A(nBins); + std::vector> nTracksDCAr_C(nBins); + std::vector> nTracksDCAz_A(nBins); + std::vector> nTracksDCAz_C(nBins); + std::vector> mipdEdxRatioA(nBins); + std::vector> mipdEdxRatioC(nBins); + std::vector> tpcChi2A(nBins); + std::vector> tpcChi2C(nBins); + std::vector> tpcNClA(nBins); + std::vector> tpcNClC(nBins); + + std::vector listBranches; + listBranches.reserve(22 * nBins); + for (int i = 0; i < nBins; ++i) { + const std::string brSuf = (type == nBinTypes - 1) ? nBinsAllTypesNames[type] : fmt::format("{}_{}", nBinsAllTypesNames[type], i); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_A_Median_{}", prefix, brSuf).data(), &dcar_A_Median[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_C_Median_{}", prefix, brSuf).data(), &dcar_C_Median[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_A_WeightedMean_{}", prefix, brSuf).data(), &dcar_A_WeightedMean[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_C_WeightedMean_{}", prefix, brSuf).data(), &dcar_C_WeightedMean[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_A_RMS_{}", prefix, brSuf).data(), &dcar_A_RMS[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_C_RMS_{}", prefix, brSuf).data(), &dcar_C_RMS[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_A_Median_{}", prefix, brSuf).data(), &dcaz_A_Median[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_C_Median_{}", prefix, brSuf).data(), &dcaz_C_Median[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_A_WeightedMean_{}", prefix, brSuf).data(), &dcaz_A_WeightedMean[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_C_WeightedMean_{}", prefix, brSuf).data(), &dcaz_C_WeightedMean[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_A_RMS_{}", prefix, brSuf).data(), &dcaz_A_RMS[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_C_RMS_{}", prefix, brSuf).data(), &dcaz_C_RMS[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_A_NTracks_{}", prefix, brSuf).data(), &nTracksDCAr_A[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAr_C_NTracks_{}", prefix, brSuf).data(), &nTracksDCAr_C[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_A_NTracks_{}", prefix, brSuf).data(), &nTracksDCAz_A[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mDCAz_C_NTracks_{}", prefix, brSuf).data(), &nTracksDCAz_C[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mMIPdEdxRatioA_{}", prefix, brSuf).data(), &mipdEdxRatioA[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mMIPdEdxRatioC_{}", prefix, brSuf).data(), &mipdEdxRatioC[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mTPCChi2A_{}", prefix, brSuf).data(), &tpcChi2A[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mTPCChi2C_{}", prefix, brSuf).data(), &tpcChi2C[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mTPCNClA_{}", prefix, brSuf).data(), &tpcNClA[i])); + listBranches.emplace_back(tree.Branch(fmt::format("{}mTPCNClC_{}", prefix, brSuf).data(), &tpcNClC[i])); + } + + // alloc memory + for (int i = 0; i < nBins; ++i) { + dcar_A_Median[i].resize(nTotPoints); + dcar_C_Median[i].resize(nTotPoints); + dcar_A_WeightedMean[i].resize(nTotPoints); + dcar_C_WeightedMean[i].resize(nTotPoints); + dcar_A_RMS[i].resize(nTotPoints); + dcar_C_RMS[i].resize(nTotPoints); + dcaz_A_Median[i].resize(nTotPoints); + dcaz_C_Median[i].resize(nTotPoints); + dcaz_A_WeightedMean[i].resize(nTotPoints); + dcaz_C_WeightedMean[i].resize(nTotPoints); + dcaz_A_RMS[i].resize(nTotPoints); + dcaz_C_RMS[i].resize(nTotPoints); + nTracksDCAr_A[i].resize(nTotPoints); + nTracksDCAr_C[i].resize(nTotPoints); + nTracksDCAz_A[i].resize(nTotPoints); + nTracksDCAz_C[i].resize(nTotPoints); + mipdEdxRatioA[i].resize(nTotPoints); + mipdEdxRatioC[i].resize(nTotPoints); + tpcChi2A[i].resize(nTotPoints); + tpcChi2C[i].resize(nTotPoints); + tpcNClA[i].resize(nTotPoints); + tpcNClC[i].resize(nTotPoints); + } + + std::vector time(nTotPoints); + if (type == 0) { + listBranches.emplace_back(tree.Branch("time", &time)); + } + + for (int j = 0; j < nTotPoints; ++j) { + for (int i = 0; i < nBins; ++i) { + int idx = getIndexPhi(i, j); + if (type == 1) { + idx = getIndexTgl(i, j); + } else if (type == 2) { + idx = getIndexqPt(i, j); + } else if (type == 3) { + idx = getIndexInt(j); + } + dcar_A_Median[i][j] = mDCAr_A_Median[idx]; + dcar_C_Median[i][j] = mDCAr_C_Median[idx]; + dcar_A_WeightedMean[i][j] = mDCAr_A_WeightedMean[idx]; + dcar_C_WeightedMean[i][j] = mDCAr_C_WeightedMean[idx]; + dcar_A_RMS[i][j] = mDCAr_A_RMS[idx]; + dcar_C_RMS[i][j] = mDCAr_C_RMS[idx]; + dcaz_A_Median[i][j] = mDCAz_A_Median[idx]; + dcaz_C_Median[i][j] = mDCAz_C_Median[idx]; + dcaz_A_WeightedMean[i][j] = mDCAz_A_WeightedMean[idx]; + dcaz_C_WeightedMean[i][j] = mDCAz_C_WeightedMean[idx]; + dcaz_A_RMS[i][j] = mDCAz_A_RMS[idx]; + dcaz_C_RMS[i][j] = mDCAz_C_RMS[idx]; + nTracksDCAr_A[i][j] = mDCAr_A_NTracks[idx]; + nTracksDCAr_C[i][j] = mDCAr_C_NTracks[idx]; + nTracksDCAz_A[i][j] = mDCAz_A_NTracks[idx]; + nTracksDCAz_C[i][j] = mDCAz_C_NTracks[idx]; + mipdEdxRatioA[i][j] = mMIPdEdxRatioA[idx]; + mipdEdxRatioC[i][j] = mMIPdEdxRatioC[idx]; + tpcChi2A[i][j] = mTPCChi2A[idx]; + tpcChi2C[i][j] = mTPCChi2C[idx]; + tpcNClA[i][j] = mTPCNClA[idx]; + tpcNClC[i][j] = mTPCNClC[idx]; + } + // add time only once + if (type == 0) { + time[j] = mTimeMS + j * nHBFPerTF * o2::constants::lhc::LHCOrbitMUS / 1000; + } + } + // fill branches + for (auto br : listBranches) { + br->Fill(); + } + } + tree.SetEntries(1); +} + } // end namespace o2 diff --git a/Detectors/Calibration/testMacros/getRunParameters.cxx b/Detectors/Calibration/testMacros/getRunParameters.cxx index 7dfdce471f350..5aa2dc4c60dc2 100644 --- a/Detectors/Calibration/testMacros/getRunParameters.cxx +++ b/Detectors/Calibration/testMacros/getRunParameters.cxx @@ -20,6 +20,7 @@ #include "DataFormatsCTP/Scalers.h" #include "DataFormatsCTP/Configuration.h" #include "DataFormatsParameters/GRPMagField.h" +#include "DataFormatsParameters/GRPECSObject.h" #include "CommonTypes/Units.h" #include @@ -64,6 +65,17 @@ void writeBFieldToFile(float b) fclose(fptr); } +void writeDetListToFile(std::string detList) +{ + FILE* fptr = fopen("DetList.txt", "w"); + if (fptr == nullptr) { + LOGP(fatal, "ERROR: Could not open file to write detector list!"); + return; + } + fprintf(fptr, "%s", detList.c_str()); + fclose(fptr); +} + bool initOptionsAndParse(bpo::options_description& options, int argc, char* argv[], bpo::variables_map& vm) { options.add_options()( @@ -106,7 +118,7 @@ int main(int argc, char* argv[]) long duration = 0; // duration as O2end - O2start: auto& ccdb_inst = o2::ccdb::BasicCCDBManager::instance(); - ccdb_inst.setURL("https://alice-ccdb.cern.ch"); + ccdb_inst.setURL("http://alice-ccdb.cern.ch"); std::pair run_times = ccdb_inst.getRunDuration(run); long run_O2duration = long(run_times.second - run_times.first); // access SOR and EOR timestamps @@ -123,6 +135,20 @@ int main(int argc, char* argv[]) LOGP(info, "run {}: B field = {}", run, magFieldL3Curr); writeBFieldToFile((float)magFieldL3Curr); + // getting the detector list + LOGP(info, "Getting detector participating in the run"); + std::map metadataRun; + metadataRun["runNumber"] = std::to_string(run); + o2::parameters::GRPECSObject* ecsObj = ccdb_inst.getSpecific("GLO/Config/GRPECS", tsSOR, metadataRun); + std::string dets = ""; + for (int i = o2::detectors::DetID::First; i < o2::detectors::DetID::nDetectors; ++i) { + if (ecsObj->isDetReadOut(i)) { + dets = dets + o2::detectors::DetID::getName(i) + " "; + } + } + LOGP(info, "run {}: detectors in readout = {}", run, dets); + writeDetListToFile(dets); + LOGP(info, "Checking IR and duration"); if (run < 519041) { // LHC22c, d diff --git a/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h b/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h index 2cf97e55e8c7c..4384628d01f4c 100644 --- a/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h +++ b/Detectors/DCS/testWorkflow/src/DCStoDPLconverter.h @@ -14,6 +14,7 @@ #include "Framework/DataSpecUtils.h" #include "Framework/ExternalFairMQDeviceProxy.h" +#include "Framework/RawDeviceService.h" #include #include #include "DetectorsDCS/DataPointIdentifier.h" @@ -40,9 +41,7 @@ struct hash { }; } // namespace std -namespace o2 -{ -namespace dcs +namespace o2::dcs { using DPID = o2::dcs::DataPointIdentifier; using DPVAL = o2::dcs::DataPointValue; @@ -54,7 +53,8 @@ using DPCOM = o2::dcs::DataPointCompositeObject; o2f::InjectorFunction dcs2dpl(std::unordered_map& dpid2group, bool fbiFirst, bool verbose = false, int FBIPerInterval = 1) { - return [dpid2group, fbiFirst, verbose, FBIPerInterval](o2::framework::TimingInfo& tinfo, fair::mq::Device& device, fair::mq::Parts& parts, o2f::ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + return [dpid2group, fbiFirst, verbose, FBIPerInterval](o2::framework::TimingInfo& tinfo, framework::ServiceRegistryRef const& services, fair::mq::Parts& parts, o2f::ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) -> bool { + auto *device = services.get().device(); static std::unordered_map cache; // will keep only the latest measurement in the 1-second wide window for each DPID static std::unordered_map sentToChannel; static auto timer = std::chrono::high_resolution_clock::now(); @@ -69,7 +69,7 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp // check if we got FBI (Master) or delta (MasterDelta) if (!parts.Size()) { LOGP(warn, "Empty input recieved at timeslice {}", tinfo.timeslice); - return; + return false; } std::string firstName = std::string((char*)&(reinterpret_cast(parts.At(0)->GetData()))->id); @@ -124,6 +124,7 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp } std::chrono::duration> duration = timerNow - timer; + bool didSendMessages = false; if (duration.count() > 1 && (seenFBI || !fbiFirst)) { // did we accumulate for 1 sec and have we seen FBI if it was requested? std::unordered_map, std::hash> outputs; // in the cache we have the final values of the DPs that we should put in the output @@ -158,7 +159,7 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp hdr.payloadSize = it.second.size() * sizeof(DPCOM); hdr.firstTForbit = 0; // this should be irrelevant for DCS o2h::Stack headerStack{hdr, o2::framework::DataProcessingHeader{tinfo.timeslice, 1, creation}}; - auto fmqFactory = device.GetChannel(channel).Transport(); + auto fmqFactory = device->GetChannel(channel).Transport(); auto hdMessage = fmqFactory->CreateMessage(headerStack.size(), fair::mq::Alignment{64}); auto plMessage = fmqFactory->CreateMessage(hdr.payloadSize, fair::mq::Alignment{64}); memcpy(hdMessage->GetData(), headerStack.data(), headerStack.size()); @@ -181,8 +182,9 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp if (verbose) { LOG(info) << "Sending " << msgIt.second->Size() / 2 << " parts to channel " << msgIt.first; } - o2f::sendOnChannel(device, *msgIt.second.get(), msgIt.first, tinfo.timeslice); + o2f::sendOnChannel(*device, *msgIt.second.get(), msgIt.first, tinfo.timeslice); sentToChannel[msgIt.first]++; + didSendMessages |= msgIt.second->Size() > 0; } timer = timerNow; cache.clear(); @@ -200,10 +202,10 @@ o2f::InjectorFunction dcs2dpl(std::unordered_map& dp } LOGP(info, "{} inputs ({} bytes) of which {} FBI ({} bytes) seen in {:.3f} s | {}", nInp, fmt::group_digits(szInp), nInpFBI, fmt::group_digits(szInpFBI), runtime, sent); } + return didSendMessages; }; } -} // namespace dcs } // namespace o2 #endif /* O2_DCS_TO_DPL_CONVERTER_H */ diff --git a/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx b/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx index cd94bb61b87d3..9b697da428ad2 100644 --- a/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx +++ b/Detectors/DCS/testWorkflow/src/dcs-config-proxy.cxx @@ -17,6 +17,7 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ControlService.h" +#include "Framework/RawDeviceService.h" #include "Framework/Logger.h" #include "Framework/Lifetime.h" #include "Framework/ConfigParamSpec.h" @@ -66,16 +67,17 @@ auto getDataOriginFromFilename(const std::string& filename) InjectorFunction dcs2dpl(const std::string& acknowledge) { - return [acknowledge](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { + return [acknowledge](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) -> bool { + auto *device = services.get().device(); if (parts.Size() == 0) { // received at ^c, ignore LOG(info) << "ignoring empty message"; - return; + return false; } // make sure just 2 messages received if (parts.Size() != 2) { LOG(error) << "received " << parts.Size() << " instead of 2 expected"; - sendAnswer("error0: wrong number of messages", acknowledge, device); - return; + sendAnswer("error0: wrong number of messages", acknowledge, *device); + return false; } std::string filename{static_cast(parts.At(0)->GetData()), parts.At(0)->GetSize()}; size_t filesize = parts.At(1)->GetSize(); @@ -83,8 +85,8 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) o2::header::DataOrigin dataOrigin = getDataOriginFromFilename(filename); if (dataOrigin == o2::header::gDataOriginInvalid) { LOG(error) << "unknown detector for " << filename; - sendAnswer(fmt::format("{}:error1: unrecognized filename", filename), acknowledge, device); - return; + sendAnswer(fmt::format("{}:error1: unrecognized filename", filename), acknowledge, *device); + return false; } o2::header::DataHeader hdrF("DCS_CONFIG_FILE", dataOrigin, 0); @@ -93,8 +95,8 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) auto channel = channelRetriever(outsp, newTimesliceId); if (channel.empty()) { LOG(error) << "No output channel found for OutputSpec " << outsp; - sendAnswer(fmt::format("{}:error2: no channel to send", filename), acknowledge, device); - return; + sendAnswer(fmt::format("{}:error2: no channel to send", filename), acknowledge, *device); + return false; } hdrF.tfCounter = newTimesliceId; @@ -111,7 +113,7 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) hdrN.payloadSize = parts.At(0)->GetSize(); hdrN.firstTForbit = 0; // this should be irrelevant for DCS - auto fmqFactory = device.GetChannel(channel).Transport(); + auto fmqFactory = device->GetChannel(channel).Transport(); std::uint64_t creation = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); o2::header::Stack headerStackF{hdrF, DataProcessingHeader{newTimesliceId, 1, creation}}; @@ -129,15 +131,16 @@ InjectorFunction dcs2dpl(const std::string& acknowledge) fair::mq::Parts outPartsF; outPartsF.AddPart(std::move(hdMessageF)); outPartsF.AddPart(std::move(plMessageF)); - sendOnChannel(device, outPartsF, channel, (size_t)-1); + sendOnChannel(*device, outPartsF, channel, (size_t)-1); fair::mq::Parts outPartsN; outPartsN.AddPart(std::move(hdMessageN)); outPartsN.AddPart(std::move(plMessageN)); - sendOnChannel(device, outPartsN, channel, newTimesliceId); + sendOnChannel(*device, outPartsN, channel, newTimesliceId); - sendAnswer(fmt::format("{}:ok", filename), acknowledge, device); + sendAnswer(fmt::format("{}:ok", filename), acknowledge, *device); LOG(info) << "Sent DPL message and acknowledgment for file " << filename; + return true; }; } diff --git a/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h b/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h index 8723a7a68264b..0fcb87f20f2ff 100644 --- a/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h +++ b/Detectors/EMCAL/base/include/EMCALBase/RCUTrailer.h @@ -153,6 +153,10 @@ class RCUTrailer /// \return Size of the payload as number of 32 bit workds uint32_t getPayloadSize() const { return mPayloadSize; } + /// \brief Get number of corrupted trailer words (undefined trailer word code) + /// \return Number of trailer word corruptions + uint32_t getTrailerWordCorruptions() const { return mWordCorruptions; } + /// \brief Get the firmware version /// \return Firmware version uint8_t getFirmwareVersion() const { return mFirmwareVersion; } @@ -384,6 +388,11 @@ class RCUTrailer /// are assigned based on the trailer word marker. static RCUTrailer constructFromPayloadWords(const gsl::span payloadwords); + /// \brief Check whether the word is a valid last trailer word + /// \param trailerword Word to be checked + /// \return True if the word is a valid last trailer word, false if there are inconsistencies + static bool checkLastTrailerWord(uint32_t trailerword); + private: /// \struct AltroConfig /// \brief Bit field configuration of the ALTRO config registers @@ -438,6 +447,7 @@ class RCUTrailer uint8_t mFirmwareVersion = 0; ///< RCU firmware version uint32_t mTrailerSize = 0; ///< Size of the trailer (in number of 32 bit words) uint32_t mPayloadSize = 0; ///< Size of the payload (in nunber of 32 bit words) + uint32_t mWordCorruptions = 0; ///< Number of trailer word corruptions (decoding only) uint32_t mFECERRA = 0; ///< contains errors related to ALTROBUS transactions uint32_t mFECERRB = 0; ///< contains errors related to ALTROBUS transactions ErrorCounters mErrorCounter = {0, 0}; ///< Error counter registers diff --git a/Detectors/EMCAL/base/src/RCUTrailer.cxx b/Detectors/EMCAL/base/src/RCUTrailer.cxx index 770f2857ac86a..6d10d0cb93c1d 100644 --- a/Detectors/EMCAL/base/src/RCUTrailer.cxx +++ b/Detectors/EMCAL/base/src/RCUTrailer.cxx @@ -14,6 +14,7 @@ #include #include "CommonConstants/LHCConstants.h" #include "EMCALBase/RCUTrailer.h" +#include using namespace o2::emcal; @@ -34,6 +35,29 @@ void RCUTrailer::reset() mIsInitialized = false; } +bool RCUTrailer::checkLastTrailerWord(uint32_t trailerword) +{ + const int MIN_FWVERSION = 2; + const int MAX_FWVERSION = 2; + if ((trailerword >> 30) != 3) { + return false; + } + auto firmwarevesion = (trailerword >> 16) & 0xFF; + auto trailerSize = (trailerword & 0x7F); + if (firmwarevesion < MIN_FWVERSION || firmwarevesion > MAX_FWVERSION) { + return false; + } + if (trailerSize < 2) { + return false; + } + if (firmwarevesion == 2) { + if (trailerSize < 9) { + return false; + } + } + return true; +} + void RCUTrailer::constructFromRawPayload(const gsl::span payloadwords) { reset(); @@ -62,7 +86,7 @@ void RCUTrailer::constructFromRawPayload(const gsl::span payload foundTrailerWords++; int parCode = (word >> 26) & 0xF; int parData = word & 0x3FFFFFF; - // std::cout << "Found trailer word 0x" << std::hex << word << "(Par code: " << std::dec << parCode << ", Par data: 0x" << std::hex << parData << std::dec << ")"; + // std::cout << "Found trailer word 0x" << std::hex << word << "(Par code: " << std::dec << parCode << ", Par data: 0x" << std::hex << parData << std::dec << ")" << std::endl; switch (parCode) { case 1: // ERR_REG1 @@ -94,7 +118,8 @@ void RCUTrailer::constructFromRawPayload(const gsl::span payload mAltroConfig.mWord2 = parData & 0x1FFFFFF; break; default: - std::cerr << "Undefined parameter code " << parCode << ", ignore it !\n"; + LOG(warning) << "RCU trailer: Undefined parameter code " << parCode << " in word " << index << " (0x" << std::hex << word << std::dec << "), ignoring word"; + mWordCorruptions++; break; } } @@ -226,8 +251,8 @@ void RCUTrailer::printStream(std::ostream& stream) const << "Sparse readout: " << (isSparseReadout() ? "yes" : "no") << "\n" << "AltroCFG1: 0x" << std::hex << mAltroConfig.mWord1 << "\n" << "AltroCFG2: 0x" << std::hex << mAltroConfig.mWord2 << "\n" - << "Sampling time: " << timesample << " ns\n" - << "L1 Phase: " << l1phase << " ns\n" + << "Sampling time: " << std::dec << timesample << " ns\n" + << "L1 Phase: " << std::dec << l1phase << " ns (" << mAltroConfig.mL1Phase << ")\n" << std::dec << std::fixed; if (errors.size()) { stream << "Errors: \n" diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h index e46c466356545..be661a105395d 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/AltroDecoder.h @@ -11,6 +11,7 @@ #ifndef ALICEO2_EMCAL_ALTRODECODER_H #define ALICEO2_EMCAL_ALTRODECODER_H +#include #include #include #include @@ -155,9 +156,13 @@ class MinorAltroDecodingError /// \brief Error codes connected with the ALTRO decoding enum class ErrorType_t { BUNCH_HEADER_NULL, ///< Bunch header is 0 + CHANNEL_HEADER, ///< Channel header corruption CHANNEL_END_PAYLOAD_UNEXPECT, ///< Unexpected end of payload (channel or trailer word in bunch words) CHANNEL_PAYLOAD_EXCEED, ///< Exceeding channel payload block - BUNCH_LENGTH_EXCEED ///< Bunch length exceeding channel payload size + CHANNEL_ORDER, ///< Channels not in increasing order + BUNCH_LENGTH_EXCEED, ///< Bunch length exceeding channel payload size + BUNCH_LENGTH_ALLOW_EXCEED, ///< Exceeds maximum allowed bunch length + BUNCH_STARTTIME ///< Bunch start time exceeding }; /// \brief Dummy constructor @@ -206,7 +211,7 @@ class MinorAltroDecodingError /// \brief Get the number of error types handled by the AltroDecoderError /// \return Number of error types - static constexpr int getNumberOfErrorTypes() noexcept { return 4; } + static constexpr int getNumberOfErrorTypes() noexcept { return 8; } /// \brief Get the name connected to the error type /// @@ -302,6 +307,13 @@ class AltroDecoder /// \brief Destructor ~AltroDecoder() = default; + /// \brief Set the max. allowed bunch length + /// \param maxBunchLength Max. allowed bunch lengths + /// + /// Rejects bunches in case the decoded bunch length or the + /// decoded start time exceeds the maximum allowed bunch length. + void setMaxBunchLength(int maxBunchLength) { mMaxBunchLength = maxBunchLength; } + /// \brief Decode the ALTRO stream /// \throw AltroDecoderError if the RCUTrailer or ALTRO payload cannot be decoded /// @@ -341,11 +353,17 @@ class AltroDecoder /// In case of failure an exception is thrown. void checkRCUTrailer(); + /// \brief Check hardware address in channel header for consistency + /// \param hwaddress Hardware address to check + /// \return True if the channel is consistent (branch, FEC and Altro in expected range) - false otherwise + bool checkChannelHWAddress(int hwaddress); + RawReaderMemory& mRawReader; ///< underlying raw reader RCUTrailer mRCUTrailer; ///< RCU trailer std::vector mChannels; ///< vector of channels in the raw stream std::vector mMinorDecodingErrors; ///< Container for minor (non-crashing) errors bool mChannelsInitialized = false; ///< check whether the channels are initialized + unsigned int mMaxBunchLength = UINT_MAX; ///< Max bunch length ClassDefNV(AltroDecoder, 1); }; diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index 9bef0b9184117..1617a9f1a7d54 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -169,6 +169,8 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& trigVec, VCELL& c Cell::EncoderVersion encodingversion = o2::emcal::Cell::EncoderVersion::EncodingV0; if (header.majorVersion == 1 && header.minorVersion == 1) { encodingversion = o2::emcal::Cell::EncoderVersion::EncodingV1; + } else if (header.majorVersion == 1 && header.minorVersion == 2) { + encodingversion = o2::emcal::Cell::EncoderVersion::EncodingV2; } uint32_t firstEntry = 0, cellCount = 0; diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h index 58f57d53a4441..7d68b7bb7a03f 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawDecodingError.h @@ -44,7 +44,8 @@ class RawDecodingError : public std::exception HEADER_INVALID, ///< Header in memory not belonging to requested superpage PAGE_START_INVALID, ///< Page position starting outside payload size PAYLOAD_INVALID, ///< Payload in memory not belonging to requested superpage - TRAILER_DECODING ///< Inconsistent trailer in memory (several trailer words missing the trailer marker) + TRAILER_DECODING, ///< Inconsistent trailer in memory (several trailer words missing the trailer marker) + TRAILER_INCOMPLETE ///< Incomplete trailer words (i.e. registers) }; /// \brief Constructor @@ -93,6 +94,8 @@ class RawDecodingError : public std::exception return 5; case ErrorType_t::TRAILER_DECODING: return 6; + case ErrorType_t::TRAILER_INCOMPLETE: + return 7; }; // can never reach this, due to enum class // just to make Werror happy @@ -101,7 +104,7 @@ class RawDecodingError : public std::exception /// \brief Get the number of error codes /// \return Number of error codes - static constexpr int getNumberOfErrorTypes() { return 7; } + static constexpr int getNumberOfErrorTypes() { return 8; } static ErrorType_t intToErrorType(unsigned int errortype) { @@ -109,7 +112,7 @@ class RawDecodingError : public std::exception static constexpr std::array errortypes = {{ErrorType_t::PAGE_NOTFOUND, ErrorType_t::HEADER_DECODING, ErrorType_t::PAYLOAD_DECODING, ErrorType_t::HEADER_INVALID, ErrorType_t::PAGE_START_INVALID, ErrorType_t::PAYLOAD_INVALID, - ErrorType_t::TRAILER_DECODING}}; + ErrorType_t::TRAILER_DECODING, ErrorType_t::TRAILER_INCOMPLETE}}; return errortypes[errortype]; } @@ -137,6 +140,8 @@ class RawDecodingError : public std::exception return "PayloadCorruption"; case ErrorType_t::TRAILER_DECODING: return "TrailerDecoding"; + case ErrorType_t::TRAILER_INCOMPLETE: + return "TrailerIncomplete"; }; return "Undefined error"; } @@ -177,6 +182,8 @@ class RawDecodingError : public std::exception return "Payload corruption"; case ErrorType_t::TRAILER_DECODING: return "Trailer decoding"; + case ErrorType_t::TRAILER_INCOMPLETE: + return "Trailer incomplete"; }; return "Undefined error"; } @@ -217,6 +224,8 @@ class RawDecodingError : public std::exception return "Access to payload not belonging to requested superpage"; case ErrorType_t::TRAILER_DECODING: return "Inconsistent trailer in memory"; + case ErrorType_t::TRAILER_INCOMPLETE: + return "Incomplete trailer"; }; return "Undefined error"; } diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h index 843bda5f8398b..f726d1307fb90 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RawReaderMemory.h @@ -11,11 +11,13 @@ #ifndef ALICEO2_EMCAL_RAWREADERMEMORY_H #define ALICEO2_EMCAL_RAWREADERMEMORY_H +#include #include #include #include "EMCALBase/RCUTrailer.h" #include "EMCALReconstruction/RawBuffer.h" +#include "EMCALReconstruction/RawDecodingError.h" #include "EMCALReconstruction/RawPayload.h" #include "Headers/RAWDataHeader.h" #include "Headers/RDHAny.h" @@ -36,6 +38,46 @@ namespace emcal class RawReaderMemory { public: + /// \class MinorError + /// \brief Minor (non-crashing) raw decoding errors + /// + /// Minor errors share the same codes as major raw decoding errors, + /// however are not crashing. + class MinorError + { + public: + /// \brief Dummy constructor + MinorError() = default; + + /// \brief Main constructor + /// \param errortype Type of the error + /// \param feeID ID of the FEE equipment + MinorError(RawDecodingError::ErrorType_t errortype, int feeID) : mErrorType(errortype), mFEEID(feeID) {} + + /// \brief Destructor + ~MinorError() = default; + + /// \brief Set the type of the error + /// \param errortype Type of the error + void setErrorType(RawDecodingError::ErrorType_t errortype) { mErrorType = errortype; } + + /// \brief Set the ID of the FEE equipment + /// \param feeID ID of the FEE + void setFEEID(int feeID) { mFEEID = feeID; } + + /// \brief Get type of the error + /// \return Type of the error + RawDecodingError::ErrorType_t getErrorType() const { return mErrorType; } + + /// \brief Get ID of the FEE + /// \return ID of the FEE + int getFEEID() const { return mFEEID; } + + private: + RawDecodingError::ErrorType_t mErrorType; ///< Type of the error + int mFEEID; ///< ID of the FEC responsible for the ERROR + }; + /// \brief Constructor RawReaderMemory(const gsl::span rawmemory); @@ -58,8 +100,7 @@ class RawReaderMemory /// \brief Read next payload from the stream /// /// Read the next pages until the stop bit is found. - void - next(); + void next(); /// \brief Read the next page from the stream (single DMA page) /// \param resetPayload If true the raw payload is reset @@ -85,6 +126,10 @@ class RawReaderMemory /// \return Raw Payload of the data until the stop bit is received. const RawPayload& getPayload() const { return mRawPayload; } + /// \brief Get minor (non-crashing) raw decoding errors + /// \return Minor raw decoding errors + gsl::span getMinorErrors() const { return mMinorErrors; } + /// \brief Return size of the payload /// \return size of the payload int getPayloadSize() const { return mRawPayload.getPayloadSize(); } @@ -103,11 +148,10 @@ class RawReaderMemory /// Rewind stream to the first entry void init(); - /// \brief Check whether the current page is accepted - /// \param page Raw page to check - /// \return True if the page is accepted, false otherwise - bool acceptPage(const char* page) const; - + /// \brief Decode raw header words + /// \param headerwords Headerwords + /// \return Decoded RDH + /// \throw RawDecodingError with code HEADER_DECODING if the payload does not correspond to an expected header o2::header::RDHAny decodeRawHeader(const void* headerwords); private: @@ -123,6 +167,7 @@ class RawReaderMemory int mCurrentFEE = -1; ///< Current FEE in the data stream bool mRawHeaderInitialized = false; ///< RDH for current page initialized bool mPayloadInitialized = false; ///< Payload for current page initialized + std::vector mMinorErrors; ///< Minor raw decoding errors ClassDefNV(RawReaderMemory, 1); }; diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h index 98217825ab1ce..c675f57b3506a 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/RecoParam.h @@ -27,22 +27,46 @@ namespace emcal class RecoParam : public o2::conf::ConfigurableParamHelper { public: + /// \brief Destructor ~RecoParam() override = default; + /// \brief Get the average cell time shift + /// \return Average cell time shift double getCellTimeShiftNanoSec() const { return mCellTimeShiftNanoSec; } + + /// \brief Get noise threshold for LGnoHG error + /// \return Noise threshold double getNoiseThresholdLGnoHG() const { return mNoiseThresholdLGnoHG; } + + /// \brief Get the BC phase + /// \return BC phase int getPhaseBCmod4() const { return mPhaseBCmod4; } + /// \brief Get the max. allowed bunch length + /// \return Max. allowed bunch length + /// + /// In case of max. bunch length 0 the max. bunch length is auto-determined from + /// the RCU trailer + int getMaxAllowedBunchLength() const { return mMaxBunchLength; } + + /// \brief Print current reconstruction parameters to stream + /// \param stream Stream to print on void PrintStream(std::ostream& stream) const; private: double mNoiseThresholdLGnoHG = 10.; ///< Noise threshold applied to suppress LGnoHG error double mCellTimeShiftNanoSec = 470.; ///< Time shift applied on the cell time to center trigger peak around 0 int mPhaseBCmod4 = 1; ///< Rollback of the BC ID in the correction of the cell time for the BC mod 4 + unsigned int mMaxBunchLength = 15; ///< Max. allowed bunch length O2ParamDef(RecoParam, "EMCRecoParam"); }; -std::ostream& operator<<(std::ostream& stream, const RecoParam& s); + +/// \brief Streaming operator for the reconstruction parameters +/// \param stream Stream to print on +/// \param par RecoParams to be printed +/// \return Stream after printing the reco params +std::ostream& operator<<(std::ostream& stream, const RecoParam& par); } // namespace emcal namespace framework diff --git a/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx b/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx index 2df0890c2ef5b..2adeac0122068 100644 --- a/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx +++ b/Detectors/EMCAL/reconstruction/src/AltroDecoder.cxx @@ -42,7 +42,6 @@ void AltroDecoder::readRCUTrailer() gsl::span payloadwords(payloadwordsOrig.data(), payloadwordsOrig.size()); mRCUTrailer.constructFromRawPayload(payloadwords); } catch (RCUTrailer::Error& e) { - LOG(error) << "Error while decoding RCU trailer: " << e.what(); throw AltroDecoderError(AltroDecoderError::ErrorType_t::RCU_TRAILER_ERROR, fmt::format("{} {}", AltroDecoderError::getErrorTypeDescription(AltroDecoderError::ErrorType_t::RCU_TRAILER_ERROR), e.what())); } } @@ -63,6 +62,7 @@ void AltroDecoder::readChannels() int currentpos = 0; auto& buffer = mRawReader.getPayload().getPayloadWords(); auto maxpayloadsize = buffer.size() - mRCUTrailer.getTrailerSize(); + int lastFEC = -1; while (currentpos < maxpayloadsize) { auto currentword = buffer[currentpos++]; if (currentword >> 30 != 1) { @@ -75,6 +75,23 @@ void AltroDecoder::readChannels() uint16_t payloadsize = (channelheader >> 16) & 0x3FF; bool badchannel = (channelheader >> 29) & 0x1; + // check hardware address for consistency + if (!checkChannelHWAddress(hwaddress)) { + // Inconsistent HW address information, channel header corrupted, payload must be skipped + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::CHANNEL_ORDER, channelheader, currentword); + continue; + } + + int currentfec = 10 * Channel::getBranchIndexFromHwAddress(hwaddress) + Channel::getFecIndexFromHwAddress(hwaddress); + // std::cout << "Branch: " << Channel::getBranchIndexFromHwAddress(hwaddress) << ", FEC " << Channel::getFecIndexFromHwAddress(hwaddress) << " -> " << currentfec << " (channel " << Channel::getChannelIndexFromHwAddress(hwaddress) << " )" << std::endl; + if (currentfec < lastFEC) { + // FECs are ordered by the SRU, breaking the order is a clear sign of data corruption + // std::cout << "Error Fec, current " << currentfec << ", last " << lastFEC << std::endl; + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::CHANNEL_ORDER, channelheader, currentword); + continue; + } + lastFEC = currentfec; + /// decode all words for channel bool foundChannelError = false; int numberofwords = (payloadsize + 2) / 3; @@ -122,6 +139,18 @@ void AltroDecoder::readChannels() // we must break here as well, the bunch is cut and the pointer would be set to invalid memory break; } + // Raise minor decoding error in case the bunch length exceeds the maximum possible amount of samples + if ((unsigned int)bunchlength > mMaxBunchLength) { + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_ALLOW_EXCEED, channelheader, 0); + // Same as above: if the bunch length exceeds the maximum possible bunch length it will for sure conflict with the next bunch + break; + } + // Raise minor decoding error in case the start timne the maximum possible amount of samples (resulting in negative first sample index) + if ((unsigned int)starttime > mMaxBunchLength) { + mMinorDecodingErrors.emplace_back(MinorAltroDecodingError::ErrorType_t::BUNCH_STARTTIME, channelheader, 0); + // Also here we must break, out-of-bounds start time will create troubles in the raw fit + break; + } if (bunchlength == 0) { // skip bunches with bunch size 0, they don't contain any payload // Map error type to NULL header since header word is null @@ -138,6 +167,23 @@ void AltroDecoder::readChannels() mChannelsInitialized = true; } +bool AltroDecoder::checkChannelHWAddress(int hwaddress) +{ + unsigned int branch = Channel::getBranchIndexFromHwAddress(hwaddress), + fec = Channel::getFecIndexFromHwAddress(hwaddress), + altro = Channel::getAltroIndexFromHwAddress(hwaddress); + if (branch > 1) { + return false; + } + if (fec > 9) { + return false; + } + if (!(altro == 0 || altro == 2 || altro == 3 || altro == 4)) { + return false; + } + return true; +} + const RCUTrailer& AltroDecoder::getRCUTrailer() const { if (!mRCUTrailer.isInitialized()) { @@ -186,6 +232,7 @@ int AltroDecoderError::errorTypeToInt(AltroErrType errortype) case AltroErrType::CHANNEL_ERROR: errorNumber = 7; break; + default: break; } @@ -328,12 +375,24 @@ int MinorAltroDecodingError::errorTypeToInt(MinorAltroErrType errortype) case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: errorNumber = 1; break; - case MinorAltroErrType::BUNCH_HEADER_NULL: + case MinorAltroErrType::CHANNEL_ORDER: errorNumber = 2; break; - case MinorAltroErrType::BUNCH_LENGTH_EXCEED: + case MinorAltroErrType::CHANNEL_HEADER: errorNumber = 3; break; + case MinorAltroErrType::BUNCH_HEADER_NULL: + errorNumber = 4; + break; + case MinorAltroErrType::BUNCH_LENGTH_EXCEED: + errorNumber = 5; + break; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + errorNumber = 6; + break; + case MinorAltroErrType::BUNCH_STARTTIME: + errorNumber = 7; + break; }; return errorNumber; @@ -352,11 +411,23 @@ MinorAltroErrType MinorAltroDecodingError::intToErrorType(int errornumber) errorType = MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED; break; case 2: - errorType = MinorAltroErrType::BUNCH_HEADER_NULL; + errorType = MinorAltroErrType::CHANNEL_ORDER; break; case 3: + errorType = MinorAltroErrType::CHANNEL_HEADER; + break; + case 4: + errorType = MinorAltroErrType::BUNCH_HEADER_NULL; + break; + case 5: errorType = MinorAltroErrType::BUNCH_LENGTH_EXCEED; break; + case 6: + errorType = MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED; + break; + case 7: + errorType = MinorAltroErrType::BUNCH_STARTTIME; + break; default: break; } @@ -371,10 +442,18 @@ const char* MinorAltroDecodingError::getErrorTypeName(ErrorType_t errortype) return "ChannelEndPayloadUnexpected"; case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: return "ChannelPayloadExceed"; + case MinorAltroErrType::CHANNEL_ORDER: + return "ChannelOrderError"; + case MinorAltroErrType::CHANNEL_HEADER: + return "ChannelHeader"; case MinorAltroErrType::BUNCH_HEADER_NULL: return "BunchHeaderNull"; case MinorAltroErrType::BUNCH_LENGTH_EXCEED: return "BunchLengthExceed"; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + return "BunchLengthAllowExceed"; + case MinorAltroErrType::BUNCH_STARTTIME: + return "BunchStarttimeExceed"; }; return ""; } @@ -386,10 +465,18 @@ const char* MinorAltroDecodingError::getErrorTypeTitle(ErrorType_t errortype) return "Channel end unexpected"; case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: return "Channel exceed"; + case MinorAltroErrType::CHANNEL_ORDER: + return "FEC order"; + case MinorAltroErrType::CHANNEL_HEADER: + return "Channel header invalid"; case MinorAltroErrType::BUNCH_HEADER_NULL: return "Bunch header null"; case MinorAltroErrType::BUNCH_LENGTH_EXCEED: return "Bunch length exceed"; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + return "Bunch length impossible"; + case MinorAltroErrType::BUNCH_STARTTIME: + return "Bunch starttime exceed"; }; return ""; } @@ -401,10 +488,18 @@ const char* MinorAltroDecodingError::getErrorTypeDescription(ErrorType_t errorty return "Unexpected end of payload in altro channel payload!"; case MinorAltroErrType::CHANNEL_PAYLOAD_EXCEED: return "Trying to access out-of-bound payload!"; + case MinorAltroErrType::CHANNEL_ORDER: + return "Invalid FEC order"; + case MinorAltroErrType::CHANNEL_HEADER: + return "Invalid channel header"; case MinorAltroErrType::BUNCH_HEADER_NULL: return "Bunch header 0 or not configured!"; case MinorAltroErrType::BUNCH_LENGTH_EXCEED: return "Bunch length exceeding channel payload size!"; + case MinorAltroErrType::BUNCH_LENGTH_ALLOW_EXCEED: + return "Bunch length exceeding max. possible bunch size!"; + case MinorAltroErrType::BUNCH_STARTTIME: + return "Bunch start time outside range!"; }; return ""; } diff --git a/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx b/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx index 22cad97dd7e76..5f3b04f600192 100644 --- a/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx +++ b/Detectors/EMCAL/reconstruction/src/CTFCoder.cxx @@ -66,6 +66,6 @@ void CTFCoder::assignDictVersion(o2::ctf::CTFDictHeader& h) const h = mExtHeader; } else { h.majorVersion = 1; - h.minorVersion = 1; + h.minorVersion = 2; } } \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx b/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx index a9f15b3adeeb4..8ccbc5d63104a 100644 --- a/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx +++ b/Detectors/EMCAL/reconstruction/src/RawReaderMemory.cxx @@ -8,12 +8,13 @@ // 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 #include #include #include "EMCALReconstruction/RawReaderMemory.h" -#include "EMCALReconstruction/RawDecodingError.h" #include "DetectorsRaw/RDHUtils.h" +#include "Headers/RAWDataHeader.h" +#include "Headers/DAQID.h" using namespace o2::emcal; @@ -36,6 +37,16 @@ o2::header::RDHAny RawReaderMemory::decodeRawHeader(const void* payloadwords) if (headerversion < RDHDecoder::getVersion() || headerversion > RDHDecoder::getVersion()) { throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); } + if (!RDHDecoder::checkRDH(payloadwords, false, true)) { + // Header size != to 64 and 0 in reserved words indicate that the header is a fake header + throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); + } + if (RDHDecoder::getVersion(payloadwords) >= 6) { + // If header version is >= 6 check that the source ID is EMCAL + if (RDHDecoder::getSourceID(payloadwords) != o2::header::DAQID::EMC) { + throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); + } + } return {*reinterpret_cast(payloadwords)}; } @@ -51,6 +62,7 @@ void RawReaderMemory::next() { mRawPayload.reset(); mCurrentTrailer.reset(); + mMinorErrors.clear(); bool isDataTerminated = false; do { nextPage(false); @@ -117,9 +129,31 @@ void RawReaderMemory::nextPage(bool doResetPayload) } if (mCurrentPosition + RDHDecoder::getMemorySize(mRawHeader) > mRawMemoryBuffer.size()) { // Payload incomplete + // Set to offset to next or buffer size, whatever is smaller + if (mCurrentPosition + RDHDecoder::getOffsetToNext(mRawHeader) < mRawMemoryBuffer.size()) { + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); + if (mCurrentPosition + 64 < mRawMemoryBuffer.size()) { + // check if the page continues with a RDH + // if next page is not a header decodeRawHeader will throw a RawDecodingError + auto nextheader = decodeRawHeader(mRawMemoryBuffer.data() + mCurrentPosition); + if (RDHDecoder::getVersion(nextheader) != RDHDecoder::getVersion(mRawHeader)) { + // Next header has different header version - clear indication that it is a fake header + throw RawDecodingError(RawDecodingError::ErrorType_t::HEADER_DECODING, mCurrentFEE); + } + } + } else { + mCurrentPosition = mRawMemoryBuffer.size(); + } + // RDHDecoder::printRDH(mRawHeader); throw RawDecodingError(RawDecodingError::ErrorType_t::PAYLOAD_DECODING, mCurrentFEE); } else if (mCurrentPosition + RDHDecoder::getHeaderSize(mRawHeader) > mRawMemoryBuffer.size()) { // Start position of the payload is outside the payload range + // Set to offset to next or buffer size, whatever is smaller + if (mCurrentPosition + RDHDecoder::getOffsetToNext(mRawHeader) < mRawMemoryBuffer.size()) { + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); + } else { + mCurrentPosition = mRawMemoryBuffer.size(); + } throw RawDecodingError(RawDecodingError::ErrorType_t::PAGE_START_INVALID, mCurrentFEE); } else { mRawBuffer.readFromMemoryBuffer(gsl::span(mRawMemoryBuffer.data() + mCurrentPosition + RDHDecoder::getHeaderSize(mRawHeader), RDHDecoder::getMemorySize(mRawHeader) - RDHDecoder::getHeaderSize(mRawHeader))); @@ -139,7 +173,7 @@ void RawReaderMemory::nextPage(bool doResetPayload) // The trailer is only decoded if the DDL is in the range of SRU DDLs. STU pages are propagated // 1-1 without trailer parsing auto lastword = *(mRawBuffer.getDataWords().rbegin()); - if (lastword >> 30 == 3) { + if (RCUTrailer::checkLastTrailerWord(lastword)) { // lastword is a trailer word // decode trailer and chop try { @@ -150,7 +184,17 @@ void RawReaderMemory::nextPage(bool doResetPayload) mCurrentTrailer.setPayloadSize(mCurrentTrailer.getPayloadSize() + trailer.getPayloadSize()); } payloadWithoutTrailer = gsl::span(mRawBuffer.getDataWords().data(), mRawBuffer.getDataWords().size() - trailer.getTrailerSize()); + if (trailer.getTrailerWordCorruptions()) { + mMinorErrors.emplace_back(RawDecodingError::ErrorType_t::TRAILER_INCOMPLETE, mCurrentFEE); + } } catch (RCUTrailer::Error& e) { + // We must forward the position in such cases in order to not end up in an infinity loop + if (mCurrentPosition + RDHDecoder::getOffsetToNext(mRawHeader) < mRawMemoryBuffer.size()) { + mCurrentPosition += RDHDecoder::getOffsetToNext(mRawHeader); + } else { + mCurrentPosition = mRawMemoryBuffer.size(); + } + // Page is not consistent - must be skipped throw RawDecodingError(RawDecodingError::ErrorType_t::TRAILER_DECODING, mCurrentFEE); } } else { diff --git a/Detectors/EMCAL/reconstruction/src/RecoParam.cxx b/Detectors/EMCAL/reconstruction/src/RecoParam.cxx index 00f95b613d1e5..f80721ba9b88e 100644 --- a/Detectors/EMCAL/reconstruction/src/RecoParam.cxx +++ b/Detectors/EMCAL/reconstruction/src/RecoParam.cxx @@ -16,9 +16,9 @@ O2ParamImpl(o2::emcal::RecoParam); using namespace o2::emcal; -std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::RecoParam& s) +std::ostream& o2::emcal::operator<<(std::ostream& stream, const o2::emcal::RecoParam& par) { - s.PrintStream(stream); + par.PrintStream(stream); return stream; } @@ -28,5 +28,6 @@ void RecoParam::PrintStream(std::ostream& stream) const << "\n=============================================" << "\nTime offset (ns): " << mCellTimeShiftNanoSec << "\nNoise threshold HGLG suppression: " << mNoiseThresholdLGnoHG - << "\nPhase in BC mod 4 correction: " << mPhaseBCmod4; + << "\nPhase in BC mod 4 correction: " << mPhaseBCmod4 + << "\nMax number of ALTRO samples: " << mMaxBunchLength; } \ No newline at end of file diff --git a/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx b/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx index 785ace30e9307..c14ab175f7bdf 100644 --- a/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx +++ b/Detectors/EMCAL/reconstruction/test/testMinorAltroDecodingError.cxx @@ -23,23 +23,39 @@ namespace emcal BOOST_AUTO_TEST_CASE(MinorAltroDecodingError_test) { - BOOST_CHECK_EQUAL(MinorAltroDecodingError::getNumberOfErrorTypes(), 4); - std::array errornames = {{"ChannelEndPayloadUnexpected", + BOOST_CHECK_EQUAL(MinorAltroDecodingError::getNumberOfErrorTypes(), 8); + std::array errornames = {{"ChannelEndPayloadUnexpected", "ChannelPayloadExceed", + "ChannelOrderError", + "ChannelHeader", "BunchHeaderNull", - "BunchLengthExceed"}}, + "BunchLengthExceed", + "BunchLengthAllowExceed", + "BunchStarttimeExceed"}}, errortitles = {{"Channel end unexpected", "Channel exceed", + "FEC order", + "Channel header invalid", "Bunch header null", - "Bunch length exceed"}}, + "Bunch length exceed", + "Bunch length impossible", + "Bunch starttime exceed"}}, errordescriptions = {{"Unexpected end of payload in altro channel payload!", "Trying to access out-of-bound payload!", + "Invalid FEC order", + "Invalid channel header", "Bunch header 0 or not configured!", - "Bunch length exceeding channel payload size!"}}; - std::array errortypes = {{MinorAltroDecodingError::ErrorType_t::CHANNEL_END_PAYLOAD_UNEXPECT, + "Bunch length exceeding channel payload size!", + "Bunch length exceeding max. possible bunch size!", + "Bunch start time outside range!"}}; + std::array errortypes = {{MinorAltroDecodingError::ErrorType_t::CHANNEL_END_PAYLOAD_UNEXPECT, MinorAltroDecodingError::ErrorType_t::CHANNEL_PAYLOAD_EXCEED, + MinorAltroDecodingError::ErrorType_t::CHANNEL_ORDER, + MinorAltroDecodingError::ErrorType_t::CHANNEL_HEADER, MinorAltroDecodingError::ErrorType_t::BUNCH_HEADER_NULL, - MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_EXCEED}}; + MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_EXCEED, + MinorAltroDecodingError::ErrorType_t::BUNCH_LENGTH_ALLOW_EXCEED, + MinorAltroDecodingError::ErrorType_t::BUNCH_STARTTIME}}; for (int errortype = 0; errortype < MinorAltroDecodingError::getNumberOfErrorTypes(); errortype++) { BOOST_CHECK_EQUAL(MinorAltroDecodingError::errorTypeToInt(errortypes[errortype]), errortype); BOOST_CHECK_EQUAL(MinorAltroDecodingError::intToErrorType(errortype), errortypes[errortype]); diff --git a/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx b/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx index 87004f88c09a1..02b0d8addb031 100644 --- a/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx +++ b/Detectors/EMCAL/reconstruction/test/testRawDecodingError.cxx @@ -28,37 +28,39 @@ void testThrow(RawDecodingError::ErrorType_t errtype, unsigned int feeID) BOOST_AUTO_TEST_CASE(RawDecodingError_test) { - BOOST_CHECK_EQUAL(RawDecodingError::getNumberOfErrorTypes(), 7); - std::array errornames = {{"PageNotFound", + BOOST_CHECK_EQUAL(RawDecodingError::getNumberOfErrorTypes(), 8); + std::array errornames = {{"PageNotFound", "HeaderDecoding", "PayloadDecoding", "HeaderCorruption", "PageStartInvalid", "PayloadCorruption", - "TrailerDecoding"}}, + "TrailerDecoding", + "TrailerIncomplete"}}, errortitles = {{"Page not found", "Header decoding", "Payload decoding", "Header corruption", "Page start invalid", "Payload corruption", - "Trailer decoding"}}, + "Trailer decoding", + "Trailer incomplete"}}, errordescriptions = {{"Page with requested index not found", "RDH of page cannot be decoded", "Payload of page cannot be decoded", "Access to header not belonging to requested superpage", "Page decoding starting outside payload size", "Access to payload not belonging to requested superpage", - "Inconsistent trailer in memory"}}; - std::array errortypes = {{ - RawDecodingError::ErrorType_t::PAGE_NOTFOUND, - RawDecodingError::ErrorType_t::HEADER_DECODING, - RawDecodingError::ErrorType_t::PAYLOAD_DECODING, - RawDecodingError::ErrorType_t::HEADER_INVALID, - RawDecodingError::ErrorType_t::PAGE_START_INVALID, - RawDecodingError::ErrorType_t::PAYLOAD_INVALID, - RawDecodingError::ErrorType_t::TRAILER_DECODING, - }}; + "Inconsistent trailer in memory", + "Incomplete trailer"}}; + std::array errortypes = {{RawDecodingError::ErrorType_t::PAGE_NOTFOUND, + RawDecodingError::ErrorType_t::HEADER_DECODING, + RawDecodingError::ErrorType_t::PAYLOAD_DECODING, + RawDecodingError::ErrorType_t::HEADER_INVALID, + RawDecodingError::ErrorType_t::PAGE_START_INVALID, + RawDecodingError::ErrorType_t::PAYLOAD_INVALID, + RawDecodingError::ErrorType_t::TRAILER_DECODING, + RawDecodingError::ErrorType_t::TRAILER_INCOMPLETE}}; for (int errortype = 0; errortype < RawDecodingError::getNumberOfErrorTypes(); errortype++) { BOOST_CHECK_EQUAL(RawDecodingError::ErrorTypeToInt(errortypes[errortype]), errortype); BOOST_CHECK_EQUAL(RawDecodingError::intToErrorType(errortype), errortypes[errortype]); diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h index 91c534c3ba82b..4388035215d4f 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/RawWriter.h @@ -191,6 +191,18 @@ class RawWriter /// The input data is converted to 10 but ALTRO words and put on the stream. std::vector encodeBunchData(const std::vector& data); + /// \brief Extracting branch index from the hardware address + /// \param hwaddress Hardware address of the channel + int getBranchIndexFromHwAddress(int hwaddress) { return ((hwaddress >> 11) & 0x1); } + + /// \brief Extracting FEC index in branch from the hardware address + /// \param hwaddress Hardware address of the channel + int getFecIndexFromHwAddress(int hwaddress) { return ((hwaddress >> 7) & 0xF); } + + /// \brief Extracting Channel index in FEC from the hardware address + /// \param hwaddress Hardware address of the channel + int getChannelIndexFromHwAddress(int hwaddress) { return (hwaddress & 0xF); } + private: int mNADCSamples = 15; ///< Number of time samples int mPedestal = 1; ///< Pedestal diff --git a/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h b/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h index 6a8bd3180bda1..4a59b7fc4db28 100644 --- a/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h +++ b/Detectors/EMCAL/simulation/include/EMCALSimulation/SimParam.h @@ -33,9 +33,6 @@ class SimParam : public o2::conf::ConfigurableParamHelper Int_t getDigitThreshold() const { return mDigitThreshold; } Float_t getPinNoise() const { return mPinNoise; } Float_t getPinNoiseLG() const { return mPinNoiseLG; } - Float_t getTimeNoise() const { return mTimeNoise; } - Float_t getTimeDelay() const { return mTimeDelay; } - Bool_t isTimeDelayFromOCDB() const { return mTimeDelayFromOCDB; } Float_t getTimeResolutionPar0() const { return mTimeResolutionPar0; } Float_t getTimeResolutionPar1() const { return mTimeResolutionPar1; } Double_t getTimeResolution(Double_t energy) const; @@ -74,9 +71,6 @@ class SimParam : public o2::conf::ConfigurableParamHelper Float_t mGainFluctuations{15.}; ///< correct fMeanPhotonElectron by the gain fluctuations Float_t mPinNoise{0.012}; ///< Electronics noise in EMC, APD Float_t mPinNoiseLG{0.1}; ///< Electronics noise in EMC, APD, Low Gain - Float_t mTimeNoise{1.28e-5}; ///< Electronics noise in EMC, time - Float_t mTimeDelay{600e-9}; ///< Simple time delay to mimick roughly delay in data - Bool_t mTimeDelayFromOCDB{false}; ///< Get time delay from OCDB Float_t mTimeResolutionPar0{0.26666}; ///< Time resolution of FEE electronics Float_t mTimeResolutionPar1{1.4586}; ///< Time resolution of FEE electronics Int_t mNADCEC{0x10000}; ///< number of channels in EC section ADC diff --git a/Detectors/EMCAL/simulation/src/RawWriter.cxx b/Detectors/EMCAL/simulation/src/RawWriter.cxx index e289b9bfbca6e..14c32c0e299ae 100644 --- a/Detectors/EMCAL/simulation/src/RawWriter.cxx +++ b/Detectors/EMCAL/simulation/src/RawWriter.cxx @@ -9,6 +9,8 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include + #include #include @@ -127,12 +129,39 @@ bool RawWriter::processTrigger(const o2::emcal::TriggerRecord& trg) continue; } + // sort found towers according to FEC inside + // within the FEC channels are also sorted according + // their local channel ID + std::map> fecSortedTowersWithSignal; + auto& mappingDDL = mMappingHandler->getMappingForDDL(srucont.mSRUid); for (const auto& [tower, channel] : srucont.mChannels) { - bool saturatedBunchHG = false; - createPayload(channel, ChannelType_t::HIGH_GAIN, srucont.mSRUid, payload, saturatedBunchHG); - if (saturatedBunchHG) { - createPayload(channel, ChannelType_t::LOW_GAIN, srucont.mSRUid, payload, saturatedBunchHG); + auto hwaddress = mappingDDL.getHardwareAddress(channel.mRow, channel.mCol, ChannelType_t::HIGH_GAIN); + auto fecInDLL = getBranchIndexFromHwAddress(hwaddress) * 10 + getFecIndexFromHwAddress(hwaddress); + auto channelID = getChannelIndexFromHwAddress(hwaddress); + auto fecFound = fecSortedTowersWithSignal.find(fecInDLL); + if (fecFound != fecSortedTowersWithSignal.end()) { + fecFound->second[channelID] = tower; + } else { + std::map channelsInFec; + channelsInFec[channelID] = tower; + fecSortedTowersWithSignal[fecInDLL] = channelsInFec; + } + } + + // encode payload for sorted channels + for (const auto& [fec, channelsInFec] : fecSortedTowersWithSignal) { + for (auto [channelID, tower] : channelsInFec) { + auto towerChannel = srucont.mChannels.find(tower); + if (towerChannel != srucont.mChannels.end()) { + bool saturatedBunchHG = false; + createPayload(towerChannel->second, ChannelType_t::HIGH_GAIN, srucont.mSRUid, payload, saturatedBunchHG); + if (saturatedBunchHG) { + createPayload(towerChannel->second, ChannelType_t::LOW_GAIN, srucont.mSRUid, payload, saturatedBunchHG); + } + } else { + LOG(error) << "No data found for FEC " << fec << ", channel " << channelID << "(tower " << tower << ")"; + } } } diff --git a/Detectors/EMCAL/simulation/src/SimParam.cxx b/Detectors/EMCAL/simulation/src/SimParam.cxx index 74af39125f4c3..77fcc1366f9ea 100644 --- a/Detectors/EMCAL/simulation/src/SimParam.cxx +++ b/Detectors/EMCAL/simulation/src/SimParam.cxx @@ -30,9 +30,6 @@ void SimParam::PrintStream(std::ostream& stream) const stream << "\nEMCal::SimParam.mGainFluctuations = " << mGainFluctuations; stream << "\nEMCal::SimParam.mPinNoise = " << mPinNoise; stream << "\nEMCal::SimParam.mPinNoiseLG = " << mPinNoiseLG; - stream << "\nEMCal::SimParam.mTimeNoise = " << mTimeNoise; - stream << "\nEMCal::SimParam.mTimeDelay = " << mTimeDelay; - stream << "\nEMCal::SimParam.mTimeDelayFromOCDB = " << ((mTimeDelayFromOCDB) ? "true" : "false"); stream << "\nEMCal::SimParam.mTimeResolutionPar0 = " << mTimeResolutionPar0; stream << "\nEMCal::SimParam.mTimeResolutionPar1 = " << mTimeResolutionPar1; stream << "\nEMCal::SimParam.mTimeResponseTau = " << mTimeResponseTau; diff --git a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h index ee07bb4c282e4..a90a0f1718397 100644 --- a/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h +++ b/Detectors/EMCAL/workflow/include/EMCALWorkflow/RawToCellConverterSpec.h @@ -25,6 +25,7 @@ #include "EMCALBase/Geometry.h" #include "EMCALBase/Mapper.h" #include "EMCALReconstruction/CaloRawFitter.h" +#include "EMCALReconstruction/RawReaderMemory.h" #include "EMCALReconstruction/RecoContainer.h" #include "EMCALReconstruction/ReconstructionErrors.h" #include "EMCALWorkflow/CalibLoader.h" @@ -236,6 +237,8 @@ class RawToCellConverterSpec : public framework::Task void handlePageError(const RawDecodingError& e); + void handleMinorPageError(const RawReaderMemory::MinorError& e); + header::DataHeader::SubSpecificationType mSubspecification = 0; ///< Subspecification for output channels int mNoiseThreshold = 0; ///< Noise threshold in raw fit int mNumErrorMessages = 0; ///< Current number of error messages diff --git a/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx b/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx index 86ae5a16e74b5..e53b18fafcbb0 100644 --- a/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx +++ b/Detectors/EMCAL/workflow/src/RawToCellConverterSpec.cxx @@ -114,7 +114,8 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) // container with BCid and feeID std::unordered_map> bcFreq; - double timeshift = RecoParam::Instance().getCellTimeShiftNanoSec(); // subtract offset in ns in order to center the time peak around the nominal delay + double timeshift = RecoParam::Instance().getCellTimeShiftNanoSec(); // subtract offset in ns in order to center the time peak around the nominal delay + auto maxBunchLengthRP = RecoParam::Instance().getMaxAllowedBunchLength(); // exclude bunches where either the start time or the bunch length is above the expected maximum constexpr auto originEMC = o2::header::gDataOriginEMC; constexpr auto descRaw = o2::header::gDataDescriptionRawData; @@ -146,7 +147,6 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) std::vector filter{{"filter", framework::ConcreteDataTypeMatcher(originEMC, descRaw)}}; int firstEntry = 0; for (const auto& rawData : framework::InputRecordWalker(ctx.inputs(), filter)) { - // Skip SOX headers auto rdhblock = reinterpret_cast(rawData.payload); if (o2::raw::RDHUtils::getHeaderSize(rdhblock) == static_cast(o2::framework::DataRefUtils::getPayloadSize(rawData))) { @@ -162,11 +162,20 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) rawreader.next(); } catch (RawDecodingError& e) { handlePageError(e); + if (e.getErrorType() == RawDecodingError::ErrorType_t::HEADER_DECODING || e.getErrorType() == RawDecodingError::ErrorType_t::HEADER_INVALID) { + // We must break in case of header decoding as the offset to the next payload is lost + // consequently the parser does not know where to continue leading to an infinity loop + break; + } // We must skip the page as payload is not consistent // otherwise the next functions will rethrow the exceptions as // the page format does not follow the expected format continue; } + for (auto& e : rawreader.getMinorErrors()) { + handleMinorPageError(e); + // For minor errors we do not need to skip the page, just print and send the error to the QC + } auto& header = rawreader.getRawHeader(); auto triggerBC = raw::RDHUtils::getTriggerBC(header); @@ -216,6 +225,10 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) // use the altro decoder to decode the raw data, and extract the RCU trailer AltroDecoder decoder(rawreader); + if (maxBunchLengthRP) { + // apply user-defined max. bunch length + decoder.setMaxBunchLength(maxBunchLengthRP); + } // check the words of the payload exception in altrodecoder try { decoder.decode(); @@ -325,6 +338,7 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) } } + std::bitset<46> bitSetActiveLinks; if (mActiveLinkCheck) { // build expected active mask from DCS FeeDCS* feedcs = mCalibHandler->getFEEDCS(); @@ -334,32 +348,7 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) list0.set(21, false); list1.set(7, false); // must be 0x307FFFDFFFFF if all links are active - std::bitset<46> bitSetActiveLinks((list1.to_ullong() << 32) + list0.to_ullong()); - - // Check if we have received pages from all active links - // If not we cannot trust the timeframe and must send - // empty containers - bool hasMissingLinks = false; - for (const auto& [globalBC, activelinks] : bcFreq) { - if (activelinks != bitSetActiveLinks) { - hasMissingLinks = true; - LOG(error) << "Not all EMC active links contributed in global BCid=" << globalBC << ": mask=" << (activelinks ^ bitSetActiveLinks); - if (mCreateRawDataErrors) { - for (std::size_t ilink = 0; ilink < bitSetActiveLinks.size(); ilink++) { - if (!bitSetActiveLinks.test(ilink)) { - continue; - } - if (!activelinks.test(ilink)) { - mOutputDecoderErrors.emplace_back(ilink, ErrorTypeFEE::ErrorSource_t::LINK_ERROR, 0, -1, -1); - } - } - } - } - } - if (hasMissingLinks) { - sendData(ctx, mOutputCells, mOutputTriggerRecords, mOutputDecoderErrors); - return; - } + bitSetActiveLinks = std::bitset<46>((list1.to_ullong() << 32) + list0.to_ullong()); } // Loop over BCs, sort cells with increasing tower ID and write to output containers @@ -368,6 +357,35 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) int ncellsEvent = 0, nLEDMONsEvent = 0; int eventstart = mOutputCells.size(); auto& currentevent = eventIterator.nextEvent(); + const auto interaction = currentevent.getInteractionRecord(); + if (mActiveLinkCheck) { + // check for current event if all links are present + // discard event if not all links are present + auto bcfreqFound = bcFreq.find(interaction.toLong()); + if (bcfreqFound != bcFreq.end()) { + const auto& activelinks = bcfreqFound->second; + if (activelinks != bitSetActiveLinks) { + static int nErrors = 0; + if (nErrors++ < 3) { + LOG(error) << "Not all EMC active links contributed in global BCid=" << interaction.toLong() << ": mask=" << (activelinks ^ bitSetActiveLinks) << (nErrors == 3 ? " (not reporting further errors to avoid spamming)" : ""); + } + if (mCreateRawDataErrors) { + for (std::size_t ilink = 0; ilink < bitSetActiveLinks.size(); ilink++) { + if (!bitSetActiveLinks.test(ilink)) { + continue; + } + if (!activelinks.test(ilink)) { + mOutputDecoderErrors.emplace_back(ilink, ErrorTypeFEE::ErrorSource_t::LINK_ERROR, 0, -1, -1); + } + } + } + // discard event + // create empty trigger record with dedicated trigger bit marking as rejected + mOutputTriggerRecords.emplace_back(interaction, currentevent.getTriggerBits() | o2::emcal::triggerbits::Inc, eventstart, 0); + continue; + } + } + } // Add cells if (currentevent.getNumberOfCells()) { LOG(debug) << "Event has " << currentevent.getNumberOfCells() << " cells"; @@ -380,7 +398,6 @@ void RawToCellConverterSpec::run(framework::ProcessingContext& ctx) currentevent.sortCells(true); nLEDMONsEvent = bookEventCells(currentevent.getLEDMons(), true); } - const auto interaction = currentevent.getInteractionRecord(); LOG(debug) << "Next event [Orbit " << interaction.orbit << ", BC (" << interaction.bc << "]: Accepted " << ncellsEvent << " cells and " << nLEDMONsEvent << " LEDMONS"; mOutputTriggerRecords.emplace_back(interaction, currentevent.getTriggerBits(), eventstart, ncellsEvent + nLEDMONsEvent); } @@ -686,6 +703,22 @@ void RawToCellConverterSpec::handlePageError(const RawDecodingError& e) } } +void RawToCellConverterSpec::handleMinorPageError(const RawReaderMemory::MinorError& e) +{ + if (mCreateRawDataErrors) { + mOutputDecoderErrors.emplace_back(e.getFEEID(), ErrorTypeFEE::ErrorSource_t::PAGE_ERROR, RawDecodingError::ErrorTypeToInt(e.getErrorType()), -1, -1); + } + if (mNumErrorMessages < mMaxErrorMessages) { + LOG(warning) << " Page decoding: " << RawDecodingError::getErrorCodeDescription(e.getErrorType()) << " in FEE ID " << e.getFEEID(); + mNumErrorMessages++; + if (mNumErrorMessages == mMaxErrorMessages) { + LOG(warning) << "Max. amount of error messages (" << mMaxErrorMessages << " reached, further messages will be suppressed"; + } + } else { + mErrorMessagesSuppressed++; + } +} + void RawToCellConverterSpec::sendData(framework::ProcessingContext& ctx, const std::vector& cells, const std::vector& triggers, const std::vector& decodingErrors) const { constexpr auto originEMC = o2::header::gDataOriginEMC; diff --git a/Detectors/FIT/FDD/simulation/src/Digitizer.cxx b/Detectors/FIT/FDD/simulation/src/Digitizer.cxx index c8be105263fdc..adb056792d8e4 100644 --- a/Detectors/FIT/FDD/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FDD/simulation/src/Digitizer.cxx @@ -192,25 +192,51 @@ void Digitizer::storeBC(const BCCache& bc, o2::dataformats::MCTruthContainer& labels) { // LOG(info) << "Storing BC " << bc; - int first = digitsCh.size(), nStored = 0; + float totalChargeA = 0, totalChargeC = 0; + int n_hit_A = 0, n_hit_C = 0, total_time_A = 0, total_time_C = 0; + bool nChCside = 8; for (int ic = 0; ic < Nchannels; ic++) { float chargeADC = integrateCharge(bc.pulse[ic]); + int cfdTime = int(simulateTimeCFD(bc.pulse[ic])); + if (chargeADC != 0) { + if (ic < nChCside) { + totalChargeC += chargeADC; + total_time_C += cfdTime; + n_hit_C++; + } else { + totalChargeA += chargeADC; + total_time_A += cfdTime; + n_hit_A++; + } uint8_t channelBits = parameters.defaultFEEbits; if (std::rand() % 2) { ChannelData::setFlag(ChannelData::kNumberADC, channelBits); } - digitsCh.emplace_back(ic, int(simulateTimeCFD(bc.pulse[ic])), int(chargeADC), channelBits); + digitsCh.emplace_back(ic, cfdTime, int(chargeADC), channelBits); nStored++; } } - // bc.print(); - + // SET TRIGGERS + Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0; + is_A = n_hit_A > 0; + is_C = n_hit_C > 0; + uint32_t amplA = is_A ? totalChargeA * 0.125 : -5000; // sum amplitude A side / 8 (hardware) + uint32_t amplC = is_C ? totalChargeC * 0.125 : -5000; // sum amplitude C side / 8 (hardware) + int timeA = is_A ? total_time_A / n_hit_A : -5000; // average time A side + int timeC = is_C ? total_time_C / n_hit_C : -5000; // average time C side + isVertex = is_A && is_C; + + bool isLaser = false; + bool isOutputsAreBlocked = false; + bool isDataValid = true; + mTriggers.setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C), + amplA, amplC, timeA, timeC, isLaser, isOutputsAreBlocked, isDataValid); if (nStored != 0) { int nBC = digitsBC.size(); digitsBC.emplace_back(first, nStored, bc, mTriggers); - digitsTrig.emplace_back(bc, 0, 0, 0, 0, 0); + digitsTrig.emplace_back(bc, is_A, is_C, isVertex, is_Central, is_SemiCentral); for (const auto& lbl : bc.labels) { labels.addElement(nBC, lbl); diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h index 408eed72e3af1..7ac54d79144ef 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/InteractionTag.h @@ -26,11 +26,12 @@ namespace ft0 // These are configurable params for FT0 selection as interaction tag struct InteractionTag : public o2::conf::ConfigurableParamHelper { - int minAmplitudeAC = 0; ///< use only FT0 triggers with high enough amplitude - + int minAmplitudeAC = 2; ///< use only FT0 triggers with high enough amplitude + int minAmplitudeA = 1; + int minAmplitudeC = 1; bool isSelected(const RecPoints& rp) const { - return rp.getTrigger().getVertex() && (rp.getTrigger().getAmplA() + rp.getTrigger().getAmplC()) > minAmplitudeAC; + return rp.getTrigger().getVertex() && rp.getTrigger().getAmplA() >= minAmplitudeA && rp.getTrigger().getAmplC() >= minAmplitudeC && (rp.getTrigger().getAmplA() + rp.getTrigger().getAmplC()) > minAmplitudeAC; } O2ParamDef(InteractionTag, "ft0tag"); diff --git a/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h index 1e8827b90c2a3..1c95f354c6292 100644 --- a/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h +++ b/Detectors/FIT/raw/include/FITRaw/DataBlockBase.h @@ -58,8 +58,6 @@ namespace o2 namespace fit { -using namespace std; - static constexpr size_t SIZE_WORD = 16; static constexpr size_t SIZE_WORD_GBT = 10; // should be changed to gloabal variable static constexpr size_t SIZE_MAX_PAGE = 8192; // should be changed to gloabal variable diff --git a/Detectors/GlobalTracking/CMakeLists.txt b/Detectors/GlobalTracking/CMakeLists.txt index f681ac1b84f46..bd5fb4f9976d1 100644 --- a/Detectors/GlobalTracking/CMakeLists.txt +++ b/Detectors/GlobalTracking/CMakeLists.txt @@ -8,6 +8,7 @@ # 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. +# add_compile_options(-O0 -g -fPIC) o2_add_library(GlobalTracking TARGETVARNAME targetName diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h index 9f7e740322cd5..06cab0e501aac 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchGlobalFwd.h @@ -134,6 +134,9 @@ class MatchGlobalFwd void setMFTROFrameLengthMUS(float fums); ///< set MFT ROFrame duration in BC (continuous mode only) void setMFTROFrameLengthInBC(int nbc); + ///< set MFT ROFrame bias in BC (continuous mode only) or time shift applied already as MFTAlpideParam.roFrameBiasInBC + void setMFTROFrameBiasInBC(int nbc); + const std::vector& getMatchedFwdTracks() const { return mMatchedTracks; } const std::vector& getMFTMatchingPlaneParams() const { return mMFTMatchPlaneParams; } const std::vector& getMCHMatchingPlaneParams() const { return mMCHMatchPlaneParams; } @@ -288,6 +291,9 @@ class MatchGlobalFwd int mMFTROFrameLengthInBC = 0; ///< MFT RO frame in BC (for MFT cont. mode only) float mMFTROFrameLengthMUS = -1.; ///< MFT RO frame in \mus float mMFTROFrameLengthMUSInv = -1.; ///< MFT RO frame in \mus inverse + int mMFTROFrameBiasInBC = 0; ///< MFT ROF bias in BC wrt to orbit start + float mMFTROFrameBiasMUS = -1.; ///< MFT ROF bias in \mus + float mMFTROFrameBiasMUSInv = -1.; ///< MFT ROF bias in \mus inverse std::map mMatchingFunctionMap; ///< MFT-MCH Matching function std::map mCutFunctionMap; ///< MFT-MCH Candidate cut function diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchHMP.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchHMP.h index 0f856bdae9717..0f7e2214220d9 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchHMP.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchHMP.h @@ -37,7 +37,6 @@ #include "DetectorsBase/GeometryManager.h" #include "DataFormatsHMP/Cluster.h" -#include "GlobalTracking/MatchTPCITS.h" #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTRD/TrackTRD.h" #include "ReconstructionDataFormats/PID.h" diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h index 39acc8f93368f..08caa8cc09fe4 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchITSTPCQC.h @@ -28,6 +28,7 @@ #include "Steer/MCKinematicsReader.h" #include #include +#include namespace o2 { @@ -46,6 +47,10 @@ struct LblInfo { class MatchITSTPCQC { public: + enum matchType : int { TPC = 0, + ITS, + SIZE }; + MatchITSTPCQC() = default; ~MatchITSTPCQC(); @@ -56,37 +61,41 @@ class MatchITSTPCQC void finalize(); void reset(); - TH1D* getHistoPt() const { return mPt; } - TH1D* getHistoPtTPC() const { return mPtTPC; } - TEfficiency* getFractionITSTPCmatch() const { return mFractionITSTPCmatch; } + TH1D* getHistoPtNum(matchType m) const { return mPtNum[m]; } + TH1D* getHistoPtDen(matchType m) const { return mPtDen[m]; } + TEfficiency* getFractionITSTPCmatch(matchType m) const { return mFractionITSTPCmatch[m]; } + + TH1D* getHistoPtNumNoEta0(matchType m) const { return mPtNum_noEta0[m]; } + TH1D* getHistoPtDenNoEta0(matchType m) const { return mPtDen_noEta0[m]; } + TEfficiency* getFractionITSTPCmatchNoEta0(matchType m) const { return mFractionITSTPCmatch_noEta0[m]; } - TH1F* getHistoPhi() const { return mPhi; } - TH1F* getHistoPhiTPC() const { return mPhiTPC; } - TEfficiency* getFractionITSTPCmatchPhi() const { return mFractionITSTPCmatchPhi; } + TH1F* getHistoPhiNum(matchType m) const { return mPhiNum[m]; } + TH1F* getHistoPhiDen(matchType m) const { return mPhiDen[m]; } + TEfficiency* getFractionITSTPCmatchPhi(matchType m) const { return mFractionITSTPCmatchPhi[m]; } - TH2F* getHistoPhiVsPt() const { return mPhiVsPt; } - TH2F* getHistoPhiVsPtTPC() const { return mPhiVsPtTPC; } - TEfficiency* getFractionITSTPCmatchPhiVsPt() const { return mFractionITSTPCmatchPhiVsPt; } + TH2F* getHistoPhiVsPtNum(matchType m) const { return mPhiVsPtNum[m]; } + TH2F* getHistoPhiVsPtDen(matchType m) const { return mPhiVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiVsPt(matchType m) const { return mFractionITSTPCmatchPhiVsPt[m]; } - TH1F* getHistoEta() const { return mEta; } - TH1F* getHistoEtaTPC() const { return mEtaTPC; } - TEfficiency* getFractionITSTPCmatchEta() const { return mFractionITSTPCmatchEta; } + TH1F* getHistoEtaNum(matchType m) const { return mEtaNum[m]; } + TH1F* getHistoEtaDen(matchType m) const { return mEtaDen[m]; } + TEfficiency* getFractionITSTPCmatchEta(matchType m) const { return mFractionITSTPCmatchEta[m]; } - TH2F* getHistoEtaVsPt() const { return mEtaVsPt; } - TH2F* getHistoEtaVsPtTPC() const { return mEtaVsPtTPC; } - TEfficiency* getFractionITSTPCmatchEtaVsPt() const { return mFractionITSTPCmatchEtaVsPt; } + TH2F* getHistoEtaVsPtNum(matchType m) const { return mEtaVsPtNum[m]; } + TH2F* getHistoEtaVsPtDen(matchType m) const { return mEtaVsPtDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaVsPt(matchType m) const { return mFractionITSTPCmatchEtaVsPt[m]; } - TH1F* getHistoPtPhysPrim() const { return mPtPhysPrim; } - TH1F* getHistoPtTPCPhysPrim() const { return mPtTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchPhysPrim() const { return mFractionITSTPCmatchPhysPrim; } + TH1F* getHistoPtPhysPrimNum(matchType m) const { return mPtPhysPrimNum[m]; } + TH1F* getHistoPtPhysPrimDen(matchType m) const { return mPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim(matchType m) const { return mFractionITSTPCmatchPhysPrim[m]; } - TH1F* getHistoPhiPhysPrim() const { return mPhiPhysPrim; } - TH1F* getHistoPhiTPCPhysPrim() const { return mPhiTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchPhiPhysPrim() const { return mFractionITSTPCmatchPhiPhysPrim; } + TH1F* getHistoPhiPhysPrimNum(matchType m) const { return mPhiPhysPrimNum[m]; } + TH1F* getHistoPhiPhysPrimDen(matchType m) const { return mPhiPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhiPhysPrim(matchType m) const { return mFractionITSTPCmatchPhiPhysPrim[m]; } - TH1F* getHistoEtaPhysPrim() const { return mEtaPhysPrim; } - TH1F* getHistoEtaTPCPhysPrim() const { return mEtaTPCPhysPrim; } - TEfficiency* getFractionITSTPCmatchEtaPhysPrim() const { return mFractionITSTPCmatchEtaPhysPrim; } + TH1F* getHistoEtaPhysPrimNum(matchType m) const { return mEtaPhysPrimNum[m]; } + TH1F* getHistoEtaPhysPrimDen(matchType m) const { return mEtaPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchEtaPhysPrim(matchType m) const { return mFractionITSTPCmatchEtaPhysPrim[m]; } TH2F* getHistoResidualPt() const { return mResidualPt; } TH2F* getHistoResidualPhi() const { return mResidualPhi; } @@ -95,7 +104,18 @@ class MatchITSTPCQC TH1F* getHistoChi2Matching() const { return mChi2Matching; } TH1F* getHistoChi2Refit() const { return mChi2Refit; } TH2F* getHistoTimeResVsPt() const { return mTimeResVsPt; } + TH1F* getHistoDCAr() const { return mDCAr; } + + TH1D* getHisto1OverPtNum(matchType m) const { return m1OverPtNum[m]; } + TH1D* getHisto1OverPtDen(matchType m) const { return m1OverPtDen[m]; } + TEfficiency* getFractionITSTPCmatch1OverPt(matchType m) const { return mFractionITSTPCmatch1OverPt[m]; } + + TH1D* getHisto1OverPtPhysPrimNum(matchType m) const { return m1OverPtPhysPrimNum[m]; } + TH1D* getHisto1OverPtPhysPrimDen(matchType m) const { return m1OverPtPhysPrimDen[m]; } + TEfficiency* getFractionITSTPCmatchPhysPrim1OverPt(matchType m) const { return mFractionITSTPCmatchPhysPrim1OverPt[m]; } + void getHistos(TObjArray& objar); + void setSources(GID::mask_t src) { mSrc = src; } void setUseMC(bool b) { mUseMC = b; } bool getUseMC() const { return mUseMC; } @@ -115,52 +135,82 @@ class MatchITSTPCQC void setMinDCAtoBeamPipeDistanceCut(float v) { mDCACut = v; } void setMinDCAtoBeamPipeYCut(float v) { mDCACutY = v; } + // to remove after merging QC PR + TH1D* getHistoPt() const { return nullptr; } // old + TH1D* getHistoPtTPC() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatch() const { return nullptr; } // old + TH1F* getHistoPhi() const { return nullptr; } // old + TH1F* getHistoPhiTPC() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatchPhi() const { return nullptr; } // old + TH2F* getHistoPhiVsPt() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatchPhiVsPt() const { return nullptr; } // old + TH1F* getHistoEta() const { return nullptr; } // old + TH1F* getHistoEtaTPC() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatchEta() const { return nullptr; } // old + TH2F* getHistoEtaVsPt() const { return nullptr; } // old + TH2F* getHistoEtaVsPtTPC() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatchEtaVsPt() const { return nullptr; } // old + TH1F* getHistoPtPhysPrim() const { return nullptr; } // old + TH1F* getHistoPtTPCPhysPrim() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatchPhysPrim() const { return nullptr; } // old + TH1F* getHistoPhiPhysPrim() const { return nullptr; } // old + TH1F* getHistoPhiTPCPhysPrim() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatchPhiPhysPrim() const { return nullptr; } // old + TH1F* getHistoEtaPhysPrim() const { return nullptr; } // old + TH1F* getHistoEtaTPCPhysPrim() const { return nullptr; } // old + TEfficiency* getFractionITSTPCmatchEtaPhysPrim() const { return nullptr; } // old + private: std::shared_ptr mDataRequest; o2::globaltracking::RecoContainer mRecoCont; - GID::mask_t mSrc = GID::getSourcesMask("TPC,ITS-TPC"); - GID::mask_t mAllowedSources = GID::getSourcesMask("TPC,ITS-TPC"); + GID::mask_t mSrc = GID::getSourcesMask("ITS,TPC,ITS-TPC"); + GID::mask_t mAllowedSources = GID::getSourcesMask("ITS,TPC,ITS-TPC"); // TPC gsl::span mTPCTracks; + // ITS + gsl::span mITSTracks; // ITS-TPC gsl::span mITSTPCTracks; bool mUseMC = false; float mBz = 0; ///< nominal Bz - std::unordered_map mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, - // value is the LbLinfo with the id of the track with the highest pT found with that label so far, - // and the flag to say if it is a physical primary or not - std::unordered_map mMapTPCLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, - // value is the LblInfo with the id of the track with the highest number of TPC clusters found - // with that label so far, and the flag to say if it is a physical primary or not + std::array, matchType::SIZE> mMapLabels; // map with labels that have been found for the matched ITSTPC tracks; key is the label, + // value is the LbLinfo with the id of the track with the highest pT found with that label so far, + // and the flag to say if it is a physical primary or not + std::array, matchType::SIZE> mMapRefLabels; // map with labels that have been found for the unmatched TPC tracks; key is the label, + // value is the LblInfo with the id of the track with the highest number of TPC clusters found + // with that label so far, and the flag to say if it is a physical primary or not o2::steer::MCKinematicsReader mcReader; // reader of MC information // Pt - TH1D* mPt = nullptr; - TH1D* mPtTPC = nullptr; - TEfficiency* mFractionITSTPCmatch = nullptr; - TH1F* mPtPhysPrim = nullptr; - TH1F* mPtTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchPhysPrim = nullptr; + TH1D* mPtNum[matchType::SIZE] = {}; + TH1D* mPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch[matchType::SIZE] = {}; + TH1D* mPtNum_noEta0[matchType::SIZE] = {}; + TH1D* mPtDen_noEta0[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch_noEta0[matchType::SIZE] = {}; + TH1F* mPtPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim[matchType::SIZE] = {}; // Phi - TH1F* mPhi = nullptr; - TH1F* mPhiTPC = nullptr; - TEfficiency* mFractionITSTPCmatchPhi = nullptr; - TH1F* mPhiPhysPrim = nullptr; - TH1F* mPhiTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchPhiPhysPrim = nullptr; - TH2F* mPhiVsPt = nullptr; - TH2F* mPhiVsPtTPC = nullptr; - TEfficiency* mFractionITSTPCmatchPhiVsPt = nullptr; + TH1F* mPhiNum[matchType::SIZE] = {}; + TH1F* mPhiDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhi[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimNum[matchType::SIZE] = {}; + TH1F* mPhiPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiPhysPrim[matchType::SIZE] = {}; + TH2F* mPhiVsPtNum[matchType::SIZE] = {}; + TH2F* mPhiVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhiVsPt[matchType::SIZE] = {}; // Eta - TH1F* mEta = nullptr; - TH1F* mEtaTPC = nullptr; - TEfficiency* mFractionITSTPCmatchEta = nullptr; - TH1F* mEtaPhysPrim = nullptr; - TH1F* mEtaTPCPhysPrim = nullptr; - TEfficiency* mFractionITSTPCmatchEtaPhysPrim = nullptr; - TH2F* mEtaVsPt = nullptr; - TH2F* mEtaVsPtTPC = nullptr; - TEfficiency* mFractionITSTPCmatchEtaVsPt = nullptr; + TH1F* mEtaNum[matchType::SIZE] = {}; + TH1F* mEtaDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEta[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimNum[matchType::SIZE] = {}; + TH1F* mEtaPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaPhysPrim[matchType::SIZE] = {}; + TH2F* mEtaVsPtNum[matchType::SIZE] = {}; + TH2F* mEtaVsPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchEtaVsPt[matchType::SIZE] = {}; // Residuals TH2F* mResidualPt = nullptr; TH2F* mResidualPhi = nullptr; @@ -169,11 +219,20 @@ class MatchITSTPCQC TH1F* mChi2Matching = nullptr; TH1F* mChi2Refit = nullptr; TH2F* mTimeResVsPt = nullptr; + TH1F* mDCAr = nullptr; + // 1/Pt + TH1D* m1OverPtNum[matchType::SIZE] = {}; + TH1D* m1OverPtDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatch1OverPt[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimNum[matchType::SIZE] = {}; + TH1D* m1OverPtPhysPrimDen[matchType::SIZE] = {}; + TEfficiency* mFractionITSTPCmatchPhysPrim1OverPt[matchType::SIZE] = {}; void setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool is2D = false); int mNTPCSelectedTracks = 0; - int mNITSTPCSelectedTracks = 0; + int mNITSSelectedTracks = 0; + int mNITSTPCSelectedTracks[matchType::SIZE] = {0, 0}; // cut values float mPtCut = 0.1f; diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h index db0abf1233e00..0e19b15d9a401 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTOF.h @@ -90,7 +90,7 @@ class MatchTOF public: ///< perform matching for provided input - void run(const o2::globaltracking::RecoContainer& inp); + void run(const o2::globaltracking::RecoContainer& inp, unsigned long firstTForbit = 0); void setCosmics() { @@ -173,7 +173,7 @@ class MatchTOF } void setFIT(bool value = true) { mIsFIT = value; } - static int findFITIndex(int bc, const gsl::span& FITRecPoints); + static int findFITIndex(int bc, const gsl::span& FITRecPoints, unsigned long firstOrbit); void checkRefitter(); bool makeConstrainedTPCTrack(int matchedID, o2::dataformats::TrackTPCTOF& trConstr); @@ -216,13 +216,16 @@ class MatchTOF void doMatching(int sec); void doMatchingForTPC(int sec); void selectBestMatches(); - static void BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2); - static void BestMatchesHP(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels); + void BestMatches(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, const std::vector* TracksWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels, float calibMaxChi2); + void BestMatchesHP(std::vector& matchedTracksPairs, std::vector* matchedTracks, std::vector* matchedTracksIndex, int* matchedClustersIndex, const gsl::span& FITRecPoints, const std::vector& TOFClusWork, std::vector& CalibInfoTOF, unsigned long Timestamp, bool MCTruthON, const o2::dataformats::MCTruthContainer* TOFClusLabels, const std::vector* TracksLblWork, std::vector* OutTOFLabels); bool propagateToRefX(o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, o2::track::TrackLTIntegral& intLT); bool propagateToRefXWithoutCov(o2::track::TrackParCov& trc, float xRef /*in cm*/, float stepInCm /*in cm*/, float bz); void updateTimeDependentParams(); + static bool mHasFillScheme; + static bool mFillScheme[o2::constants::lhc::LHCMaxBunches]; + //================================================================ // Data members @@ -269,6 +272,8 @@ class MatchTOF unsigned long mTimestamp = 0; ///< in ms + unsigned long mFirstTForbit = 0; ///< First orbit in TF (needed to align FT0 recpoints) + // from ruben gsl::span mTPCTracksArray; ///< input TPC tracks span diff --git a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h index 16c8051f45d5a..87cb8f6b8a44e 100644 --- a/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h +++ b/Detectors/GlobalTracking/include/GlobalTracking/MatchTPCITS.h @@ -55,6 +55,9 @@ #include "DataFormatsITSMFT/TrkClusRef.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "CorrectionMapsHelper.h" +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) +#include "MemoryResources/MemoryResources.h" +#endif class TTree; @@ -86,7 +89,7 @@ namespace tpc { class TrackTPC; class VDriftCorrFact; -} +} // namespace tpc namespace gpu { @@ -124,7 +127,7 @@ struct TrackLocTPC : public o2::track::TrackParCov { float timeErr = 0.f; ///< time sigma (makes sense for constrained tracks only) int sourceID = 0; ///< TPC track origin in o2::dataformats::GlobalTrackID gid{}; // global track source ID (TPC track may be part of it) - int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC + int matchID = MinusOne; ///< entry (non if MinusOne) of its matchTPC struct in the mMatchesTPC Constraint_t constraint{Constrained}; float getCorrectedTime(float dt) const // return time0 corrected for extra drift (to match certain Z) @@ -142,10 +145,17 @@ struct TrackLocTPC : public o2::track::TrackParCov { ///< ITS track outward parameters propagated to reference X, with time bracket and index of ///< original track in the currently loaded ITS reco output struct TrackLocITS : public o2::track::TrackParCov { + enum : uint16_t { CloneBefore = 0x1, + CloneAfter = 0x2 }; o2::math_utils::Bracketf_t tBracket; ///< bracketing time in \mus - int sourceID = 0; ///< track origin id - int roFrame = MinusOne; ///< ITS readout frame assigned to this track - int matchID = MinusOne; ///< entry (non if MinusOne) of its matchCand struct in the mMatchesITS + int sourceID = 0; ///< track origin id + int roFrame = MinusOne; ///< ITS readout frame assigned to this track + int matchID = MinusOne; ///< entry (non if MinusOne) of its matchCand struct in the mMatchesITS + bool hasCloneBefore() const { return getUserField() & CloneBefore; } + bool hasCloneAfter() const { return getUserField() & CloneAfter; } + int getCloneShift() const { return hasCloneBefore() ? -1 : (hasCloneAfter() ? 1 : 0); } + void setCloneBefore() { setUserField(getUserField() | CloneBefore); } + void setCloneAfter() { setUserField(getUserField() | CloneAfter); } ClassDefNV(TrackLocITS, 1); }; @@ -221,6 +231,7 @@ struct TPCABSeed { winLinkID = lID; status = Validated; } + int8_t getNLayers() const { return o2::its::RecoGeomHelper::getNLayers() - lowestLayer; } bool needAlteranative() const { return status == NeedAlternative; } void setNeedAlternative() { status = NeedAlternative; } ABTrackLink& getLink(int i) { return trackLinks[i]; } @@ -252,6 +263,8 @@ struct TPCABSeed { linkID = link.parentID; } } + size_t sizeInternal() const { return sizeof(ABTrackLink) * trackLinks.size(); } + size_t capInternal() const { return sizeof(ABTrackLink) * trackLinks.capacity(); } }; struct InteractionCandidate : public o2::InteractionRecord { @@ -278,6 +291,8 @@ struct ITSChipClustersRefs { clusterID.clear(); std::memset(chipRefs.data(), 0, chipRefs.size() * sizeof(ClusRange)); // reset chip->cluster references } + size_t sizeInternal() const { return sizeof(int) * clusterID.size(); } + size_t capInternal() const { return sizeof(int) * clusterID.capacity(); } }; class MatchTPCITS @@ -285,9 +300,6 @@ class MatchTPCITS public: using ITSCluster = o2::BaseCluster; using ClusRange = o2::dataformats::RangeReference; - using MCLabContCl = o2::dataformats::MCTruthContainer; - using MCLabContTr = std::vector; - using MCLabSpan = gsl::span; using TPCTransform = o2::gpu::TPCFastTransform; using BracketF = o2::math_utils::Bracket; using BracketIR = o2::math_utils::Bracket; @@ -303,8 +315,28 @@ class MatchTPCITS static constexpr int MaxSeedsPerLayer = 50; // TODO static constexpr int NITSLayers = o2::its::RecoGeomHelper::getNLayers(); ///< perform matching for provided input - void run(const o2::globaltracking::RecoContainer& inp); - +#if !defined(__CINT__) && !defined(__MAKECINT__) && !defined(__ROOTCLING__) && !defined(__CLING__) + void run(const o2::globaltracking::RecoContainer& inp, + pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib); + void refitWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib); + bool refitTrackTPCITS(int iTPC, int& iITS, pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib); + void reportSizes(pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib); + bool runAfterBurner(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, pmr::vector& ABTrackletClusterIDs, + pmr::vector& ABTrackletRefs, pmr::vector>& calib); + void refitABWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, pmr::vector& ABTrackletClusterIDs, + pmr::vector& ABTrackletRefs, pmr::vector>& calib); + bool refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector& matchedTracks, pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs); +#endif // CLING void setSkipTPCOnly(bool v) { mSkipTPCOnly = v; } void setCosmics(bool v) { mCosmics = v; } bool isCosmics() const { return mCosmics; } @@ -361,13 +393,6 @@ class MatchTPCITS void printCandidatesTPC() const; void printCandidatesITS() const; - const std::vector& getMatchedTracks() const { return mMatchedTracks; } - const MCLabContTr& getMatchLabels() const { return mOutLabels; } - const MCLabContTr& getABTrackletLabels() const { return mABTrackletLabels; } - const std::vector& getABTrackletClusterIDs() const { return mABTrackletClusterIDs; } - const std::vector& getABTrackletRefs() const { return mABTrackletRefs; } - const std::vector& getTglITSTPC() const { return mTglITSTPC; } - //>>> ====================== options =============================>>> void setUseMatCorrFlag(MatCorrType f) { mUseMatCorrFlag = f; } auto getUseMatCorrFlag() const { return mUseMatCorrFlag; } @@ -426,8 +451,6 @@ class MatchTPCITS void doMatching(int sec); - void refitWinners(); - bool refitTrackTPCITS(int iTPC, int& iITS); bool refitTPCInward(o2::track::TrackParCov& trcIn, float& chi2, float xTgt, int trcID, float timeTB) const; void selectBestMatches(); @@ -504,25 +527,22 @@ class MatchTPCITS } // ========================= AFTERBURNER ========================= - void runAfterBurner(); int prepareABSeeds(); void processABSeed(int sid, const ITSChipClustersRefs& itsChipClRefs); int followABSeed(const o2::track::TrackParCov& seed, const ITSChipClustersRefs& itsChipClRefs, int seedID, int lrID, TPCABSeed& ABSeed); int registerABTrackLink(TPCABSeed& ABSeed, const o2::track::TrackParCov& trc, int clID, int parentID, int lr, int laddID, float chi2Cl); bool isBetter(float chi2A, float chi2B) { return chi2A < chi2B; } // RS FIMXE TODO - void refitABWinners(); - bool refitABTrack(int iITSAB, const TPCABSeed& seed); void accountForOverlapsAB(int lrSeed); float correctTPCTrack(o2::track::TrackParCov& trc, const TrackLocTPC& tTPC, const InteractionCandidate& cand) const; // RS FIXME will be needed for refit //================================================================ - bool mInitDone = false; ///< flag init already done - bool mFieldON = true; ///< flag for field ON/OFF - bool mCosmics = false; ///< flag cosmics mode - bool mMCTruthON = false; ///< flag availability of MC truth - float mBz = 0; ///< nominal Bz - int mTFCount = 0; ///< internal TF counter for debugger - int mNThreads = 1; ///< number of OMP threads + bool mInitDone = false; ///< flag init already done + bool mFieldON = true; ///< flag for field ON/OFF + bool mCosmics = false; ///< flag cosmics mode + bool mMCTruthON = false; ///< flag availability of MC truth + float mBz = 0; ///< nominal Bz + int mTFCount = 0; ///< internal TF counter for debugger + int mNThreads = 1; ///< number of OMP threads o2::InteractionRecord mStartIR{0, 0}; ///< IR corresponding to the start of the TF ///========== Parameters to be set externally, e.g. from CCDB ==================== @@ -546,27 +566,27 @@ class MatchTPCITS ///< assigned time0 and its track Z position (converted from mTPCTimeEdgeZSafeMargin) float mTPCTimeEdgeTSafeMargin = 0.f; float mTPCExtConstrainedNSigmaInv = 0.f; // inverse for NSigmas for TPC time-interval from external constraint time sigma - int mITSROFrameLengthInBC = 0; ///< ITS RO frame in BC (for ITS cont. mode only) - float mITSROFrameLengthMUS = -1.; ///< ITS RO frame in \mus - float mITSTimeResMUS = -1.; ///< nominal ITS time resolution derived from ROF - float mITSROFrameLengthMUSInv = -1.; ///< ITS RO frame in \mus inverse - int mITSTimeBiasInBC = 0; ///< ITS RO frame shift in BCs, i.e. t_i = (I_ROF*mITSROFrameLengthInBC + mITSTimeBiasInBC)*BCLength_MUS - float mITSTimeBiasMUS = 0.; ///< ITS RO frame shift in \mus, i.e. t_i = (I_ROF*mITSROFrameLengthInBC)*BCLength_MUS + mITSTimeBiasMUS - float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds - float mTPCVDriftInv = -1.; ///< inverse TPC nominal drift speed in cm/microseconds - float mTPCDriftTimeOffset = 0; ///< drift time offset in mus - float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds - float mTPCTBinNS = 0.; ///< TPC time bin duration in ns - float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds - float mZ2TPCBin = 0.; ///< conversion coeff from Z to TPC time-bin - float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z - float mNTPCBinsFullDrift = 0.; ///< max time bin for full drift - float mTPCZMax = 0.; ///< max drift length - float mTPCmeanX0Inv = 1. / 31850.; ///< TPC gas 1/X0 + int mITSROFrameLengthInBC = 0; ///< ITS RO frame in BC (for ITS cont. mode only) + float mITSROFrameLengthMUS = -1.; ///< ITS RO frame in \mus + float mITSTimeResMUS = -1.; ///< nominal ITS time resolution derived from ROF + float mITSROFrameLengthMUSInv = -1.; ///< ITS RO frame in \mus inverse + int mITSTimeBiasInBC = 0; ///< ITS RO frame shift in BCs, i.e. t_i = (I_ROF*mITSROFrameLengthInBC + mITSTimeBiasInBC)*BCLength_MUS + float mITSTimeBiasMUS = 0.; ///< ITS RO frame shift in \mus, i.e. t_i = (I_ROF*mITSROFrameLengthInBC)*BCLength_MUS + mITSTimeBiasMUS + float mTPCVDrift = -1.; ///< TPC drift speed in cm/microseconds + float mTPCVDriftInv = -1.; ///< inverse TPC nominal drift speed in cm/microseconds + float mTPCDriftTimeOffset = 0; ///< drift time offset in mus + float mTPCTBinMUS = 0.; ///< TPC time bin duration in microseconds + float mTPCTBinNS = 0.; ///< TPC time bin duration in ns + float mTPCTBinMUSInv = 0.; ///< inverse TPC time bin duration in microseconds + float mZ2TPCBin = 0.; ///< conversion coeff from Z to TPC time-bin + float mTPCBin2Z = 0.; ///< conversion coeff from TPC time-bin to Z + float mNTPCBinsFullDrift = 0.; ///< max time bin for full drift + float mTPCZMax = 0.; ///< max drift length + float mTPCmeanX0Inv = 1. / 31850.; ///< TPC gas 1/X0 float mMinTPCTrackPtInv = 999.; ///< cutoff on TPC track inverse pT float mMinITSTrackPtInv = 999.; ///< cutoff on ITS track inverse pT - bool mVDriftCalibOn = false; ///< flag to produce VDrift calibration data + bool mVDriftCalibOn = false; ///< flag to produce VDrift calibration data o2::tpc::VDriftCorrFact mTPCDrift{}; o2::gpu::CorrectionMapsHelper* mTPCCorrMapsHelper = nullptr; @@ -581,12 +601,14 @@ class MatchTPCITS const o2::globaltracking::RecoContainer* mRecoCont = nullptr; ///>>>------ these are input arrays which should not be modified by the matching code // since this info is provided by external device - gsl::span mTPCTracksArray; ///< input TPC tracks span - gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span - gsl::span mITSTrackROFRec; ///< input ITS tracks ROFRecord span - gsl::span mITSTracksArray; ///< input ITS tracks span - gsl::span mITSTrackClusIdx; ///< input ITS track cluster indices span - std::vector mITSClustersArray; ///< ITS clusters created in loadInput + gsl::span mTPCTracksArray; ///< input TPC tracks span + gsl::span mTPCTrackClusIdx; ///< input TPC track cluster indices span + gsl::span mITSTrackROFRec; ///< input ITS tracks ROFRecord span + gsl::span mITSTracksArray; ///< input ITS tracks span + gsl::span mITSTrackClusIdx; ///< input ITS track cluster indices span + std::vector mITSClustersArray; ///< ITS clusters created in loadInput + std::vector mITSClusterSizes; ///< ITS cluster sizes created in loadInput + gsl::span mITSClusterROFRec; ///< input ITS clusters ROFRecord span gsl::span mFITInfo; ///< optional input FIT info span @@ -596,26 +618,31 @@ class MatchTPCITS const o2::tpc::ClusterNativeAccess* mTPCClusterIdxStruct = nullptr; ///< struct holding the TPC cluster indices - const MCLabContCl* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels - MCLabSpan mITSTrkLabels; ///< input ITS Track MC labels - MCLabSpan mTPCTrkLabels; ///< input TPC Track MC labels + const o2::dataformats::MCTruthContainer* mITSClsLabels = nullptr; ///< input ITS Cluster MC labels + gsl::span mITSTrkLabels; ///< input ITS Track MC labels + gsl::span mTPCTrkLabels; ///< input TPC Track MC labels /// <<<----- + size_t mNMatches = 0; + size_t mNCalibPrelim = 0; size_t mNMatchesControl = 0; + + size_t mNABRefsClus = 0; + float mAB2MatchGuess = 0.2; // heuristic guess about fraction of AB matches in total matches std::vector mInteractions; ///< possible interaction times std::vector mInteractionMUSLUT; ///< LUT for interactions in 1MUS bins ///< container for record the match of TPC track to single ITS track - std::vector mMatchRecordsTPC; + std::vector mMatchRecordsTPC; // RSS DEQ ///< container for reference to MatchRecord involving particular ITS track - std::vector mMatchRecordsITS; + std::vector mMatchRecordsITS; // RSS DEQ //// std::vector mITSROFofTPCBin; ///< aux structure for mapping of TPC time-bins on ITS ROFs std::vector mITSROFTimes; ///< min/max times of ITS ROFs in \mus std::vector mTPCWork; ///< TPC track params prepared for matching std::vector mITSWork; ///< ITS track params prepared for matching - MCLabContTr mTPCLblWork; ///< TPC track labels - MCLabContTr mITSLblWork; ///< ITS track labels + std::vector mTPCLblWork; ///< TPC track labels + std::vector mITSLblWork; ///< ITS track labels std::vector mWinnerChi2Refit; ///< vector of refitChi2 for winners // ------------------------------ @@ -623,11 +650,7 @@ class MatchTPCITS ///< indices of selected track entries in mTPCWork (for tracks selected by AfterBurner) std::vector mTPCABIndexCache; std::vector mABWinnersIDs; - std::vector mABTrackletClusterIDs; ///< IDs of ITS clusters for AfterBurner winners - std::vector mABTrackletRefs; ///< references on AfterBurner winners clusters - std::vector mABClusterLinkIndex; ///< index of 1st ABClusterLink for every cluster used by AfterBurner, -1: unused, -10: used by external ITS tracks - MCLabContTr mABTrackletLabels; - // ------------------------------ + std::vector mABClusterLinkIndex; ///< index of 1st ABClusterLink for every cluster used by AfterBurner, -1: unused, -10: used by external ITS tracks ///< per sector indices of TPC track entry in mTPCWork std::array, o2::constants::math::NSectors> mTPCSectIndexCache; @@ -642,13 +665,6 @@ class MatchTPCITS /// mapping for tracks' continuos ROF cycle to actual continuous readout ROFs with eventual gaps std::vector mITSTrackROFContMapping; - ///< outputs tracks container - std::vector mMatchedTracks; - MCLabContTr mOutLabels; ///< Labels: = TPC labels with flag isFake set in case of fake matching - - ///< container for for vdrift calibration - std::vector mTglITSTPC; - o2::its::RecoGeomHelper mRGHelper; ///< helper for cluster and geometry access #ifdef _ALLOW_DEBUG_TREES_ @@ -680,7 +696,6 @@ class MatchTPCITS static constexpr std::string_view TimerName[] = {"Total", "PrepareITS", "PrepareTPC", "DoMatching", "SelectBest", "Refit", "ABSeeds", "ABMatching", "ABWinners", "ABRefit", "IO", "Debug"}; TStopwatch mTimer[NStopWatches]; - }; //______________________________________________ @@ -701,7 +716,6 @@ inline bool MatchTPCITS::isDisabledITS(const TrackLocITS& t) const { return t.ma //______________________________________________ inline bool MatchTPCITS::isDisabledTPC(const TrackLocTPC& t) const { return t.matchID < 0; } - } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx b/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx index 90c8c08fa6d70..4639e830e6415 100644 --- a/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx +++ b/Detectors/GlobalTracking/src/MatchGlobalFwd.cxx @@ -262,10 +262,10 @@ bool MatchGlobalFwd::prepareMFTData() } return false; } - float tMin = nBC * o2::constants::lhc::LHCBunchSpacingMUS; - float tMax = (nBC + mMFTROFrameLengthInBC) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMin = (nBC + mMFTROFrameBiasInBC) * o2::constants::lhc::LHCBunchSpacingMUS; + float tMax = (nBC + mMFTROFrameLengthInBC + mMFTROFrameBiasInBC) * o2::constants::lhc::LHCBunchSpacingMUS; if (!mMFTTriggered) { - auto irofCont = nBC / mMFTROFrameLengthInBC; + auto irofCont = (nBC + mMFTROFrameBiasInBC) / mMFTROFrameLengthInBC; if (mMFTTrackROFContMapping.size() <= irofCont) { // there might be gaps in the non-empty rofs, this will map continuous ROFs index to non empty ones mMFTTrackROFContMapping.resize((1 + irofCont / 128) * 128, 0); } @@ -651,6 +651,14 @@ void MatchGlobalFwd::setMFTROFrameLengthInBC(int nbc) mMFTROFrameLengthMUSInv = 1. / mMFTROFrameLengthMUS; } +//_________________________________________________________ +void MatchGlobalFwd::setMFTROFrameBiasInBC(int nbc) +{ + mMFTROFrameBiasInBC = nbc; + mMFTROFrameBiasMUS = nbc * o2::constants::lhc::LHCBunchSpacingNS * 1e-3; + mMFTROFrameBiasMUSInv = 1. / mMFTROFrameBiasMUS; +} + //_________________________________________________________ void MatchGlobalFwd::setBunchFilling(const o2::BunchFilling& bf) { diff --git a/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx b/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx index 2b54cab4c32ac..d1984e3b1a7b8 100644 --- a/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx +++ b/Detectors/GlobalTracking/src/MatchITSTPCQC.cxx @@ -36,33 +36,46 @@ MatchITSTPCQC::~MatchITSTPCQC() void MatchITSTPCQC::deleteHistograms() { - // Pt - delete mPt; - delete mPtTPC; - delete mFractionITSTPCmatch; - delete mPtPhysPrim; - delete mPtTPCPhysPrim; - delete mFractionITSTPCmatchPhysPrim; - // Phi - delete mPhi; - delete mPhiTPC; - delete mFractionITSTPCmatchPhi; - delete mPhiPhysPrim; - delete mPhiTPCPhysPrim; - delete mFractionITSTPCmatchPhiPhysPrim; - delete mPhiVsPt; - delete mPhiVsPtTPC; - delete mFractionITSTPCmatchPhiVsPt; - // Eta - delete mEta; - delete mEtaTPC; - delete mFractionITSTPCmatchEta; - delete mEtaPhysPrim; - delete mEtaTPCPhysPrim; - delete mFractionITSTPCmatchEtaPhysPrim; - delete mEtaVsPt; - delete mEtaVsPtTPC; - delete mFractionITSTPCmatchEtaVsPt; + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + delete mPtNum[i]; + delete mPtDen[i]; + delete mFractionITSTPCmatch[i]; + delete mPtNum_noEta0[i]; + delete mPtDen_noEta0[i]; + delete mFractionITSTPCmatch_noEta0[i]; + delete mPtPhysPrimNum[i]; + delete mPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim[i]; + // Phi + delete mPhiNum[i]; + delete mPhiDen[i]; + delete mFractionITSTPCmatchPhi[i]; + delete mPhiPhysPrimNum[i]; + delete mPhiPhysPrimDen[i]; + delete mFractionITSTPCmatchPhiPhysPrim[i]; + delete mPhiVsPtNum[i]; + delete mPhiVsPtDen[i]; + delete mFractionITSTPCmatchPhiVsPt[i]; + // Eta + delete mEtaNum[i]; + delete mEtaDen[i]; + delete mFractionITSTPCmatchEta[i]; + delete mEtaPhysPrimNum[i]; + delete mEtaPhysPrimDen[i]; + delete mFractionITSTPCmatchEtaPhysPrim[i]; + delete mEtaVsPtNum[i]; + delete mEtaVsPtDen[i]; + delete mFractionITSTPCmatchEtaVsPt[i]; + // 1/Pt + delete m1OverPtNum[i]; + delete m1OverPtDen[i]; + delete mFractionITSTPCmatch1OverPt[i]; + delete m1OverPtPhysPrimNum[i]; + delete m1OverPtPhysPrimDen[i]; + delete mFractionITSTPCmatchPhysPrim1OverPt[i]; + } + // Residuals delete mResidualPt; delete mResidualPhi; @@ -71,31 +84,41 @@ void MatchITSTPCQC::deleteHistograms() delete mChi2Matching; delete mChi2Refit; delete mTimeResVsPt; + delete mDCAr; } //__________________________________________________________ void MatchITSTPCQC::reset() { - // Pt - mPt->Reset(); - mPtTPC->Reset(); - mPtPhysPrim->Reset(); - mPtTPCPhysPrim->Reset(); - // Phi - mPhi->Reset(); - mPhiTPC->Reset(); - mPhiPhysPrim->Reset(); - mPhiTPCPhysPrim->Reset(); - mPhiVsPt->Reset(); - mPhiVsPtTPC->Reset(); - // Eta - mEta->Reset(); - mEtaTPC->Reset(); - mEtaPhysPrim->Reset(); - mEtaTPCPhysPrim->Reset(); - mEtaVsPt->Reset(); - mEtaVsPtTPC->Reset(); + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i]->Reset(); + mPtDen[i]->Reset(); + mPtNum_noEta0[i]->Reset(); + mPtDen_noEta0[i]->Reset(); + mPtPhysPrimNum[i]->Reset(); + mPtPhysPrimDen[i]->Reset(); + // Phi + mPhiNum[i]->Reset(); + mPhiDen[i]->Reset(); + mPhiPhysPrimNum[i]->Reset(); + mPhiPhysPrimDen[i]->Reset(); + mPhiVsPtNum[i]->Reset(); + mPhiVsPtDen[i]->Reset(); + // Eta + mEtaNum[i]->Reset(); + mEtaDen[i]->Reset(); + mEtaPhysPrimNum[i]->Reset(); + mEtaPhysPrimDen[i]->Reset(); + mEtaVsPtNum[i]->Reset(); + mEtaVsPtDen[i]->Reset(); + // 1/Pt + m1OverPtNum[i]->Reset(); + m1OverPtDen[i]->Reset(); + m1OverPtPhysPrimNum[i]->Reset(); + m1OverPtPhysPrimDen[i]->Reset(); + } // Residuals mResidualPt->Reset(); mResidualPhi->Reset(); @@ -104,43 +127,57 @@ void MatchITSTPCQC::reset() mChi2Matching->Reset(); mChi2Refit->Reset(); mTimeResVsPt->Reset(); + mDCAr->Reset(); } //__________________________________________________________ bool MatchITSTPCQC::init() { - // Pt - mPtTPC = new TH1D("mPtTPC", "Pt distribution of TPC tracks; Pt [GeV/c]; dNdPt", 100, 0.f, 20.f); - mFractionITSTPCmatch = new TEfficiency("mFractionITSTPCmatch", "Fraction of ITSTPC matched tracks vs Pt; Pt [GeV/c]; Eff", 100, 0.f, 20.f); - mPt = new TH1D("mPt", "Pt distribution of matched tracks; Pt [GeV/c]; dNdPt", 100, 0.f, 20.f); - // Phi - mPhiTPC = new TH1F("mPhiTPC", "Phi distribution of TPC tracks; Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhi = new TEfficiency("mFractionITSTPCmatchPhi", "Fraction of ITSTPC matched tracks vs Phi; Phi [rad]; Eff", 100, 0.f, 2 * TMath::Pi()); - mPhi = new TH1F("mPhi", "Phi distribution of matched tracks; Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mPhiVsPt = new TH2F("mPhiVsPt", "Phi distribution of matched tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); - mPhiVsPtTPC = new TH2F("mPhiVsPtTPC", "Phi distribution of TPC tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhiVsPt = new TEfficiency("mFractionITSTPCmatchPhiVsPt", "Fraction of ITSTPC matched tracks vs Phi; #it{p}_{T} [GeV#it{c}]; Phi [rad]; Eff", 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); - // Eta - mEta = new TH1F("mEta", "Eta distribution of matched tracks; Eta; dNdEta", 100, -2.f, 2.f); - mEtaTPC = new TH1F("mEtaTPC", "Eta distribution of TPC tracks; Eta; dNdEta", 100, -2.f, 2.f); - mFractionITSTPCmatchEta = new TEfficiency("mFractionITSTPCmatchEta", "Fraction of ITSTPC matched tracks vs Eta; Eta; Eff", 100, -2.f, 2.f); - mEtaVsPt = new TH2F("mEtaVsPt", "Eta distribution of matched tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Eta; dNdEta", 100, 0.f, 20.f, 100, -2.f, 2.f); - mEtaVsPtTPC = new TH2F("mEtaVsPtTPC", "Eta distribution of TPC tracks vs Pt; #it{p}_{T} [GeV#it{c}]; Eta ; dNdEta", 100, 0.f, 20.f, 100, -2.f, 2.f); - mFractionITSTPCmatchEtaVsPt = new TEfficiency("mFractionITSTPCmatchEtaVsPt", "Fraction of ITSTPC matched tracks vs Eta; #it{p}_{T} [GeV#it{c}]; Eta; Eff", 100, 0.f, 20.f, 100, -2.f, 2.f); - // These will be empty in case of no MC info... - mPhiTPCPhysPrim = new TH1F("mPhiTPCPhysPrim", "Phi distribution of TPC tracks (physical primary); Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchPhiPhysPrim = new TEfficiency("mFractionITSTPCmatchPhiPhysPrim", "Fraction of ITSTPC matched tracks vs Phi (physical primary); Phi [rad]; Eff", 100, 0.f, 2 * TMath::Pi()); - mFractionITSTPCmatchEtaPhysPrim = new TEfficiency("mFractionITSTPCmatchEtaPhysPrim", "Fraction of ITSTPC matched tracks vs Eta (physical primary); Eta; Eff", 100, -2.f, 2.f); - mPhiPhysPrim = new TH1F("mPhiPhysPrim", "Phi distribution of matched tracks (physical primary); Phi [rad]; dNdPhi", 100, 0.f, 2 * TMath::Pi()); - mEtaPhysPrim = new TH1F("mEtaPhysPrim", "Eta distribution of matched tracks (physical primary); Eta; dNdEta", 100, -2.f, 2.f); - mEtaTPCPhysPrim = new TH1F("mEtaTPCPhysPrim", "Eta distribution of TPC tracks (physical primary); Eta; dNdEta", 100, -2.f, 2.f); - // ...till here + + std::array title{"TPC", "ITS"}; + std::array etaSel{"", ", |eta| < 0.9"}; + for (int i = 0; i < matchType::SIZE; ++i) { + // Pt + mPtNum[i] = new TH1D(Form("mPtNum_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtDen[i] = new TH1D(Form("mPtDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mFractionITSTPCmatch[i] = new TEfficiency(Form("mFractionITSTPCmatch_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtNum_noEta0[i] = new TH1D(Form("mPtNum_noEta0_%s", title[i].c_str()), Form("Pt distribution of ITSTPC matched tracks without |eta| < 0.05, wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mPtDen_noEta0[i] = new TH1D(Form("mPtDen_noEta0_%s", title[i].c_str()), Form("Pt distribution of %s tracks without |eta| < 0.05 %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + mFractionITSTPCmatch_noEta0[i] = new TEfficiency(Form("mFractionITSTPCmatch_noEta0_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks vs Pt without |eta| < 0.05 %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f); + // Phi + mPhiNum[i] = new TH1F(Form("mPhiNum_%s", title[i].c_str()), Form("Phi distribution of ITSTPC matched tracks, wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiDen[i] = new TH1F(Form("mPhiDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mFractionITSTPCmatchPhi[i] = new TEfficiency(Form("mFractionITSTPCmatchPhi_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtNum[i] = new TH2F(Form("mPhiVsPtNum_%s", title[i].c_str()), Form("Phi vs Pt distribution of ITSTPC matched tracks wrt %s %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + mPhiVsPtDen[i] = new TH2F(Form("mPhiVsPtDen_%s", title[i].c_str()), Form("Phi vs Pt distribution of %s tracks %s; #it{p}_{T} [GeV#it{c}]; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + mFractionITSTPCmatchPhiVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks wrt %s tracks %s, Phi vs Pt; #it{p}_{T} [GeV#it{c}]; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 20.f, 100, 0.f, 2 * TMath::Pi()); + // Eta + mEtaNum[i] = new TH1F(Form("mEtaNum_%s", title[i].c_str()), Form("Eta distribution of ITSTPC matched tracks, wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaDen[i] = new TH1F(Form("mEtaDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mFractionITSTPCmatchEta[i] = new TEfficiency(Form("mFractionITSTPCmatchEta_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks , wrt %s tracks, vs Eta; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + mEtaVsPtNum[i] = new TH2F(Form("mEtaVsPtNum_%s", title[i].c_str()), Form("Eta vs Pt distribution of ITSTPC matched tracks, wrt %s tracks; #it{p}_{T} [GeV#it{c}]; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + mEtaVsPtDen[i] = new TH2F(Form("mEtaVsPtDen_%s", title[i].c_str()), Form("Eta vs Pt distribution of %s tracks; #it{p}_{T} [GeV#it{c}]; #it{p}_{T} [GeV#it{c}]; Eta", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + mFractionITSTPCmatchEtaVsPt[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaVsPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks, wrt %s tracks, Eta vs Pt; #it{p}_{T} [GeV#it{c}]; Eta; Eff", title[i].c_str()), 100, 0.f, 20.f, 100, -2.f, 2.f); + // 1/pt + m1OverPtNum[i] = new TH1D(Form("m1OverPtNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks, wrt %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtDen[i] = new TH1D(Form("m1OverPtDen_%s", title[i].c_str()), Form("1/Pt distribution of %s tracks %s; 1/Pt [c/GeV]; dNdPt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + mFractionITSTPCmatch1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatch1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt, wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + + // These will be empty in case of no MC info... + mPhiPhysPrimNum[i] = new TH1F(Form("mPhiPhysPrimNum_%s", title[i].c_str()), Form("Phi distribution of matched tracks (physical primary), wrt %s tracks %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mPhiPhysPrimDen[i] = new TH1F(Form("mPhiPhysPrimDen_%s", title[i].c_str()), Form("Phi distribution of %s tracks (physical primary) %s; Phi [rad]; dNdPhi", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mFractionITSTPCmatchPhiPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhiPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Phi (physical primary), wrt %s tracks %s; Phi [rad]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, 0.f, 2 * TMath::Pi()); + mEtaPhysPrimNum[i] = new TH1F(Form("mEtaPhysPrimNum_%s", title[i].c_str()), Form("Eta distribution of matched tracks (physical primary), wrt %s tracks; Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mEtaPhysPrimDen[i] = new TH1F(Form("mEtaPhysPrimDen_%s", title[i].c_str()), Form("Eta distribution of %s tracks (physical primary); Eta; dNdEta", title[i].c_str()), 100, -2.f, 2.f); + mFractionITSTPCmatchEtaPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchEtaPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Eta (physical primary), wrt %s tracks; Eta; Eff", title[i].c_str()), 100, -2.f, 2.f); + } mResidualPt = new TH2F("mResidualPt", "Residuals of ITS-TPC matching in #it{p}_{T}; #it{p}_{T}^{ITS-TPC} [GeV/c]; #it{p}_{T}^{ITS-TPC} - #it{p}_{T}^{TPC} [GeV/c]", 100, 0.f, 20.f, 100, -1.f, 1.f); mResidualPhi = new TH2F("mResidualPhi", "Residuals of ITS-TPC matching in #it{#phi}; #it{#phi}^{ITS-TPC} [rad]; #it{#phi}^{ITS-TPC} - #it{#phi}^{TPC} [rad]", 100, 0.f, 2 * TMath::Pi(), 100, -1.f, 1.f); mResidualEta = new TH2F("mResidualEta", "Residuals of ITS-TPC matching in #it{#eta}; #it{#eta}^{ITS-TPC}; #it{#eta}^{ITS-TPC} - #it{#eta}^{TPC}", 100, -2.f, 2.f, 100, -1.f, 1.f); mChi2Matching = new TH1F("mChi2Matching", "Chi2 of matching; chi2", 200, 0, 300); mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 300); + mDCAr = new TH1F("mDCAr", "DCA of TPC tracks; DCAr", 200, -100, 100); // log binning for pT const Int_t nbinsPt = 100; @@ -155,36 +192,57 @@ bool MatchITSTPCQC::init() xbinsPt[i] = TMath::Exp(TMath::Log(10) * xlogPt); } mTimeResVsPt = new TH2F("mTimeResVsPt", "Time resolution vs Pt; Pt [GeV/c]; time res [us]", nbinsPt, xbinsPt, 100, 0.f, 2.f); - mPtTPCPhysPrim = new TH1F("mPtTPPhysPrimC", "Pt distribution of TPC tracks (physical primary); Pt [GeV/c]; dNdPt", nbinsPt, xbinsPt); - mFractionITSTPCmatchPhysPrim = new TEfficiency("mFractionITSTPCmatchPhysPrim", "Fraction of ITSTPC matched tracks vs Pt (physical primary); Pt [GeV/c]; Eff", nbinsPt, xbinsPt); - mPtPhysPrim = new TH1F("mPtPhysPrim", "Pt distribution of matched tracks (physical primary); Pt [GeV/c]; dNdPt", nbinsPt, xbinsPt); - - mPtTPC->Sumw2(); - mPt->Sumw2(); - mPhiTPC->Sumw2(); - mPhi->Sumw2(); - mPhiVsPt->Sumw2(); - mPhiVsPtTPC->Sumw2(); - mPtTPCPhysPrim->Sumw2(); - mPtPhysPrim->Sumw2(); - mPhiTPCPhysPrim->Sumw2(); - mPhiPhysPrim->Sumw2(); - mEtaTPC->Sumw2(); - mEtaPhysPrim->Sumw2(); - mEtaTPCPhysPrim->Sumw2(); - mEtaVsPt->Sumw2(); - mEtaVsPtTPC->Sumw2(); - - mPtTPC->SetOption("logy"); - mPt->SetOption("logy"); - mEta->SetOption("logy"); + + for (int i = 0; i < matchType::SIZE; ++i) { + mPtPhysPrimNum[i] = new TH1F(Form("mPtPhysPrimNum_%s", title[i].c_str()), Form("Pt distribution of matched tracks (physical primary), wrt %s tracks %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mPtPhysPrimDen[i] = new TH1F(Form("mPtPhysPrimDen_%s", title[i].c_str()), Form("Pt distribution of %s tracks (physical primary) %s; Pt [GeV/c]; dNdPt", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + mFractionITSTPCmatchPhysPrim[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs Pt (physical primary), wrt %s tracks %s; Pt [GeV/c]; Eff", title[i].c_str(), etaSel[i].c_str()), nbinsPt, xbinsPt); + m1OverPtPhysPrimNum[i] = new TH1D(Form("m1OverPtPhysPrimNum_%s", title[i].c_str()), Form("1/Pt distribution of matched tracks (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + m1OverPtPhysPrimDen[i] = new TH1D(Form("m1OverPtPhysPrimDen_%s", title[i].c_str()), Form("1/PtPt distribution of %s tracks (physical primary) %s; 1/Pt [c/GeV]; dNd1/Pt", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + mFractionITSTPCmatchPhysPrim1OverPt[i] = new TEfficiency(Form("mFractionITSTPCmatchPhysPrim1OverPt_%s", title[i].c_str()), Form("Fraction of ITSTPC matched tracks vs 1/Pt (physical primary), wrt %s tracks %s; 1/Pt [c/GeV]; Eff", title[i].c_str(), etaSel[i].c_str()), 100, -20.f, 20.f); + + // some extra settings + mPtNum[i]->Sumw2(); + mPtDen[i]->Sumw2(); + mPtNum_noEta0[i]->Sumw2(); + mPtDen_noEta0[i]->Sumw2(); + mPhiNum[i]->Sumw2(); + mPhiDen[i]->Sumw2(); + mPhiVsPtNum[i]->Sumw2(); + mPhiVsPtDen[i]->Sumw2(); + mPtPhysPrimNum[i]->Sumw2(); + mPtPhysPrimDen[i]->Sumw2(); + mPhiPhysPrimNum[i]->Sumw2(); + mPhiPhysPrimDen[i]->Sumw2(); + mEtaNum[i]->Sumw2(); + mEtaDen[i]->Sumw2(); + mEtaPhysPrimNum[i]->Sumw2(); + mEtaPhysPrimDen[i]->Sumw2(); + mEtaVsPtNum[i]->Sumw2(); + mEtaVsPtDen[i]->Sumw2(); + + m1OverPtNum[i]->Sumw2(); + m1OverPtDen[i]->Sumw2(); + m1OverPtPhysPrimNum[i]->Sumw2(); + m1OverPtPhysPrimDen[i]->Sumw2(); + + mPtNum_noEta0[i]->SetOption("logy"); + mPtDen_noEta0[i]->SetOption("logy"); + mPtNum[i]->SetOption("logy"); + mPtDen[i]->SetOption("logy"); + + mPtNum[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen[i]->GetYaxis()->SetTitleOffset(1.4); + mPtNum_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mPtDen_noEta0[i]->GetYaxis()->SetTitleOffset(1.4); + mEtaNum[i]->GetYaxis()->SetTitleOffset(1.4); + mEtaDen[i]->GetYaxis()->SetTitleOffset(1.4); + } + mChi2Matching->SetOption("logy"); mChi2Refit->SetOption("logy"); mTimeResVsPt->SetOption("colz logz logy logx"); - mPtTPC->GetYaxis()->SetTitleOffset(1.4); - mPt->GetYaxis()->SetTitleOffset(1.4); - mEta->GetYaxis()->SetTitleOffset(1.4); mChi2Matching->GetYaxis()->SetTitleOffset(1.4); mChi2Refit->GetYaxis()->SetTitleOffset(1.4); mTimeResVsPt->GetYaxis()->SetTitleOffset(1.4); @@ -205,7 +263,7 @@ void MatchITSTPCQC::initDataRequest() mSrc &= mAllowedSources; - if ((mSrc[GID::Source::ITSTPC] == 0 || mSrc[GID::Source::TPC] == 0)) { + if (mSrc[GID::Source::ITSTPC] == 0 || mSrc[GID::Source::TPC] == 0 || mSrc[GID::Source::ITS] == 0) { LOG(fatal) << "We cannot do ITSTPC QC, some sources are missing, check sources in " << mSrc; } @@ -224,13 +282,16 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) static int evCount = 0; mRecoCont.collectData(ctx, *mDataRequest.get()); mTPCTracks = mRecoCont.getTPCTracks(); + mITSTracks = mRecoCont.getITSTracks(); mITSTPCTracks = mRecoCont.getTPCITSTracks(); LOG(debug) << "****** Number of found ITSTPC tracks = " << mITSTPCTracks.size(); LOG(debug) << "****** Number of found TPC tracks = " << mTPCTracks.size(); + LOG(debug) << "****** Number of found ITS tracks = " << mITSTracks.size(); - // cache selection for TPC tracks + // cache selection for TPC and ITS tracks std::vector isTPCTrackSelectedEntry(mTPCTracks.size(), false); + std::vector isITSTrackSelectedEntry(mITSTracks.size(), false); TrackCuts cuts; for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { @@ -244,28 +305,64 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) } } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trkIts = mITSTracks[itrk]; + o2::dataformats::GlobalTrackID id(itrk, GID::ITS); + if (cuts.isSelected(id, mRecoCont)) { + isITSTrackSelectedEntry[itrk] = true; + } + } + // numerator + eta, chi2... if (mUseMC) { - mMapLabels.clear(); + for (int i = 0; i < matchType::SIZE; ++i) { + mMapLabels[i].clear(); + } for (int itrk = 0; itrk < static_cast(mITSTPCTracks.size()); ++itrk) { auto const& trk = mITSTPCTracks[itrk]; auto idxTrkTpc = trk.getRefTPC().getIndex(); if (isTPCTrackSelectedEntry[idxTrkTpc] == true) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); - if (mMapLabels.find(lbl) == mMapLabels.end()) { + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) == mMapLabels[matchType::TPC].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapLabels[matchType::TPC].insert({lbl, {itrk, true}}); + } else { + mMapLabels[matchType::TPC].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest pt + if (mITSTPCTracks[mMapLabels[matchType::TPC].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + auto idxTrkIts = trk.getRefITS().getIndex(); + if (isITSTrackSelectedEntry[idxTrkIts] == true) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITSTPC}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) == mMapLabels[matchType::ITS].end()) { int source = lbl.getSourceID(); int event = lbl.getEventID(); const std::vector& pcontainer = mcReader.getTracks(source, event); const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { - mMapLabels.insert({lbl, {itrk, true}}); + mMapLabels[matchType::ITS].insert({lbl, {itrk, true}}); } else { - mMapLabels.insert({lbl, {itrk, false}}); + mMapLabels[matchType::ITS].insert({lbl, {itrk, false}}); } } else { // winner (if more tracks have the same label) has the highest pt - if (mITSTPCTracks[mMapLabels.at(lbl).mIdx].getPt() < trk.getPt()) { - mMapLabels.at(lbl).mIdx = itrk; + if (mITSTPCTracks[mMapLabels[matchType::ITS].at(lbl).mIdx].getPt() < trk.getPt()) { + mMapLabels[matchType::ITS].at(lbl).mIdx = itrk; } } } @@ -273,120 +370,296 @@ void MatchITSTPCQC::run(o2::framework::ProcessingContext& ctx) LOG(info) << "number of entries in map for nominator (without duplicates) = " << mMapLabels.size(); // now we use only the tracks in the map to fill the histograms (--> tracks have passed the // track selection and there are no duplicated tracks wrt the same MC label) - for (auto const& el : mMapLabels) { - auto const& trk = mITSTPCTracks[el.second.mIdx]; - auto const& trkTpc = mTPCTracks[trk.getRefTPC()]; - mPt->Fill(trkTpc.getPt()); - mPhi->Fill(trkTpc.getPhi()); - mPhiVsPt->Fill(trkTpc.getPt(), trkTpc.getPhi()); - mEta->Fill(trkTpc.getEta()); - mEtaVsPt->Fill(trkTpc.getPt(), trkTpc.getEta()); - // we fill also the denominator - mPtTPC->Fill(trkTpc.getPt()); - mPhiTPC->Fill(trkTpc.getPhi()); - mPhiVsPtTPC->Fill(trkTpc.getPt(), trkTpc.getPhi()); - mEtaTPC->Fill(trkTpc.getEta()); - mEtaVsPtTPC->Fill(trkTpc.getPt(), trkTpc.getEta()); - if (el.second.mIsPhysicalPrimary) { - mPtPhysPrim->Fill(trkTpc.getPt()); - mPhiPhysPrim->Fill(trkTpc.getPhi()); - mEtaPhysPrim->Fill(trkTpc.getEta()); + for (int i = 0; i < matchType::SIZE; ++i) { + for (auto const& el : mMapLabels[i]) { + auto const& trk = mITSTPCTracks[el.second.mIdx]; + o2::track::TrackParCov trkDen; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkDen = mTPCTracks[trk.getRefTPC()]; + } else { + trkDen = mITSTracks[trk.getRefITS()]; + if (std::abs(trkDen.getEta()) > 0.9) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + } + } + if (isEtaITSOk) { + mPtNum[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > 0.05) { + mPtNum_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiNum[i]->Fill(trkDen.getPhi()); + mPhiVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtDen[i]->Fill(trkDen.getPt()); + if (std::abs(trkDen.getEta()) > 0.05) { + mPtDen_noEta0[i]->Fill(trkDen.getPt()); + } + mPhiDen[i]->Fill(trkDen.getPhi()); + mPhiVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getPhi()); + m1OverPtDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + } + mEtaNum[i]->Fill(trkDen.getEta()); + mEtaVsPtNum[i]->Fill(trkDen.getPt(), trkDen.getEta()); // we fill also the denominator - mPtTPCPhysPrim->Fill(trkTpc.getPt()); - mPhiTPCPhysPrim->Fill(trkTpc.getPhi()); - mEtaTPCPhysPrim->Fill(trkTpc.getEta()); + mEtaDen[i]->Fill(trkDen.getEta()); + mEtaVsPtDen[i]->Fill(trkDen.getPt(), trkDen.getEta()); + if (el.second.mIsPhysicalPrimary) { + if (isEtaITSOk) { + mPtPhysPrimNum[i]->Fill(trkDen.getPt()); + mPhiPhysPrimNum[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimNum[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + // we fill also the denominator + mPtPhysPrimDen[i]->Fill(trkDen.getPt()); + mPhiPhysPrimDen[i]->Fill(trkDen.getPhi()); + m1OverPtPhysPrimDen[i]->Fill(trkDen.getSign() * trkDen.getPtInv()); + } + mEtaPhysPrimNum[i]->Fill(trkDen.getEta()); + // we fill also the denominator + mEtaPhysPrimDen[i]->Fill(trkDen.getEta()); + } + ++mNITSTPCSelectedTracks[i]; } - ++mNITSTPCSelectedTracks; } } - + int iITSTPC = 0; for (auto const& trk : mITSTPCTracks) { if (trk.getRefTPC().getIndex() >= mTPCTracks.size()) { - LOG(fatal) << "******************** ATTENTION! idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; - continue; + LOG(fatal) << "******************** ATTENTION! for TPC track associated to matched track: idx = " << trk.getRefTPC().getIndex() << ", size of container = " << mTPCTracks.size() << " in TF " << evCount; } - auto const& trkTpc = mTPCTracks[trk.getRefTPC()]; - auto idxTrkTpc = trk.getRefTPC().getIndex(); - if (isTPCTrackSelectedEntry[idxTrkTpc] == true) { - if (!mUseMC) { - mPt->Fill(trkTpc.getPt()); - mPhi->Fill(trkTpc.getPhi()); - mPhiVsPt->Fill(trkTpc.getPt(), trkTpc.getPhi()); - mEta->Fill(trkTpc.getEta()); - mEtaVsPt->Fill(trkTpc.getPt(), trkTpc.getEta()); + std::array title{"TPC", "ITS"}; + for (int i = 0; i < matchType::SIZE; ++i) { + o2::track::TrackParCov trkRef; + int idxTrkRef; + bool fillHisto = false; + bool isEtaITSOk = true; + if (i == matchType::TPC) { + trkRef = mTPCTracks[trk.getRefTPC()]; + idxTrkRef = trk.getRefTPC().getIndex(); + if (isTPCTrackSelectedEntry[idxTrkRef] == true) { + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } + } else { + idxTrkRef = trk.getRefITS().getIndex(); + if (trk.getRefITS().getSource() == GID::ITSAB) { + // do not use afterburner tracks + LOG(debug) << "Track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " is from afterburner"; + continue; + } + if (idxTrkRef >= mITSTracks.size()) { + LOG(fatal) << "******************** ATTENTION! for ITS track associated to matched track (NOT from AB): idx = " << trk.getRefITS().getIndex() << ", size of container = " << mITSTracks.size() << " in TF " << evCount; + } + trkRef = mITSTracks[trk.getRefITS()]; + LOG(debug) << "Checking track (ITS) with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " and pt = " << trkRef.getPt(); + if (isITSTrackSelectedEntry[idxTrkRef] == true) { + LOG(debug) << "Track was selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we keep it in the numerator, pt = " << trkRef.getPt(); + fillHisto = true; + ++mNITSTPCSelectedTracks[i]; + } else { + LOG(debug) << "Track was not selected (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + if (std::abs(trkRef.getEta()) > 0.9) { + // ITS track outside |eta | < 0.9, we don't fill pt, nor phi , nor phi vs pt histos + isEtaITSOk = false; + LOG(debug) << "Track (ITS), with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " will be discarded when filling pt of phi related histograms, since eta = " << trkRef.getEta() << " , we don't keep it in the numerator, pt = " << trkRef.getPt(); + } + } + if (fillHisto == true) { + if (!mUseMC) { + LOG(debug) << "Filling num (" << title[i] << ") with track with id " << idxTrkRef << " for ITSTPC track " << iITSTPC << " with pt = " << trkRef.getPt(); + if (isEtaITSOk) { + mPtNum[i]->Fill(trkRef.getPt()); + if (std::abs(trkRef.getEta()) > 0.05) { + mPtNum_noEta0[i]->Fill(trkRef.getPt()); + } + mPhiNum[i]->Fill(trkRef.getPhi()); + mPhiVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getPhi()); + m1OverPtNum[i]->Fill(trkRef.getSign() * trkRef.getPtInv()); + } + mEtaNum[i]->Fill(trkRef.getEta()); + mEtaVsPtNum[i]->Fill(trkRef.getPt(), trkRef.getEta()); + } + if (i == matchType::TPC) { + mResidualPt->Fill(trk.getPt(), trk.getPt() - trkRef.getPt()); + mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkRef.getPhi()); + mResidualEta->Fill(trk.getEta(), trk.getEta() - trkRef.getEta()); + mChi2Matching->Fill(trk.getChi2Match()); + mChi2Refit->Fill(trk.getChi2Refit()); + mTimeResVsPt->Fill(trkRef.getPt(), trk.getTimeMUS().getTimeStampError()); + math_utils::Point3D v{}; + std::array dca; + if (trkRef.propagateParamToDCA(v, mBz, &dca)) { + mDCAr->Fill(dca[0]); + } + LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); + } + } else { + LOG(debug) << "Not filling num (" << title[i] << ") for ITSTPC track " << iITSTPC << " for track with pt " << trkRef.getPt(); } - mResidualPt->Fill(trk.getPt(), trk.getPt() - trkTpc.getPt()); - mResidualPhi->Fill(trk.getPhi(), trk.getPhi() - trkTpc.getPhi()); - mResidualEta->Fill(trk.getEta(), trk.getEta() - trkTpc.getEta()); - mChi2Matching->Fill(trk.getChi2Match()); - mChi2Refit->Fill(trk.getChi2Refit()); - mTimeResVsPt->Fill(trkTpc.getPt(), trk.getTimeMUS().getTimeStampError()); - LOG(debug) << "*** chi2Matching = " << trk.getChi2Match() << ", chi2refit = " << trk.getChi2Refit() << ", timeResolution = " << trk.getTimeMUS().getTimeStampError(); - ++mNITSTPCSelectedTracks; } + ++iITSTPC; } // now filling the denominator for the efficiency calculation if (mUseMC) { - mMapTPCLabels.clear(); + for (int i = 0; i < matchType::SIZE; ++i) { + mMapRefLabels[i].clear(); + } // filling the map where we store for each MC label, the track id of the reconstructed // track with the highest number of TPC clusters for (int itrk = 0; itrk < static_cast(mTPCTracks.size()); ++itrk) { auto const& trk = mTPCTracks[itrk]; if (isTPCTrackSelectedEntry[itrk] == true) { auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::TPC}); - if (mMapLabels.find(lbl) != mMapLabels.end()) { + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::TPC].find(lbl) != mMapLabels[matchType::TPC].end()) { // the track was already added to the denominator continue; } - if (mMapTPCLabels.find(lbl) == mMapTPCLabels.end()) { + if (mMapRefLabels[matchType::TPC].find(lbl) == mMapRefLabels[matchType::TPC].end()) { int source = lbl.getSourceID(); int event = lbl.getEventID(); const std::vector& pcontainer = mcReader.getTracks(source, event); const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { - mMapTPCLabels.insert({lbl, {itrk, true}}); + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, true}}); } else { - mMapTPCLabels.insert({lbl, {itrk, false}}); + mMapRefLabels[matchType::TPC].insert({lbl, {itrk, false}}); } } else { // winner (if more tracks have the same label) has the highest number of TPC clusters - if (mTPCTracks[mMapTPCLabels.at(lbl).mIdx].getNClusters() < trk.getNClusters()) { - mMapTPCLabels.at(lbl).mIdx = itrk; + if (mTPCTracks[mMapRefLabels[matchType::TPC].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::TPC].at(lbl).mIdx = itrk; + } + } + } + } + // same for ITS + // filling the map where we store for each MC label, the track id of the reconstructed + // track with the highest number of ITS clusters + for (int itrk = 0; itrk < static_cast(mITSTracks.size()); ++itrk) { + auto const& trk = mITSTracks[itrk]; + if (isITSTrackSelectedEntry[itrk] == true) { + auto lbl = mRecoCont.getTrackMCLabel({(unsigned int)(itrk), GID::Source::ITS}); + if (!lbl.isValid()) { + continue; + } + if (mMapLabels[matchType::ITS].find(lbl) != mMapLabels[matchType::ITS].end()) { + // the track was already added to the denominator + continue; + } + if (mMapRefLabels[matchType::ITS].find(lbl) == mMapRefLabels[matchType::ITS].end()) { + int source = lbl.getSourceID(); + int event = lbl.getEventID(); + const std::vector& pcontainer = mcReader.getTracks(source, event); + const o2::MCTrack& p = pcontainer[lbl.getTrackID()]; + if (MCTrackNavigator::isPhysicalPrimary(p, pcontainer)) { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, true}}); + } else { + mMapRefLabels[matchType::ITS].insert({lbl, {itrk, false}}); + } + } else { + // winner (if more tracks have the same label) has the highest number of ITS clusters + if (mITSTracks[mMapRefLabels[matchType::ITS].at(lbl).mIdx].getNClusters() < trk.getNClusters()) { + mMapRefLabels[matchType::ITS].at(lbl).mIdx = itrk; } } } } - LOG(info) << "number of entries in map for denominator (without duplicates) = " << mMapTPCLabels.size() + mMapLabels.size(); + LOG(info) << "number of entries in map for denominator of TPC tracks (without duplicates) = " << mMapRefLabels[matchType::TPC].size() + mMapLabels[matchType::TPC].size(); + LOG(info) << "number of entries in map for denominator of ITS tracks (without duplicates) = " << mMapRefLabels[matchType::ITS].size() + mMapLabels[matchType::ITS].size(); // now we use only the tracks in the map to fill the histograms (--> tracks have passed the // track selection and there are no duplicated tracks wrt the same MC label) - for (auto const& el : mMapTPCLabels) { + for (auto const& el : mMapRefLabels[matchType::TPC]) { auto const& trk = mTPCTracks[el.second.mIdx]; - mPtTPC->Fill(trk.getPt()); - mPhiTPC->Fill(trk.getPhi()); - mPhiVsPtTPC->Fill(trk.getPt(), trk.getPhi()); - mEtaTPC->Fill(trk.getEta()); - mEtaVsPtTPC->Fill(trk.getPt(), trk.getEta()); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); if (el.second.mIsPhysicalPrimary) { - mPtTPCPhysPrim->Fill(trk.getPt()); - mPhiTPCPhysPrim->Fill(trk.getPhi()); - mEtaTPCPhysPrim->Fill(trk.getEta()); + mPtPhysPrimDen[matchType::TPC]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::TPC]->Fill(trk.getPhi()); + mEtaPhysPrimDen[matchType::TPC]->Fill(trk.getEta()); + m1OverPtPhysPrimDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); } ++mNTPCSelectedTracks; } + for (auto const& el : mMapRefLabels[matchType::ITS]) { + auto const& trk = mITSTracks[el.second.mIdx]; + if (std::abs(trk.getEta()) < 0.9) { + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + if (el.second.mIsPhysicalPrimary) { + if (std::abs(trk.getEta()) < 0.9) { + mPtPhysPrimDen[matchType::ITS]->Fill(trk.getPt()); + mPhiPhysPrimDen[matchType::ITS]->Fill(trk.getPhi()); + m1OverPtPhysPrimDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } + mEtaPhysPrimDen[matchType::ITS]->Fill(trk.getEta()); + } + ++mNITSSelectedTracks; + } } else { // if we are in data, we loop over all tracks (no check on the label) for (size_t itrk = 0; itrk < mTPCTracks.size(); ++itrk) { auto const& trk = mTPCTracks[itrk]; if (isTPCTrackSelectedEntry[itrk] == true) { - mPtTPC->Fill(trk.getPt()); - mPhiTPC->Fill(trk.getPhi()); - mPhiVsPtTPC->Fill(trk.getPt(), trk.getPhi()); - mEtaTPC->Fill(trk.getEta()); - mEtaVsPtTPC->Fill(trk.getPt(), trk.getEta()); + LOG(debug) << "Filling den (TPC) with track with pt = " << trk.getPt(); + mPtDen[matchType::TPC]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::TPC]->Fill(trk.getPt()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for den pt, phi, phi vs pt, 1.pt histos"; + } + mPhiDen[matchType::TPC]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getPhi()); + mEtaDen[matchType::TPC]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::TPC]->Fill(trk.getPt(), trk.getEta()); + m1OverPtDen[matchType::TPC]->Fill(trk.getSign() * trk.getPtInv()); ++mNTPCSelectedTracks; } } + for (size_t itrk = 0; itrk < mITSTracks.size(); ++itrk) { + auto const& trk = mITSTracks[itrk]; + LOG(debug) << "Checking den for track (ITS) " << itrk << " with pt " << trk.getPt() << " and eta = " << trk.getEta(); + if (isITSTrackSelectedEntry[itrk] == true) { + if (std::abs(trk.getEta()) < 0.9) { + LOG(debug) << "Filling den for track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta(); + mPtDen[matchType::ITS]->Fill(trk.getPt()); + if (std::abs(trk.getEta()) > 0.05) { + mPtDen_noEta0[matchType::ITS]->Fill(trk.getPt()); + } + mPhiDen[matchType::ITS]->Fill(trk.getPhi()); + mPhiVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getPhi()); + m1OverPtDen[matchType::ITS]->Fill(trk.getSign() * trk.getPtInv()); + } else { + LOG(debug) << "Track (ITS) " << itrk << " with pt = " << trk.getPt() << " and eta = " << trk.getEta() << " not used for num pt, phi, phi vs pt, 1.pt histos"; + } + mEtaDen[matchType::ITS]->Fill(trk.getEta()); + mEtaVsPtDen[matchType::ITS]->Fill(trk.getPt(), trk.getEta()); + ++mNITSSelectedTracks; + } else { + LOG(debug) << "Not filling for this track (ITS) " << itrk << " with pt = " << trk.getPt(); + } + } } evCount++; } @@ -420,36 +693,48 @@ bool MatchITSTPCQC::selectTrack(o2::tpc::TrackTPC const& track) void MatchITSTPCQC::finalize() { + std::array title{"TPC", "ITS"}; + // first we use denominators and nominators to set the TEfficiency; later they are scaled - for (int i = 0; i < mPtTPC->GetNbinsX(); ++i) { - if (mPtTPC->GetBinContent(i + 1) < mPt->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mPtTPC[i] = " << mPtTPC->GetBinContent(i + 1) << ", mPt[i] = " << mPt->GetBinContent(i + 1); + // some checks + for (int i = 0; i < matchType::SIZE; ++i) { + for (int i = 0; i < mPtDen[i]->GetNbinsX(); ++i) { + if (mPtDen[i]->GetBinContent(i + 1) < mPtNum[i]->GetBinContent(i + 1)) { + LOG(error) << title[i] << ": bin " << i + 1 << " in [" << mPtNum[i]->GetBinLowEdge(i + 1) << " , " << mPtNum[i]->GetBinLowEdge(i + 1) + mPtNum[i]->GetBinWidth(i + 1) << "]: mPtDen[i] = " << mPtDen[i]->GetBinContent(i + 1) << ", mPtNum[i] = " << mPtNum[i]->GetBinContent(i + 1); + } } - } - for (int i = 0; i < mPhiTPC->GetNbinsX(); ++i) { - if (mPhiTPC->GetBinContent(i + 1) < mPhi->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mPhiTPC[i] = " << mPhiTPC->GetBinContent(i + 1) << ", mPhi[i] = " << mPhi->GetBinContent(i + 1); + for (int i = 0; i < mPtDen_noEta0[i]->GetNbinsX(); ++i) { + if (mPtDen_noEta0[i]->GetBinContent(i + 1) < mPtNum_noEta0[i]->GetBinContent(i + 1)) { + LOG(error) << title[i] << ": bin " << i + 1 << " in [" << mPtNum_noEta0[i]->GetBinLowEdge(i + 1) << " , " << mPtNum_noEta0[i]->GetBinLowEdge(i + 1) + mPtNum_noEta0[i]->GetBinWidth(i + 1) << "]: mPtDen_noEta0[i] = " << mPtDen_noEta0[i]->GetBinContent(i + 1) << ", mPtNum_noEta0[i] = " << mPtNum_noEta0[i]->GetBinContent(i + 1); + } } - } - for (int i = 0; i < mEtaTPC->GetNbinsX(); ++i) { - if (mEtaTPC->GetBinContent(i + 1) < mEta->GetBinContent(i + 1)) { - LOG(error) << "bin " << i + 1 << ": mEtaTPC[i] = " << mEtaTPC->GetBinContent(i + 1) << ", mEta[i] = " << mEta->GetBinContent(i + 1); + for (int i = 0; i < mPhiDen[i]->GetNbinsX(); ++i) { + if (mPhiDen[i]->GetBinContent(i + 1) < mPhiNum[i]->GetBinContent(i + 1)) { + LOG(error) << title[i] << ": bin " << i + 1 << " in [" << mPhiNum[i]->GetBinLowEdge(i + 1) << " , " << mPhiNum[i]->GetBinLowEdge(i + 1) + mPhiNum[i]->GetBinWidth(i + 1) << "]: mPhiDen[i] = " << mPhiDen[i]->GetBinContent(i + 1) << ", mPhiNum[i] = " << mPhiNum[i]->GetBinContent(i + 1); + } + } + for (int i = 0; i < mEtaDen[i]->GetNbinsX(); ++i) { + if (mEtaDen[i]->GetBinContent(i + 1) < mEtaNum[i]->GetBinContent(i + 1)) { + LOG(error) << title[i] << ": bin " << i + 1 << " in [" << mEtaNum[i]->GetBinLowEdge(i + 1) << " , " << mEtaNum[i]->GetBinLowEdge(i + 1) + mEtaNum[i]->GetBinWidth(i + 1) << "]: mEtaDen[i] = " << mEtaDen[i]->GetBinContent(i + 1) << ", mEtaNum[i] = " << mEtaNum[i]->GetBinContent(i + 1); + } } - } - // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents - setEfficiency(mFractionITSTPCmatch, mPt, mPtTPC); - setEfficiency(mFractionITSTPCmatchPhi, mPhi, mPhiTPC); - setEfficiency(mFractionITSTPCmatchEta, mEta, mEtaTPC); - setEfficiency(mFractionITSTPCmatchPhiVsPt, mPhiVsPt, mPhiVsPtTPC, true); - setEfficiency(mFractionITSTPCmatchEtaVsPt, mEtaVsPt, mEtaVsPtTPC, true); - if (mUseMC) { - setEfficiency(mFractionITSTPCmatchPhysPrim, mPtPhysPrim, mPtTPCPhysPrim); - setEfficiency(mFractionITSTPCmatchPhiPhysPrim, mPhiPhysPrim, mPhiTPCPhysPrim); - setEfficiency(mFractionITSTPCmatchEtaPhysPrim, mEtaPhysPrim, mEtaTPCPhysPrim); + // filling the efficiency + setEfficiency(mFractionITSTPCmatch[i], mPtNum[i], mPtDen[i]); + setEfficiency(mFractionITSTPCmatch_noEta0[i], mPtNum_noEta0[i], mPtDen_noEta0[i]); + setEfficiency(mFractionITSTPCmatchPhi[i], mPhiNum[i], mPhiDen[i]); + setEfficiency(mFractionITSTPCmatchEta[i], mEtaNum[i], mEtaDen[i]); + setEfficiency(mFractionITSTPCmatchPhiVsPt[i], mPhiVsPtNum[i], mPhiVsPtDen[i], true); + setEfficiency(mFractionITSTPCmatchEtaVsPt[i], mEtaVsPtNum[i], mEtaVsPtDen[i], true); + setEfficiency(mFractionITSTPCmatch1OverPt[i], m1OverPtNum[i], m1OverPtDen[i]); + if (mUseMC) { + setEfficiency(mFractionITSTPCmatchPhysPrim[i], mPtPhysPrimNum[i], mPtPhysPrimDen[i]); + setEfficiency(mFractionITSTPCmatchPhiPhysPrim[i], mPhiPhysPrimNum[i], mPhiPhysPrimDen[i]); + setEfficiency(mFractionITSTPCmatchEtaPhysPrim[i], mEtaPhysPrimNum[i], mEtaPhysPrimDen[i]); + setEfficiency(mFractionITSTPCmatchPhysPrim1OverPt[i], m1OverPtPhysPrimNum[i], m1OverPtPhysPrimDen[i]); + } } - /* mPtTPC->Scale(scaleFactTPC); mPt->Scale(scaleFactITSTPC); @@ -493,6 +778,7 @@ void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool i } } } + // we need to force to replace the total histogram, otherwise it will compare it to the previous passed one, and it might get an error of inconsistency in the bin contents if (!eff->SetTotalHistogram(*hden, "f")) { LOG(fatal) << "Something went wrong when defining the efficiency denominator " << eff->GetName() << " from " << hnum->GetName(); } @@ -511,30 +797,56 @@ void MatchITSTPCQC::setEfficiency(TEfficiency* eff, TH1* hnum, TH1* hden, bool i void MatchITSTPCQC::getHistos(TObjArray& objar) { - objar.Add(mPtTPC); - objar.Add(mFractionITSTPCmatch); - objar.Add(mPt); - objar.Add(mPhiTPC); - objar.Add(mPhiVsPtTPC); - objar.Add(mFractionITSTPCmatchPhi); - objar.Add(mPhi); - objar.Add(mPhiVsPt); - objar.Add(mPtTPCPhysPrim); - objar.Add(mFractionITSTPCmatchPhysPrim); - objar.Add(mPtPhysPrim); - objar.Add(mPhiTPCPhysPrim); - objar.Add(mFractionITSTPCmatchPhiPhysPrim); - objar.Add(mPhiPhysPrim); - objar.Add(mEta); - objar.Add(mEtaVsPt); + for (int i = 0; i < matchType::SIZE; ++i) { + objar.Add(mPtNum[i]); + objar.Add(mPtDen[i]); + objar.Add(mFractionITSTPCmatch[i]); + + objar.Add(mPtNum_noEta0[i]); + objar.Add(mPtDen_noEta0[i]); + objar.Add(mFractionITSTPCmatch_noEta0[i]); + + objar.Add(mPtPhysPrimNum[i]); + objar.Add(mPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim[i]); + + objar.Add(mPhiNum[i]); + objar.Add(mPhiDen[i]); + objar.Add(mFractionITSTPCmatchPhi[i]); + + objar.Add(mPhiPhysPrimNum[i]); + objar.Add(mPhiPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhiPhysPrim[i]); + + objar.Add(mPhiVsPtNum[i]); + objar.Add(mPhiVsPtDen[i]); + objar.Add(mFractionITSTPCmatchPhiVsPt[i]); + + objar.Add(mEtaNum[i]); + objar.Add(mEtaDen[i]); + objar.Add(mFractionITSTPCmatchEta[i]); + + objar.Add(mEtaPhysPrimNum[i]); + objar.Add(mEtaPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchEtaPhysPrim[i]); + + objar.Add(mEtaVsPtNum[i]); + objar.Add(mEtaVsPtDen[i]); + objar.Add(mFractionITSTPCmatchEtaVsPt[i]); + + objar.Add(m1OverPtNum[i]); + objar.Add(m1OverPtDen[i]); + objar.Add(mFractionITSTPCmatch1OverPt[i]); + + objar.Add(m1OverPtPhysPrimNum[i]); + objar.Add(m1OverPtPhysPrimDen[i]); + objar.Add(mFractionITSTPCmatchPhysPrim1OverPt[i]); + } objar.Add(mChi2Matching); objar.Add(mChi2Refit); objar.Add(mTimeResVsPt); - objar.Add(mEtaPhysPrim); - objar.Add(mEtaTPC); - objar.Add(mEtaVsPtTPC); - objar.Add(mEtaTPCPhysPrim); objar.Add(mResidualPt); objar.Add(mResidualPhi); objar.Add(mResidualEta); + objar.Add(mDCAr); } diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index 9cebaa73e24df..3283f14b0bdde 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -53,11 +53,16 @@ using Cluster = o2::tof::Cluster; using GTrackID = o2::dataformats::GlobalTrackID; using timeEst = o2::dataformats::TimeStampWithError; +bool MatchTOF::mHasFillScheme = false; +bool MatchTOF::mFillScheme[o2::constants::lhc::LHCMaxBunches] = {0}; + ClassImp(MatchTOF); //______________________________________________ -void MatchTOF::run(const o2::globaltracking::RecoContainer& inp) +void MatchTOF::run(const o2::globaltracking::RecoContainer& inp, unsigned long firstTForbit) { + mFirstTForbit = firstTForbit; + if (!mMatchParams) { mMatchParams = &o2::globaltracking::MatchTOFParams::Instance(); mSigmaTimeCut = mMatchParams->nsigmaTimeCut; @@ -1183,24 +1188,42 @@ void MatchTOF::doMatchingForTPC(int sec) return; } //______________________________________________ -int MatchTOF::findFITIndex(int bc, const gsl::span& FITRecPoints) +int MatchTOF::findFITIndex(int bc, const gsl::span& FITRecPoints, unsigned long firstOrbit) { + if ((!mHasFillScheme) && o2::tof::Utils::hasFillScheme()) { + mHasFillScheme = true; + for (int ibc = 0; ibc < o2::tof::Utils::getNinteractionBC(); ibc++) { + mFillScheme[o2::tof::Utils::getInteractionBC(ibc)] = true; + } + } + if (FITRecPoints.size() == 0) { return -1; } int index = -1; - int distMax = 5; // require bc distance below 5 + int distMax = 0; + bool bestQuality = false; // prioritize FT0 BC with good quality (FT0-AC + vertex) to remove umbiguity in Pb-Pb (not a strict cut because inefficient in pp) + const int distThr = 8; for (unsigned int i = 0; i < FITRecPoints.size(); i++) { const o2::InteractionRecord ir = FITRecPoints[i].getInteractionRecord(); - int bct0 = ir.orbit * o2::constants::lhc::LHCMaxBunches + ir.bc; + if (mHasFillScheme && !mFillScheme[ir.bc]) { + continue; + } + bool quality = (fabs(FITRecPoints[i].getCollisionTime(0)) < 1000 && fabs(FITRecPoints[i].getVertex()) < 1000); + if (bestQuality && !quality) { // if current has no good quality and the one previoulsy selected has -> discard the current one + continue; + } + int bct0 = (ir.orbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; int dist = bc - bct0; - if (dist < 0 || dist > distMax) { + bool worseDistance = dist < 0 || dist > distThr || dist < distMax; + if (worseDistance && (!quality || bestQuality)) { // discard if BC is later than the one selected, but is has a better quality continue; } + bestQuality = quality; distMax = dist; index = i; } @@ -1254,14 +1277,15 @@ void MatchTOF::BestMatches(std::vector& match const o2::track::TrackLTIntegral& intLT = matchingPair.getLTIntegralOut(); - if (FITRecPoints.size() > 0) { - int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC(), FITRecPoints); + if (FITRecPoints.size() > 0 && mIsFIT) { + int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC() - o2::tof::Geo::LATENCYWINDOW_IN_BC, FITRecPoints, mFirstTForbit); if (index > -1) { o2::InteractionRecord ir = FITRecPoints[index].getInteractionRecord(); - t0info = ir.bc2ns() * 1E3; + int bct0 = (ir.orbit - mFirstTForbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; + t0info = bct0 * o2::tof::Geo::BC_TIME_INPS; } - } else { // move time to time in orbit to avoid loss of precision when truncating from double to float + } else if (!mIsFIT) { // move time to time in orbit to avoid loss of precision when truncating from double to float int bcStarOrbit = int((TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - intLT.getTOF(o2::track::PID::Pion)) * o2::tof::Geo::BC_TIME_INPS_INV); bcStarOrbit = (bcStarOrbit / o2::constants::lhc::LHCMaxBunches) * o2::constants::lhc::LHCMaxBunches; // truncation t0info = bcStarOrbit * o2::tof::Geo::BC_TIME_INPS; @@ -1280,7 +1304,14 @@ void MatchTOF::BestMatches(std::vector& match } int mask = 0; - float deltat = o2::tof::Utils::subtractInteractionBC(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion), mask, true); + float deltat; + + if (t0info > 0 && mIsFIT) { // FT0 BC found + deltat = TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion) - o2::tof::Geo::LATENCYWINDOW_IN_BC * o2::tof::Geo::BC_TIME_INPS; // latency subtracted + mask = 1 << 8; // only FT0 BC allowed (-> BC=0 since t0info already subtracted and assumed to be aligned to the true IntBC) + } else if (!mIsFIT) { + deltat = o2::tof::Utils::subtractInteractionBC(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - t0info - intLT.getTOF(o2::track::PID::Pion), mask, true); + } const o2::track::TrackParCov& trc = TracksWork[trkType][itrk].first; float pt = trc.getPt(); // from outer parameters! @@ -1301,7 +1332,7 @@ void MatchTOF::BestMatches(std::vector& match flags = flags | o2::dataformats::CalibInfoTOF::kMultiHit; } - if (matchingPair.getChi2() < calibMaxChi2) { // extra cut in ChiSquare for storing calib info + if (matchingPair.getChi2() < calibMaxChi2 && t0info > 0) { // extra cut in ChiSquare for storing calib info CalibInfoTOF.emplace_back(TOFClusWork[matchingPair.getTOFClIndex()].getMainContributingChannel(), Timestamp / 1000 + int(TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() * 1E-12), // add time stamp deltat, @@ -1394,12 +1425,13 @@ void MatchTOF::BestMatchesHP(std::vector& mat // get fit info double t0info = 0; - if (FITRecPoints.size() > 0) { - int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC(), FITRecPoints); + if (FITRecPoints.size() > 0 && mIsFIT) { + int index = findFITIndex(TOFClusWork[matchingPair.getTOFClIndex()].getBC() - o2::tof::Geo::LATENCYWINDOW_IN_BC, FITRecPoints, mFirstTForbit); if (index > -1) { o2::InteractionRecord ir = FITRecPoints[index].getInteractionRecord(); - t0info = ir.bc2ns() * 1E3; + int bct0 = (ir.orbit - mFirstTForbit) * o2::constants::lhc::LHCMaxBunches + ir.bc; + t0info = bct0 * o2::tof::Geo::BC_TIME_INPS; } } else { // move time to time in orbit to avoid loss of precision when truncating from double to float int bcStarOrbit = int((TOFClusWork[matchingPair.getTOFClIndex()].getTimeRaw() - intLT.getTOF(o2::track::PID::Pion)) * o2::tof::Geo::BC_TIME_INPS_INV); diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index c913571038129..3f22b9da176f6 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -65,7 +65,13 @@ MatchTPCITS::MatchTPCITS() = default; MatchTPCITS::~MatchTPCITS() = default; //______________________________________________ -void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) +void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp, + pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib) { ///< perform matching for provided input if (!mInitDone) { @@ -83,8 +89,8 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) break; } if (mVDriftCalibOn) { // in the beginning of the output vector we send the full and reference VDrift used for this TF - mTglITSTPC.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, -999.); - mTglITSTPC.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); + calib.emplace_back(mTPCVDrift, mTPCDrift.refVDrift, -999.); + calib.emplace_back(mTPCDriftTimeOffset, mTPCDrift.refTimeOffset, -999.); } mTimer[SWDoMatching].Start(false); @@ -101,12 +107,16 @@ void MatchTPCITS::run(const o2::globaltracking::RecoContainer& inp) selectBestMatches(); - refitWinners(); - + bool fullMatchRefitDone = false; if (mUseFT0 && Params::Instance().runAfterBurner) { - runAfterBurner(); + fullMatchRefitDone = runAfterBurner(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib); + } + if (!fullMatchRefitDone) { + refitWinners(matchedTracks, matchLabels, calib); // it afterburner is active, full matches refit will be done by it + } + if (mParams->verbosity > 0) { + reportSizes(matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); } - #ifdef _ALLOW_DEBUG_TREES_ if (mDBGOut && isDebugFlag(WinnerMatchesTree)) { dumpWinnerMatches(); @@ -139,21 +149,17 @@ void MatchTPCITS::clear() mMatchRecordsTPC.clear(); mMatchRecordsITS.clear(); mWinnerChi2Refit.clear(); - mMatchedTracks.clear(); mITSWork.clear(); mTPCWork.clear(); mInteractions.clear(); mITSROFTimes.clear(); mITSTrackROFContMapping.clear(); mITSClustersArray.clear(); + mITSClusterSizes.clear(); mTPCABSeeds.clear(); mTPCABIndexCache.clear(); mABWinnersIDs.clear(); mABClusterLinkIndex.clear(); - mABTrackletRefs.clear(); - mABTrackletClusterIDs.clear(); - mABTrackletLabels.clear(); - mTglITSTPC.clear(); mNMatchesControl = 0; for (int sec = o2::constants::math::NSectors; sec--;) { @@ -164,9 +170,8 @@ void MatchTPCITS::clear() } if (mMCTruthON) { - mOutLabels.clear(); - mITSROFTimes.clear(); mTPCLblWork.clear(); + mITSLblWork.clear(); } } @@ -263,8 +268,9 @@ void MatchTPCITS::selectBestMatches() { ///< loop over match records and select the ones with best chi2 mTimer[SWSelectBest].Start(false); - int nValidated = 0, iter = 0, nValidatedTotal = 0; - + int nValidated = 0, iter = 0; + mNMatches = 0; + mNCalibPrelim = 0; do { nValidated = 0; int ntpc = mTPCWork.size(), nremaining = 0; @@ -276,6 +282,9 @@ void MatchTPCITS::selectBestMatches() nremaining++; if (validateTPCMatch(it)) { nValidated++; + if (mVDriftCalibOn && (!mFieldON || std::abs(tTPC.getQ2Pt()) < mParams->maxVDriftTrackQ2Pt)) { + mNCalibPrelim++; + } continue; } } @@ -283,31 +292,45 @@ void MatchTPCITS::selectBestMatches() LOGP(info, "iter {}: Validated {} of {} remaining matches", iter, nValidated, nremaining); } iter++; - nValidatedTotal += nValidated; + mNMatches += nValidated; } while (nValidated); mTimer[SWSelectBest].Stop(); - LOGP(info, "Validated {} matches out of {} for {} TPC and {} ITS tracks in {} iterations", nValidatedTotal, mNMatchesControl, mTPCWork.size(), mITSWork.size(), iter); + LOGP(info, "Validated {} matches out of {} for {} TPC and {} ITS tracks in {} iterations", mNMatches, mNMatchesControl, mTPCWork.size(), mITSWork.size(), iter); } //______________________________________________ bool MatchTPCITS::validateTPCMatch(int iTPC) { - const auto& tTPC = mTPCWork[iTPC]; + auto& tTPC = mTPCWork[iTPC]; auto& rcTPC = mMatchRecordsTPC[tTPC.matchID]; // best TPC->ITS match - /* // should never happen - if (rcTPC.nextRecID == Validated) { - LOG(warning) << "TPC->ITS was already validated"; - return false; // RS do we need this - } - */ // check if it is consistent with corresponding ITS->TPC match auto& tITS = mITSWork[rcTPC.partnerID]; // partner ITS track auto& rcITS = mMatchRecordsITS[tITS.matchID]; // best ITS->TPC match record if (rcITS.nextRecID == Validated) { return false; } - if (rcITS.partnerID == iTPC) { // is best matching TPC track for this ITS track actually iTPC? + if (rcITS.partnerID == iTPC) { // is best matching TPC track for this ITS track actually iTPC? + int cloneID = tITS.getCloneShift(); // check if there is a clone of tITS + while (cloneID) { + cloneID += rcTPC.partnerID; + auto& tITSClone = mITSWork[cloneID]; + if (isDisabledITS(tITSClone)) { // ignore clone + break; + } + int nextITSCloneMatchID = tITSClone.matchID; + if (rcITS.isBetter(mMatchRecordsITS[nextITSCloneMatchID])) { // best ITS->TPC match record for the clone is worse than the rcITS + LOGP(debug, "Suppressing clone cloneID={} of winner clone {} of source ITS {}", cloneID, rcTPC.partnerID, tITS.sourceID); + while (nextITSCloneMatchID > MinusOne) { + auto& rcITSClone = mMatchRecordsITS[nextITSCloneMatchID]; + removeITSfromTPC(cloneID, rcITSClone.partnerID); + nextITSCloneMatchID = rcITSClone.nextRecID; + } + tITSClone.matchID = MinusTen; // disable + break; + } + return false; // ignore match at this iteration + } // unlink winner TPC track from all ITS candidates except winning one int nextTPC = rcTPC.nextRecID; while (nextTPC > MinusOne) { @@ -326,6 +349,7 @@ bool MatchTPCITS::validateTPCMatch(int iTPC) nextITS = rcITSrem.nextRecID; } rcITS.nextRecID = Validated; + tTPC.gid.setBit(0); // Flag full match return true; } return false; @@ -429,14 +453,14 @@ bool MatchTPCITS::prepareTPCData() mTPCTrkLabels = inp.getTPCTracksMCLabels(); } - int ntr = mTPCTracksArray.size(); - mMatchRecordsTPC.reserve(mParams->maxMatchCandidates * ntr); // number of records might be actually more than N tracks! - mTPCWork.reserve(ntr); + int ntr = mTPCTracksArray.size(), ntrW = 0.7 * ntr; + mMatchRecordsTPC.reserve(mParams->maxMatchCandidates * ntrW); // number of records might be actually more than N tracks! + mTPCWork.reserve(ntrW); if (mMCTruthON) { - mTPCLblWork.reserve(ntr); + mTPCLblWork.reserve(ntrW); } for (int sec = o2::constants::math::NSectors; sec--;) { - mTPCSectIndexCache[sec].reserve(100 + 1.2 * ntr / o2::constants::math::NSectors); + mTPCSectIndexCache[sec].reserve(100 + 1.2 * ntrW / o2::constants::math::NSectors); } auto creator = [this](auto& trk, GTrackID gid, float time0, float terr) { @@ -557,6 +581,27 @@ bool MatchTPCITS::prepareITSData() auto pattIt = patterns.begin(); mITSClustersArray.reserve(clusITS.size()); o2::its::ioutils::convertCompactClusters(clusITS, pattIt, mITSClustersArray, mITSDict); + + // ITS clusters sizes + mITSClusterSizes.reserve(clusITS.size()); + auto pattIt2 = patterns.begin(); + for (auto& clus : clusITS) { + auto pattID = clus.getPatternID(); + unsigned int npix; + if (pattID == o2::itsmft::CompCluster::InvalidPatternID || mITSDict->isGroup(pattID)) { + o2::itsmft::ClusterPattern patt; + patt.acquirePattern(pattIt2); + npix = patt.getNPixels(); + } else { + npix = mITSDict->getNpixels(pattID); + } + if (npix < 255) { + mITSClusterSizes.push_back(npix); + } else { + mITSClusterSizes.push_back(255); + } + } + if (mMCTruthON) { mITSClsLabels = inp.mcITSClusters.get(); } @@ -1148,6 +1193,9 @@ void MatchTPCITS::addLastTrackCloneForNeighbourSector(int sector) o2::base::Propagator::Instance()->PropagateToXBxByBz(trc, mParams->XMatchingRef, MaxSnp, 2., MatCorrType::USEMatCorrNONE)) { // TODO: use faster prop here, no 3d field, materials mITSSectIndexCache[sector].push_back(mITSWork.size() - 1); // register track CLONE + // flag clone + mITSWork.back().setCloneBefore(); + mITSWork[mITSWork.size() - 2].setCloneAfter(); if (mMCTruthON) { mITSLblWork.emplace_back(mITSTrkLabels[trc.sourceID]); } @@ -1231,56 +1279,61 @@ void MatchTPCITS::print() const } //______________________________________________ -void MatchTPCITS::refitWinners() +void MatchTPCITS::refitWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib) { ///< refit winning tracks - mTimer[SWRefit].Start(false); + matchedTracks.reserve(mNMatches + mABWinnersIDs.size()); + if (mMCTruthON) { + matchLabels.reserve(mNMatches + mABWinnersIDs.size()); + } + if (mVDriftCalibOn) { + calib.reserve(mNCalibPrelim * 1.2 + 1); + } + LOG(debug) << "Refitting winner matches"; mWinnerChi2Refit.resize(mITSWork.size(), -1.f); int iITS; for (int iTPC = 0; iTPC < (int)mTPCWork.size(); iTPC++) { - if (!refitTrackTPCITS(iTPC, iITS)) { + const auto& tTPC = mTPCWork[iTPC]; + if (isDisabledTPC(tTPC) || !tTPC.gid.testBit(0) || !refitTrackTPCITS(iTPC, iITS, matchedTracks, matchLabels, calib)) { continue; } - mWinnerChi2Refit[iITS] = mMatchedTracks.back().getChi2Refit(); + mWinnerChi2Refit[iITS] = matchedTracks.back().getChi2Refit(); } mTimer[SWRefit].Stop(); } //______________________________________________ -bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) +bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS, pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector>& calib) { ///< refit in inward direction the pair of TPC and ITS tracks const float maxStep = 2.f; // max propagation step (TODO: tune) const auto& tTPC = mTPCWork[iTPC]; - if (isDisabledTPC(tTPC)) { - return false; // no match - } const auto& tpcMatchRec = mMatchRecordsTPC[tTPC.matchID]; iITS = tpcMatchRec.partnerID; const auto& tITS = mITSWork[iITS]; const auto& itsTrOrig = mITSTracksArray[tITS.sourceID]; - mMatchedTracks.emplace_back(tTPC, tITS); // create a copy of TPC track at xRef - auto& trfit = mMatchedTracks.back(); + auto& trfit = matchedTracks.emplace_back(tTPC, tITS); // create a copy of TPC track at xRef + trfit.getParamOut().setUserField(0); // reset eventual clones flag // in continuos mode the Z of TPC track is meaningless, unless it is CE crossing // track (currently absent, TODO) if (!mCompareTracksDZ) { trfit.setZ(tITS.getZ()); // fix the seed Z } - float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus - float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time + float deltaT = (trfit.getZ() - tTPC.getZ()) * mTPCVDriftInv; // time correction in \mus + float timeErr = tTPC.constraint == TrackLocTPC::Constrained ? tTPC.timeErr : std::sqrt(tITS.getSigmaZ2() + tTPC.getSigmaZ2()) * mTPCVDriftInv; // estimate the error on time if (timeErr > mITSTimeResMUS && tTPC.constraint != TrackLocTPC::Constrained) { timeErr = mITSTimeResMUS; // chose smallest error deltaT = tTPC.constraint == TrackLocTPC::ASide ? tITS.tBracket.mean() - tTPC.time0 : tTPC.time0 - tITS.tBracket.mean(); } timeErr += mParams->globalTimeExtraErrorMUS; - float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS; /// precise time estimate, optionally corrected for bias - if (timeC < 0) { // RS TODO similar check is needed for other edge of TF + float timeC = tTPC.getCorrectedTime(deltaT) + mParams->globalTimeBiasMUS; /// precise time estimate, optionally corrected for bias + if (timeC < 0) { // RS TODO similar check is needed for other edge of TF if (timeC + std::min(timeErr, mParams->tfEdgeTimeToleranceMUS * mTPCTBinMUSInv) < 0) { - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } timeC = 0.; @@ -1321,7 +1374,7 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) if (nclRefit != ncl) { LOGP(debug, "Refit in ITS failed after ncl={}, match between TPC track #{} and ITS track #{}", nclRefit, tTPC.sourceID, tITS.sourceID); LOGP(debug, "{:s}", trfit.asString()); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } @@ -1343,7 +1396,7 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) || !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } if (mVDriftCalibOn) { @@ -1356,13 +1409,13 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) LOGP(alarm, "Impossible imposed timebin {} for TPC track time0:{}, dBwd:{} dFwd:{} TB | ZShift:{}, TShift:{}", tImposed, mTPCTracksArray[tTPC.sourceID].getTime0(), mTPCTracksArray[tTPC.sourceID].getDeltaTBwd(), mTPCTracksArray[tTPC.sourceID].getDeltaTFwd(), trfit.getZ() - tTPC.getZ(), deltaT); LOGP(info, "Trc: {}", mTPCTracksArray[tTPC.sourceID].asString()); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), tImposed, &chi2Out, true, false); // outward refit if (retVal < 0) { LOG(debug) << "Refit failed"; - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } auto posEnd = tracOut.getXYZGlo(); @@ -1396,7 +1449,7 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) trfit.setRefITS({unsigned(tITS.sourceID), o2::dataformats::GlobalTrackID::ITS}); if (mMCTruthON) { // store MC info: we assign TPC track label and declare the match fake if the ITS and TPC labels are different (their fake flag is ignored) - auto& lbl = mOutLabels.emplace_back(mTPCLblWork[iTPC]); + auto& lbl = matchLabels.emplace_back(mTPCLblWork[iTPC]); lbl.setFakeFlag(mITSLblWork[iITS] != mTPCLblWork[iTPC]); } @@ -1428,7 +1481,7 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) } } if (fillVDCalib) { - mTglITSTPC.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0); + calib.emplace_back(tITS.getTgl(), tTPC.getTgl(), minDiffFT0); } #ifdef _ALLOW_DEBUG_TREES_ if (mDBGOut) { @@ -1449,14 +1502,14 @@ bool MatchTPCITS::refitTrackTPCITS(int iTPC, int& iITS) } //______________________________________________ -bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) +bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed, pmr::vector& matchedTracks, pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs) { ///< refit AfterBurner track const float maxStep = 2.f; // max propagation step (TODO: tune) const auto& tTPC = mTPCWork[seed.tpcWID]; const auto& winLink = seed.getLink(seed.winLinkID); - auto& newtr = mMatchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster + auto& newtr = matchedTracks.emplace_back(winLink, winLink); // create a copy of winner param at innermost ITS cluster auto& tracOut = newtr.getParamOut(); auto& tofL = newtr.getLTIntegralOut(); auto geom = o2::its::GeometryTGeo::Instance(); @@ -1465,13 +1518,13 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) propagator->estimateLTFast(tofL, winLink); // guess about initial value for the track integral from the origin // refit track outward in the ITS - const auto& itsClRefs = mABTrackletRefs[iITSAB]; + const auto& itsClRefs = ABTrackletRefs[iITSAB]; int nclRefit = 0, ncl = itsClRefs.getNClusters(); float chi2 = 0.f; // NOTE: the ITS cluster absolute indices are stored from inner to outer layers for (int icl = itsClRefs.getFirstEntry(); icl < itsClRefs.getEntriesBound(); icl++) { - const auto& clus = mITSClustersArray[mABTrackletClusterIDs[icl]]; + const auto& clus = mITSClustersArray[ABTrackletClusterIDs[icl]]; float alpha = geom->getSensorRefAlpha(clus.getSensorID()), x = clus.getX(); if (!tracOut.rotate(alpha) || // note: here we also calculate the L,T integral @@ -1489,7 +1542,7 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) if (nclRefit != ncl) { LOGP(debug, "AfterBurner refit in ITS failed after ncl={}, match between TPC track #{} and ITS tracklet #{}", nclRefit, tTPC.sourceID, iITSAB); LOGP(debug, "{:s}", tracOut.asString()); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } // perform TPC refit with interaction time constraint @@ -1500,7 +1553,7 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) if (!tracOut.getXatLabR(o2::constants::geom::XTPCInnerRef, xtogo, mBz, o2::track::DirOutward) || !propagator->PropagateToXBxByBz(tracOut, xtogo, MaxSnp, 10., mUseMatCorrFlag, &tofL)) { LOG(debug) << "Propagation to inner TPC boundary X=" << xtogo << " failed, Xtr=" << tracOut.getX() << " snp=" << tracOut.getSnp(); - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } float chi2Out = 0; @@ -1508,7 +1561,7 @@ bool MatchTPCITS::refitABTrack(int iITSAB, const TPCABSeed& seed) int retVal = mTPCRefitter->RefitTrackAsTrackParCov(tracOut, mTPCTracksArray[tTPC.sourceID].getClusterRef(), timeC * mTPCTBinMUSInv, &chi2Out, true, false); // outward refit if (retVal < 0) { LOG(debug) << "Refit failed"; - mMatchedTracks.pop_back(); // destroy failed track + matchedTracks.pop_back(); // destroy failed track return false; } auto posEnd = tracOut.getXYZGlo(); @@ -1682,15 +1735,17 @@ int MatchTPCITS::prepareInteractionTimes() } //______________________________________________ -void MatchTPCITS::runAfterBurner() +bool MatchTPCITS::runAfterBurner(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, + pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs, pmr::vector>& calib) { mTimer[SWABSeeds].Start(false); + mNABRefsClus = 0; prepareABSeeds(); int nIntCand = mInteractions.size(), nABSeeds = mTPCABSeeds.size(); LOGP(info, "AfterBurner will check {} seeds from {} TPC tracks and {} interaction candidates with {} threads", nABSeeds, mTPCABIndexCache.size(), nIntCand, mNThreads); // TMP mTimer[SWABSeeds].Stop(); if (!nIntCand || !mTPCABSeeds.size()) { - return; + return false; } mTimer[SWABMatch].Start(false); std::vector itsChipClRefsBuff(mNThreads); @@ -1763,23 +1818,34 @@ void MatchTPCITS::runAfterBurner() ABSeed.validate(bestID); ABSeed.flagLinkUsedClusters(bestID, mABClusterLinkIndex); mABWinnersIDs.push_back(tTPC.matchID = candAB[i].seedID); + mNABRefsClus += ABSeed.getNLayers(); nwin++; // RSTMP LOG(info) << "Iter: " << iter << " validated seed " << i << "[" << candAB[i].seedID << "/" << candAB[i].chi2 << "] for TPC track " << ABSeed.tpcWID << " last lr: " << int(ABSeed.lowestLayer) << " Ncont: " << int(link.nContLayers); } mTimer[SWABWinners].Stop(); mTimer[SWABRefit].Start(false); - refitABWinners(); + refitABWinners(matchedTracks, matchLabels, ABTrackletLabels, ABTrackletClusterIDs, ABTrackletRefs, calib); mTimer[SWABRefit].Stop(); + return true; } //______________________________________________ -void MatchTPCITS::refitABWinners() +void MatchTPCITS::refitABWinners(pmr::vector& matchedTracks, pmr::vector& matchLabels, pmr::vector& ABTrackletLabels, + pmr::vector& ABTrackletClusterIDs, pmr::vector& ABTrackletRefs, pmr::vector>& calib) { - mABTrackletClusterIDs.reserve(mABWinnersIDs.size() * (o2::its::RecoGeomHelper::getNLayers() - mParams->lowestLayerAB)); - mABTrackletRefs.reserve(mABWinnersIDs.size()); + // refit normal matches + refitWinners(matchedTracks, matchLabels, calib); + + ABTrackletClusterIDs.reserve(mNABRefsClus); + ABTrackletRefs.reserve(mABWinnersIDs.size()); if (mMCTruthON) { - mABTrackletLabels.reserve(mABWinnersIDs.size()); + ABTrackletLabels.reserve(mABWinnersIDs.size()); + } + if (matchedTracks.capacity() < mABWinnersIDs.size() + matchedTracks.size()) { + LOGP(warn, "need to expand matched tracks container from {} to {}", matchedTracks.capacity(), mABWinnersIDs.size() + matchedTracks.size()); + matchedTracks.reserve(mABWinnersIDs.size() + matchedTracks.size()); } + std::map labelOccurence; auto accountClusterLabel = [&labelOccurence, itsClLabs = mITSClsLabels](int clID) { auto labels = itsClLabs->getLabels(clID); @@ -1792,24 +1858,25 @@ void MatchTPCITS::refitABWinners() for (auto wid : mABWinnersIDs) { const auto& ABSeed = mTPCABSeeds[wid]; - int start = mABTrackletClusterIDs.size(); + int start = ABTrackletClusterIDs.size(); int lID = ABSeed.winLinkID, ncl = 0; - auto& clref = mABTrackletRefs.emplace_back(start, ncl); + auto& clref = ABTrackletRefs.emplace_back(start, ncl); while (lID > MinusOne) { const auto& winL = ABSeed.getLink(lID); if (winL.clID > MinusOne) { - mABTrackletClusterIDs.push_back(winL.clID); + ABTrackletClusterIDs.push_back(winL.clID); ncl++; clref.pattern |= 0x1 << winL.layerID; + clref.setClusterSize(winL.layerID, mITSClusterSizes[winL.clID]); if (mMCTruthON) { accountClusterLabel(winL.clID); } } lID = winL.parentID; } - if (!refitABTrack(mABTrackletRefs.size() - 1, ABSeed)) { // on failure, destroy added tracklet reference - mABTrackletRefs.pop_back(); - mABTrackletClusterIDs.resize(start); + if (!refitABTrack(ABTrackletRefs.size() - 1, ABSeed, matchedTracks, ABTrackletClusterIDs, ABTrackletRefs)) { // on failure, destroy added tracklet reference + ABTrackletRefs.pop_back(); + ABTrackletClusterIDs.resize(start); // RSS if (mMCTruthON) { labelOccurence.clear(); } @@ -1829,14 +1896,14 @@ void MatchTPCITS::refitABWinners() lab.setFakeFlag(); } labelOccurence.clear(); - mABTrackletLabels.push_back(lab); // ITSAB tracklet label - auto& lblGlo = mOutLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]); + ABTrackletLabels.push_back(lab); // ITSAB tracklet label + auto& lblGlo = matchLabels.emplace_back(mTPCLblWork[ABSeed.tpcWID]); lblGlo.setFakeFlag(lab != lblGlo); LOG(debug) << "ABWinner ncl=" << ncl << " mcLBAB " << lab << " mcLBGlo " << lblGlo << " chi2=" << ABSeed.getLink(ABSeed.winLinkID).chi2Norm() << " pT = " << ABSeed.track.getPt(); } // build MC label } - LOG(info) << "AfterBurner validated " << mABTrackletRefs.size() << " tracks"; + LOG(info) << "AfterBurner validated " << ABTrackletRefs.size() << " tracks"; } //______________________________________________ @@ -2377,6 +2444,168 @@ int MatchTPCITS::preselectChipClusters(std::vector& clVecOut, const ClusRan return clVecOut.size(); } +//__________________________________________________________ +void MatchTPCITS::reportSizes(pmr::vector& matchedTracks, + pmr::vector& ABTrackletRefs, + pmr::vector& ABTrackletClusterIDs, + pmr::vector& matchLabels, + pmr::vector& ABTrackletLabels, + pmr::vector>& calib) +{ + size_t sizTotShm = 0, capTotShm = 0, sizTot = 0, capTot = 0, siz = 0, cap = 0, cnt = 0, cntCap = 0; + { + siz = matchedTracks.size() * sizeof(o2::dataformats::TrackTPCITS); + cap = matchedTracks.capacity() * sizeof(o2::dataformats::TrackTPCITS); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, matchedTracks : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletRefs.size() * sizeof(o2::itsmft::TrkClusRef); + cap = ABTrackletRefs.capacity() * sizeof(o2::itsmft::TrkClusRef); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletRefs : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletClusterIDs.size() * sizeof(int); + cap = ABTrackletClusterIDs.capacity() * sizeof(int); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletClusterIDs : size {:9} cap {:9}", siz, cap); + // + siz = matchLabels.size() * sizeof(o2::MCCompLabel); + cap = matchLabels.capacity() * sizeof(o2::MCCompLabel); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, matchLabels : size {:9} cap {:9}", siz, cap); + // + siz = ABTrackletLabels.size() * sizeof(o2::MCCompLabel); + cap = ABTrackletLabels.capacity() * sizeof(o2::MCCompLabel); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, ABTrackletLabels : size {:9} cap {:9}", siz, cap); + // + siz = calib.size() * sizeof(o2::dataformats::Triplet); + cap = calib.capacity() * sizeof(o2::dataformats::Triplet); + sizTotShm += siz; + capTotShm += cap; + LOGP(info, "Size SHM, calib : size {:9} cap {:9}", siz, cap); + } + { + siz = mITSClustersArray.size() * sizeof(ITSCluster); + cap = mITSClustersArray.capacity() * sizeof(ITSCluster); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSClustersArray : size {:9} cap {:9}", siz, cap); + // + siz = mMatchRecordsTPC.size() * sizeof(MatchRecord); + cap = mMatchRecordsTPC.capacity() * sizeof(MatchRecord); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mMatchRecordsTPC : size {:9} cap {:9}", siz, cap); + // + siz = mMatchRecordsITS.size() * sizeof(MatchRecord); + cap = mMatchRecordsITS.capacity() * sizeof(MatchRecord); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mMatchRecordsITS : size {:9} cap {:9}", siz, cap); + // + siz = mITSROFTimes.size() * sizeof(BracketF); + cap = mITSROFTimes.capacity() * sizeof(BracketF); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSROFTimes : size {:9} cap {:9}", siz, cap); + // + siz = mTPCWork.size() * sizeof(TrackLocTPC); + cap = mTPCWork.capacity() * sizeof(TrackLocTPC); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCWork : size {:9} cap {:9}", siz, cap); + // + siz = mITSWork.size() * sizeof(TrackLocITS); + cap = mITSWork.capacity() * sizeof(TrackLocITS); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSWork : size {:9} cap {:9}", siz, cap); + // + siz = mWinnerChi2Refit.size() * sizeof(float); + cap = mWinnerChi2Refit.capacity() * sizeof(float); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mWinnerChi2Refit : size {:9} cap {:9}", siz, cap); + // + siz = mTPCABSeeds.size() * sizeof(float); + cap = mTPCABSeeds.capacity() * sizeof(float); + cnt = 0; + cntCap = 0; + for (const auto& a : mTPCABSeeds) { + siz += a.sizeInternal(); + cap += a.capInternal(); + cnt += a.trackLinks.size(); + cntCap += a.trackLinks.capacity(); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCABSeeds : size {:9} cap {:9} | internals size:{}/capacity:{} for {} elements", siz, cap, cnt, cntCap, mTPCABSeeds.size()); + // + siz = mTPCABIndexCache.size() * sizeof(int); + cap = mTPCABIndexCache.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCABIndexCache : size {:9} cap {:9}", siz, cap); + // + siz = mABWinnersIDs.size() * sizeof(int); + cap = mABWinnersIDs.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mABWinnersIDs : size {:9} cap {:9}", siz, cap); + // + siz = mABClusterLinkIndex.size() * sizeof(int); + cap = mABClusterLinkIndex.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mABClusterLinkIndex : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mTPCSectIndexCache[is].size() * sizeof(int); + cap += mTPCSectIndexCache[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCSectIndexCache : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mITSSectIndexCache[is].size() * sizeof(int); + cap += mITSSectIndexCache[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSSectIndexCache : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mTPCTimeStart[is].size() * sizeof(int); + cap += mTPCTimeStart[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mTPCTimeStart : size {:9} cap {:9}", siz, cap); + // + for (int is = 0; is < o2::constants::math::NSectors; is++) { + siz += mITSTimeStart[is].size() * sizeof(int); + cap += mITSTimeStart[is].capacity() * sizeof(int); + } + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, mITSTimeStart : size {:9} cap {:9}", siz, cap); + // + siz = mITSTrackROFContMapping.size() * sizeof(int); + cap = mITSTrackROFContMapping.capacity() * sizeof(int); + sizTot += siz; + capTot += cap; + LOGP(info, "Size RSS, ITSTrackROFContMapping: size {:9} cap {:9}", siz, cap); + } + LOGP(info, "TotalSizes/Capacities: SHM: {}/{} Heap: {}/{}", sizTotShm, capTotShm, sizTot, capTot); +} + //__________________________________________________________ void MatchTPCITS::setNThreads(int n) { @@ -2414,14 +2643,11 @@ void MatchTPCITS::fillTPCITSmatchTree(int itsID, int tpcID, int rejFlag, float c if (chi2 < 0.) { // need to recalculate chi2 = getPredictedChi2NoZ(trackITS, trackTPC); } - o2::MCCompLabel lblITS, lblTPC; (*mDBGOut) << "match" << "tf=" << mTFCount << "chi2Match=" << chi2 << "its=" << trackITS << "tpc=" << trackTPC << "tcorr=" << tCorr; if (mMCTruthON) { - lblITS = mITSLblWork[itsID]; - lblTPC = mTPCLblWork[tpcID]; (*mDBGOut) << "match" - << "itsLbl=" << lblITS << "tpcLbl=" << lblTPC; + << "itsLbl=" << mITSLblWork[itsID] << "tpcLbl=" << mTPCLblWork[tpcID]; } (*mDBGOut) << "match" << "rejFlag=" << rejFlag << "\n"; @@ -2449,12 +2675,9 @@ void MatchTPCITS::dumpWinnerMatches() (*mDBGOut) << "matchWin" << "tf=" << mTFCount << "chi2Match=" << itsMatchRec.chi2 << "chi2Refit=" << mWinnerChi2Refit[iits] << "its=" << tITS << "tpc=" << tTPC; - o2::MCCompLabel lblITS, lblTPC; if (mMCTruthON) { - lblITS = mITSLblWork[iits]; - lblTPC = mTPCLblWork[itpc]; (*mDBGOut) << "matchWin" - << "itsLbl=" << lblITS << "tpcLbl=" << lblTPC; + << "itsLbl=" << mITSLblWork[iits] << "tpcLbl=" << mTPCLblWork[itpc]; } (*mDBGOut) << "matchWin" << "\n"; diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx index c7067e1e4e0b6..1069eb96c7938 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx @@ -18,6 +18,7 @@ #include "MFTWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" +#include "TPCReaderWorkflow/TriggerReaderSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" #include "HMPIDWorkflow/ClustersReaderSpec.h" #include "HMPIDWorkflow/HMPMatchedReaderSpec.h" @@ -42,8 +43,8 @@ #include "MCHIO/TrackReaderSpec.h" #include "MCHIO/ClusterReaderSpec.h" #include "MIDWorkflow/TrackReaderSpec.h" -#include "PHOSWorkflow/ReaderSpec.h" -#include "CPVWorkflow/ReaderSpec.h" +#include "PHOSWorkflow/CellReaderSpec.h" +#include "CPVWorkflow/ClusterReaderSpec.h" #include "EMCALWorkflow/PublisherSpec.h" // #include "StrangenessTrackingWorkflow/StrangenessTrackingReaderSpec.h" @@ -97,6 +98,9 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskClusters[GID::TPC]) { specs.emplace_back(o2::tpc::getClusterReaderSpec(maskClustersMC[GID::TPC])); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + specs.emplace_back(o2::tpc::getTPCTriggerReaderSpec()); + } } if (maskTracks[GID::TPC] && maskClusters[GID::TPC]) { specs.emplace_back(o2::tpc::getClusterSharingMapSpec()); @@ -160,11 +164,11 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& } if (maskTracks[GID::PHS] || maskClusters[GID::PHS]) { - specs.emplace_back(o2::phos::getCellReaderSpec(maskTracksMC[GID::PHS] || maskClustersMC[GID::PHS])); + specs.emplace_back(o2::phos::getPHOSCellReaderSpec(maskTracksMC[GID::PHS] || maskClustersMC[GID::PHS])); } if (maskTracks[GID::CPV] || maskClusters[GID::CPV]) { - specs.emplace_back(o2::cpv::getClustersReaderSpec(maskTracksMC[GID::CPV] || maskClustersMC[GID::CPV])); + specs.emplace_back(o2::cpv::getCPVClusterReaderSpec(maskTracksMC[GID::CPV] || maskClustersMC[GID::CPV])); } if (maskTracks[GID::EMC] || maskClusters[GID::EMC]) { diff --git a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx index c4aa880f414c5..582f2be3c5f84 100644 --- a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx @@ -157,6 +157,10 @@ void GlobalFwdMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) } else { mMatching.setMFTROFrameLengthInBC(alpParams.roFrameLengthInBC); // MFT ROFrame duration in \mus } + if (alpParams.roFrameBiasInBC != 0) { + mMatching.setMFTROFrameBiasInBC(alpParams.roFrameBiasInBC); // MFT ROFrame bias in BCs wrt orbit start + LOG(info) << "Setting MFT ROF bias to " << alpParams.roFrameBiasInBC << " BCs"; + } mMatching.init(); } diff --git a/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx index e282de9ea46e0..4ea565525bfaa 100644 --- a/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/HMPMatcherSpec.cxx @@ -62,6 +62,7 @@ class HMPMatcherSpec : public Task void init(InitContext& ic) final; void run(ProcessingContext& pc) final; void endOfStream(framework::EndOfStreamContext& ec) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: std::shared_ptr mDataRequest; @@ -79,9 +80,14 @@ void HMPMatcherSpec::init(InitContext& ic) mTimer.Stop(); mTimer.Reset(); //-------- init geometry and field --------// - o2::base::GeometryManager::loadGeometry(); - o2::base::Propagator::initFieldFromGRP(); - std::unique_ptr grp{o2::parameters::GRPObject::loadFrom()}; + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); +} + +void HMPMatcherSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } } void HMPMatcherSpec::run(ProcessingContext& pc) @@ -90,6 +96,7 @@ void HMPMatcherSpec::run(ProcessingContext& pc) RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); + o2::base::GRPGeomHelper::instance().checkUpdates(pc); auto creationTime = DataRefUtils::getHeader(pc.inputs().getFirstValid(true))->creation; diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index f2dc738891b62..bd454678d6cc1 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -12,6 +12,8 @@ /// @file SecondaryVertexingSpec.cxx #include +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "Framework/CCDBParamSpec.h" #include "ReconstructionDataFormats/Decay3Body.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "ReconstructionDataFormats/TrackTPCITS.h" @@ -135,6 +137,11 @@ void SecondaryVertexingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob mStrTracker.setClusterDictionary((const o2::itsmft::TopologyDictionary*)obj); return; } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOG(info) << "Imposing new MeanVertex: " << ((const o2::dataformats::MeanVertexObject*)obj)->asString(); + mVertexer.setMeanVertex((const o2::dataformats::MeanVertexObject*)obj); + return; + } } void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) @@ -179,6 +186,8 @@ void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) mStrTracker.setupFitters(); } } + + pc.inputs().get("meanvtx"); } DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool useMC) @@ -195,6 +204,7 @@ DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCas } dataRequest->requestTracks(src, useMC); dataRequest->requestPrimaryVertertices(useMC); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true false, // GRPLHCIF diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx index 5b81466363710..a235242750ba3 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -165,7 +165,7 @@ void TOFMatcherSpec::run(ProcessingContext& pc) mMatcher.setTS(creationTime); - mMatcher.run(recoData); + mMatcher.run(recoData, pc.services().get().firstTForbit); if (isTPCused) { pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MTC_TPC", ss, Lifetime::Timeframe}, mMatcher.getMatchedTrackVector(o2::dataformats::MatchInfoTOFReco::TrackType::TPC)); @@ -248,7 +248,7 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo dataRequest->requestTracks(src, useMC); dataRequest->requestClusters(GID::getSourceMask(GID::TOF), useMC); if (useFIT) { - dataRequest->requestClusters(GID::getSourceMask(GID::FT0), false); + dataRequest->requestFT0RecPoints(false); } auto ggRequest = std::make_shared(false, // orbitResetTime diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index 48600e5eb0d38..819a6cd231560 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -100,19 +100,18 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) recoData.collectData(pc, *mDataRequest.get()); updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions - mMatching.run(recoData); - - pc.outputs().snapshot(Output{"GLO", "TPCITS", 0, Lifetime::Timeframe}, mMatching.getMatchedTracks()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_REFS", 0, Lifetime::Timeframe}, mMatching.getABTrackletRefs()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_CLID", 0, Lifetime::Timeframe}, mMatching.getABTrackletClusterIDs()); - if (mUseMC) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_MC", 0, Lifetime::Timeframe}, mMatching.getMatchLabels()); - pc.outputs().snapshot(Output{"GLO", "TPCITSAB_MC", 0, Lifetime::Timeframe}, mMatching.getABTrackletLabels()); - } + static pmr::vector dummyMCLab, dummyMCLabAB; + static pmr::vector> dummyCalib; + + auto& matchedTracks = pc.outputs().make>(Output{"GLO", "TPCITS", 0, Lifetime::Timeframe}); + auto& ABTrackletRefs = pc.outputs().make>(Output{"GLO", "TPCITSAB_REFS", 0, Lifetime::Timeframe}); + auto& ABTrackletClusterIDs = pc.outputs().make>(Output{"GLO", "TPCITSAB_CLID", 0, Lifetime::Timeframe}); + auto& matchLabels = mUseMC ? pc.outputs().make>(Output{"GLO", "TPCITS_MC", 0, Lifetime::Timeframe}) : dummyMCLab; + auto& ABTrackletLabels = mUseMC ? pc.outputs().make>(Output{"GLO", "TPCITSAB_MC", 0, Lifetime::Timeframe}) : dummyMCLabAB; + auto& calib = mCalibMode ? pc.outputs().make>>(Output{"GLO", "TPCITS_VDTGL", 0, Lifetime::Timeframe}) : dummyCalib; + + mMatching.run(recoData, matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); - if (mCalibMode) { - pc.outputs().snapshot(Output{"GLO", "TPCITS_VDTGL", 0, Lifetime::Timeframe}, mMatching.getTglITSTPC()); - } mTimer.Stop(); } diff --git a/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx index 624e2602fda42..8843570fc3b52 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tfidinfo-writer-workflow.cxx @@ -26,9 +26,7 @@ void customize(std::vector& policies) void customize(std::vector& workflowOptions) { // option allowing to set parameters - std::vector options{ - ConfigParamSpec{"dataspec", VariantType::String, "tfidinfo:FLP/DISTSUBTIMEFRAME/0xccdb", {"spec from which the TFIDInfo will be extracted"}}, - ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + std::vector options{ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; std::swap(workflowOptions, options); } @@ -37,7 +35,10 @@ void customize(std::vector& workflowOptions) #include "Framework/DataProcessingHeader.h" #include "Framework/Task.h" #include "CommonDataFormat/TFIDInfo.h" +#include "CommonUtils/TreeStreamRedirector.h" #include "CommonUtils/NameConf.h" +#include "CommonConstants/LHCConstants.h" +#include "Framework/CCDBParamSpec.h" #include #include @@ -53,17 +54,31 @@ class TFIDInfoWriter : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged) { // new run is starting + auto v = pc.inputs().get*>("orbitReset"); + mOrbitReset = (*v)[0]; + } o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mData.emplace_back()); } void endOfStream(EndOfStreamContext& ec) final { + o2::utils::TreeStreamRedirector pcstream; TFile fl(mOutFileName.c_str(), "recreate"); fl.WriteObjectAny(&mData, "std::vector", "tfidinfo"); - LOGP(info, "Wrote TFIDInfo vector with {} entries to {}", mData.size(), fl.GetName()); + pcstream.SetFile(&fl); + for (const auto& info : mData) { + long ts = (mOrbitReset + long(info.firstTForbit * o2::constants::lhc::LHCOrbitMUS)) / 1000; + pcstream << "tfidTree" + << "tfidinfo=" << info << "ts=" << ts << "\n"; + } + pcstream.Close(); + LOGP(info, "Wrote tfidinfo vector and tfidTree with {} entries to {}", mData.size(), fl.GetName()); } private: + long mOrbitReset = 0; std::string mOutFileName{}; std::vector mData; }; @@ -72,8 +87,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { WorkflowSpec wf; o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(DataProcessorSpec{"tfid-info-writer", o2::framework::select(cfgc.options().get("dataspec").c_str()), - std::vector{}, AlgorithmSpec{adaptFromTask()}, + wf.emplace_back(DataProcessorSpec{"tfid-info-writer", + {{"orbitReset", "CTP", "ORBITRESET", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/OrbitReset")}}, + std::vector{}, + AlgorithmSpec{adaptFromTask()}, Options{{"tfidinfo-file-name", VariantType::String, o2::base::NameConf::getTFIDInfoFileName(), {"output file for TFIDInfo"}}}}); return wf; } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index 47c19c0ffc7a9..7d5b132ec7f3a 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -23,6 +23,7 @@ #include "CommonUtils/NameConf.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/ControlService.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TPCTrackStudy.h" @@ -67,8 +68,12 @@ class TPCTrackStudySpec : public Task o2::tpc::VDriftHelper mTPCVDriftHelper{}; o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; bool mUseMC{false}; ///< MC flag + bool mUseGPUModel{false}; float mXRef = 0.; int mNMoves = 6; + int mTFStart = 0; + int mTFEnd = 999999999; + int mTFCount = -1; bool mUseR = false; std::unique_ptr mDBGOut; std::unique_ptr mDBGOutCl; @@ -91,6 +96,9 @@ void TPCTrackStudySpec::init(InitContext& ic) mXRef = ic.options().get("target-x"); mNMoves = std::max(2, ic.options().get("n-moves")); mUseR = ic.options().get("use-r-as-x"); + mUseGPUModel = ic.options().get("use-gpu-fitter"); + mTFStart = ic.options().get("tf-start"); + mTFEnd = ic.options().get("tf-end"); if (mXRef < 0.) { mXRef = 0.; } @@ -103,10 +111,22 @@ void TPCTrackStudySpec::init(InitContext& ic) void TPCTrackStudySpec::run(ProcessingContext& pc) { + mTFCount++; + if (mTFCount < mTFStart || mTFCount > mTFEnd) { + LOGP(info, "Skipping TF {}", mTFCount); + return; + } + o2::globaltracking::RecoContainer recoData; recoData.collectData(pc, *mDataRequest.get()); // select tracks of needed type, with minimal cuts, the real selected will be done in the vertexer updateTimeDependentParams(pc); // Make sure this is called after recoData.collectData, which may load some conditions process(recoData); + + if (mTFCount > mTFEnd) { + LOGP(info, "Stopping processing after TF {}", mTFCount); + pc.services().get().endOfStream(); + return; + } } void TPCTrackStudySpec::updateTimeDependentParams(ProcessingContext& pc) @@ -205,7 +225,7 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) // create refitted copy auto trackRefit = [itr, this](o2::track::TrackParCov& trc, float t) -> bool { float chi2Out = 0; - int retVal = this->mTPCRefitter->RefitTrackAsTrackParCov(trc, this->mTPCTracksArray[itr].getClusterRef(), t, &chi2Out, false, true); + int retVal = mUseGPUModel ? this->mTPCRefitter->RefitTrackAsGPU(trc, this->mTPCTracksArray[itr].getClusterRef(), t, &chi2Out, false, true) : this->mTPCRefitter->RefitTrackAsTrackParCov(trc, this->mTPCTracksArray[itr].getClusterRef(), t, &chi2Out, false, true); if (retVal < 0) { LOGP(warn, "Refit failed ({}) with time={}: track#{}[{}]", retVal, t, counter, trc.asString()); return false; @@ -391,6 +411,9 @@ DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas {"target-x", VariantType::Float, 70.f, {"Try to propagate to this radius"}}, {"n-moves", VariantType::Int, 6, {"Number of moves in allow range"}}, {"dump-clusters", VariantType::Bool, false, {"dump clusters"}}, + {"tf-start", VariantType::Int, 0, {"1st TF to process"}}, + {"tf-end", VariantType::Int, 999999999, {"last TF to process"}}, + {"use-gpu-fitter", VariantType::Bool, false, {"use GPU track model for refit instead of TrackParCov"}}, {"use-r-as-x", VariantType::Bool, false, {"Use radius instead of target sector X"}}}; auto dataRequest = std::make_shared(); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index 3a0290a9c0b0d..ce2d53d71c385 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -31,6 +31,8 @@ #include "GlobalTrackingStudy/TrackingStudy.h" #include "TPCBase/ParameterElectronics.h" #include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/PrimaryVertexExt.h" +#include "DataFormatsFT0/RecPoints.h" #include "CommonUtils/TreeStreamRedirector.h" #include "ReconstructionDataFormats/VtxTrackRef.h" #include "ReconstructionDataFormats/DCA.h" @@ -69,7 +71,10 @@ class TrackingStudySpec : public Task std::shared_ptr mGGCCDBRequest; bool mUseMC{false}; ///< MC flag std::unique_ptr mDBGOut; + std::unique_ptr mDBGOutVtx; float mITSROFrameLengthMUS = 0.; + int mMaxNeighbours = 3; + float mMaxVTTimeDiff = 25.; // \mus GTrackID::mask_t mTracksSrc{}; o2::steer::MCKinematicsReader mcReader; // reader of MC information }; @@ -78,6 +83,10 @@ void TrackingStudySpec::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mDBGOut = std::make_unique("trackStudy.root", "recreate"); + mDBGOutVtx = std::make_unique("trackStudyVtx.root", "recreate"); + + mMaxVTTimeDiff = ic.options().get("max-vtx-timediff"); + mMaxNeighbours = ic.options().get("max-vtx-neighbours"); } void TrackingStudySpec::run(ProcessingContext& pc) @@ -111,19 +120,54 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) auto trackIndex = recoData.getPrimaryVertexMatchedTracks(); // Global ID's for associated tracks auto vtxRefs = recoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs auto prop = o2::base::Propagator::Instance(); - + auto FITInfo = recoData.getFT0RecPoints(); + static int TFCount = 0; int nv = vtxRefs.size() - 1; + o2::dataformats::PrimaryVertexExt pveDummy; + std::vector pveVec; + for (int iv = 0; iv < nv; iv++) { + LOGP(debug, "processing PV {} of {}", iv, nv); + const auto& vtref = vtxRefs[iv]; auto pv = pvvec[iv]; - for (int is = 0; is < GTrackID::NSources; is++) { - if (!GTrackID::getSourceDetectorsMask(is)[GTrackID::ITS]) { - continue; + auto& pve = pveVec.emplace_back(); + static_cast(pve) = pv; + + float bestTimeDiff = 1000, bestTime = -999; + int bestFTID = -1; + if (mTracksSrc[GTrackID::FT0]) { + for (int ift0 = vtref.getFirstEntryOfSource(GTrackID::FT0); ift0 < vtref.getFirstEntryOfSource(GTrackID::FT0) + vtref.getEntriesOfSource(GTrackID::FT0); ift0++) { + const auto& ft0 = FITInfo[trackIndex[ift0]]; + if (ft0.isValidTime(o2::ft0::RecPoints::TimeMean) && ft0.getTrigger().getAmplA() + ft0.getTrigger().getAmplC() > 20) { + auto fitTime = ft0.getInteractionRecord().differenceInBCMUS(recoData.startIR); + if (std::abs(fitTime - pv.getTimeStamp().getTimeStamp()) < bestTimeDiff) { + bestTimeDiff = fitTime - pv.getTimeStamp().getTimeStamp(); + bestFTID = trackIndex[ift0]; + } + } } - int idMin = vtxRefs[iv].getFirstEntryOfSource(is), idMax = idMin + vtxRefs[iv].getEntriesOfSource(is); + } else { + LOGP(warn, "FT0 is not requested, cannot set complete vertex info"); + } + if (bestFTID >= 0) { + pve.FT0A = FITInfo[bestFTID].getTrigger().getAmplA(); + pve.FT0Amp = pve.FT0A + FITInfo[bestFTID].getTrigger().getAmplC(); + pve.FT0Time = FITInfo[bestFTID].getInteractionRecord().differenceInBCMUS(recoData.startIR); + } + pve.VtxID = iv; + for (int is = 0; is < GTrackID::NSources; is++) { + bool skipTracks = (!GTrackID::getSourceDetectorsMask(is)[GTrackID::ITS] || !mTracksSrc[is] || !recoData.isTrackSourceLoaded(is)); + int idMin = vtref.getFirstEntryOfSource(is), idMax = idMin + vtref.getEntriesOfSource(is); for (int i = idMin; i < idMax; i++) { auto vid = trackIndex[i]; bool pvCont = vid.isPVContributor(); + if (pvCont) { + pve.nSrc[is]++; + } + if (skipTracks) { + continue; + } bool ambig = vid.isAmbiguous(); auto trc = recoData.getTrackParam(vid); float xmin = trc.getX(); @@ -131,16 +175,101 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) if (!prop->propagateToDCA(pv, trc, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dca)) { continue; } + float ttime = 0, ttimeE = 0; + recoData.getTrackTime(vid, ttime, ttimeE); (*mDBGOut) << "dca" + << "tfID=" << TFCount << "ttime=" << ttime << "ttimeE=" << ttimeE << "gid=" << vid << "pv=" << pv << "trc=" << trc << "pvCont=" << pvCont << "ambig=" << ambig << "dca=" << dca << "xmin=" << xmin << "\n"; } } } + int nvtot = mMaxNeighbours < 0 ? -1 : (int)pveVec.size(); + for (int cnt = 0; cnt < nvtot; cnt++) { + const auto& pve = pveVec[cnt]; + float tv = pve.getTimeStamp().getTimeStamp(); + std::vector pveT(mMaxNeighbours); // neighbours in time + std::vector pveZ(mMaxNeighbours); // neighbours in Z + std::vector idT(mMaxNeighbours), idZ(mMaxNeighbours); + std::vector dT(mMaxNeighbours), dZ(mMaxNeighbours); + for (int i = 0; i < mMaxNeighbours; i++) { + idT[i] = idZ[i] = -1; + dT[i] = mMaxVTTimeDiff; + dZ[i] = 1e9; + } + int cntM = cnt - 1, cntP = cnt + 1; + for (; cntM >= 0; cntM--) { // backward + const auto& vt = pveVec[cntM]; + auto dtime = std::abs(tv - vt.getTimeStamp().getTimeStamp()); + if (dtime > mMaxVTTimeDiff) { + continue; + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (dT[i] > dtime) { + dT[i] = dtime; + idT[i] = cntM; + break; + } + } + auto dz = std::abs(pve.getZ() - vt.getZ()); + for (int i = 0; i < mMaxNeighbours; i++) { + if (dZ[i] > dz) { + dZ[i] = dz; + idZ[i] = cntM; + break; + } + } + } + for (; cntP < nvtot; cntP++) { // forward + const auto& vt = pveVec[cntP]; + auto dtime = std::abs(tv - vt.getTimeStamp().getTimeStamp()); + if (dtime > mMaxVTTimeDiff) { + continue; + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (dT[i] > dtime) { + dT[i] = dtime; + idT[i] = cntP; + break; + } + } + auto dz = std::abs(pve.getZ() - vt.getZ()); + for (int i = 0; i < mMaxNeighbours; i++) { + if (dZ[i] > dz) { + dZ[i] = dz; + idZ[i] = cntP; + break; + } + } + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (idT[i] != -1) { + pveT[i] = pveVec[idT[i]]; + } else { + break; + } + } + for (int i = 0; i < mMaxNeighbours; i++) { + if (idZ[i] != -1) { + pveZ[i] = pveVec[idZ[i]]; + } else { + break; + } + } + (*mDBGOutVtx) << "pvExt" + << "pve=" << pve + << "pveT=" << pveT + << "pveZ=" << pveZ + << "tfID=" << TFCount + << "\n"; + } + + TFCount++; } void TrackingStudySpec::endOfStream(EndOfStreamContext& ec) { mDBGOut.reset(); + mDBGOutVtx.reset(); } void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -172,7 +301,10 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->inputs, outputs, AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, - Options{}}; + Options{ + {"max-vtx-neighbours", VariantType::Int, 2, {"Max PV neighbours fill, no PV study if < 0"}}, + {"max-vtx-timediff", VariantType::Float, 25.f, {"Max PV time difference to consider"}}, + }}; } } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx index e9e7db4e6f339..27a48d93dac8b 100644 --- a/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx +++ b/Detectors/GlobalTrackingWorkflow/tofworkflow/src/tof-calibinfo-reader.cxx @@ -74,8 +74,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto listname = cfgc.options().get("collection-infile"); auto toftpc = cfgc.options().get("tpc-matches"); - char* stringTBP = new char[listname.size()]; - snprintf(stringTBP, listname.size(), "%s", listname.c_str()); + char* stringTBP = new char[listname.size() + 1]; + snprintf(stringTBP, listname.size() + 1, "%s", listname.c_str()); // the lane configuration defines the subspecification ids to be distributed among the lanes. // auto tofSectors = o2::RangeTokenizer::tokenize(cfgc.options().get("tof-sectors")); diff --git a/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h b/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h index b55ba3b6c7353..9e9a78914049e 100644 --- a/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h +++ b/Detectors/HMPID/simulation/include/HMPIDSimulation/Detector.h @@ -55,6 +55,7 @@ class Detector : public o2::base::DetImpl void EndOfEvent() override { Reset(); } // for the geometry sub-parts + TGeoVolume* createAbsorber(float tickness); TGeoVolume* createChamber(int number); TGeoVolume* CreateCradle(); TGeoVolume* CradleBaseVolume(TGeoMedium* med, double l[7], const char* name); diff --git a/Detectors/HMPID/simulation/src/Detector.cxx b/Detectors/HMPID/simulation/src/Detector.cxx index 674bf7e85441d..83bab71e7177d 100644 --- a/Detectors/HMPID/simulation/src/Detector.cxx +++ b/Detectors/HMPID/simulation/src/Detector.cxx @@ -73,20 +73,20 @@ bool Detector::ProcessHits(FairVolume* v) Int_t volID = fMC->CurrentVolID(copy); auto stack = (o2::data::Stack*)fMC->GetStack(); - //Treat photons - //photon (Ckov or feedback) hits on module PC (Hpad) + // Treat photons + // photon (Ckov or feedback) hits on module PC (Hpad) if ((fMC->TrackPid() == 50000050 || fMC->TrackPid() == 50000051) && (volID == mHpad0VolID || volID == mHpad1VolID || volID == mHpad2VolID || volID == mHpad3VolID || volID == mHpad4VolID || volID == mHpad5VolID || volID == mHpad6VolID)) { - if (fMC->Edep() > 0) { //photon survided QE test i.e. produces electron + if (fMC->Edep() > 0) { // photon survided QE test i.e. produces electron if (IsLostByFresnel()) { fMC->StopTrack(); return false; - } //photon lost due to fersnel reflection on PC - Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); //take TID - Int_t pid = fMC->TrackPid(); //take PID - Float_t etot = fMC->Etot(); //total hpoton energy, [GeV] + } // photon lost due to fersnel reflection on PC + Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); // take TID + Int_t pid = fMC->TrackPid(); // take PID + Float_t etot = fMC->Etot(); // total hpoton energy, [GeV] Double_t x[3]; - fMC->TrackPosition(x[0], x[1], x[2]); //take MARS position at entrance to PC - Float_t hitTime = (Float_t)fMC->TrackTime(); //hit formation time + fMC->TrackPosition(x[0], x[1], x[2]); // take MARS position at entrance to PC + Float_t hitTime = (Float_t)fMC->TrackTime(); // hit formation time Int_t idch; // chamber number if (volID == mHpad0VolID) { idch = 0; @@ -104,20 +104,20 @@ bool Detector::ProcessHits(FairVolume* v) idch = 6; } Double_t xl, yl; - o2::hmpid::Param::instance()->mars2Lors(idch, x, xl, yl); //take LORS position - AddHit(x[0], x[1], x[2], hitTime, etot, tid, idch); //HIT for photon, position at P, etot will be set to Q - GenFee(etot); //generate feedback photons etot is modified in hit ctor to Q of hit + o2::hmpid::Param::instance()->mars2Lors(idch, x, xl, yl); // take LORS position + AddHit(x[0], x[1], x[2], hitTime, etot, tid, idch); // HIT for photon, position at P, etot will be set to Q + GenFee(etot); // generate feedback photons etot is modified in hit ctor to Q of hit stack->addHit(GetDetId()); - } //photon hit PC and DE >0 + } // photon hit PC and DE >0 return kTRUE; - } //photon hit PC + } // photon hit PC - //Treat charged particles - static Float_t eloss; //need to store mip parameters between different steps + // Treat charged particles + static Float_t eloss; // need to store mip parameters between different steps static Double_t in[3]; if (fMC->IsTrackEntering() && fMC->TrackCharge() && (volID == mHpad0VolID || volID == mHpad1VolID || volID == mHpad2VolID || volID == mHpad3VolID || volID == mHpad4VolID || volID == mHpad5VolID || volID == mHpad6VolID)) { - //Trackref stored when entering in the pad volume + // Trackref stored when entering in the pad volume o2::TrackReference tr(*fMC, GetDetId()); tr.setTrackID(stack->GetCurrentTrackNumber()); // tr.setUserId(lay); @@ -126,18 +126,18 @@ bool Detector::ProcessHits(FairVolume* v) if (fMC->TrackCharge() && (volID == mHcel0VolID || volID == mHcel1VolID || volID == mHcel2VolID || volID == mHcel3VolID || volID == mHcel4VolID || volID == mHcel5VolID || volID == mHcel6VolID)) { // charged particle in amplification gap (Hcel) - if (fMC->IsTrackEntering() || fMC->IsNewTrack()) { //entering or newly created - eloss = 0; //reset Eloss collector - fMC->TrackPosition(in[0], in[1], in[2]); //take position at the entrance - } else if (fMC->IsTrackExiting() || fMC->IsTrackStop() || fMC->IsTrackDisappeared()) { //exiting or disappeared - eloss += fMC->Edep(); //take into account last step Eloss - Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); //take TID - Int_t pid = fMC->TrackPid(); //take PID + if (fMC->IsTrackEntering() || fMC->IsNewTrack()) { // entering or newly created + eloss = 0; // reset Eloss collector + fMC->TrackPosition(in[0], in[1], in[2]); // take position at the entrance + } else if (fMC->IsTrackExiting() || fMC->IsTrackStop() || fMC->IsTrackDisappeared()) { // exiting or disappeared + eloss += fMC->Edep(); // take into account last step Eloss + Int_t tid = fMC->GetStack()->GetCurrentTrackNumber(); // take TID + Int_t pid = fMC->TrackPid(); // take PID Double_t out[3]; - fMC->TrackPosition(out[0], out[1], out[2]); //take MARS position at exit - Float_t hitTime = (Float_t)fMC->TrackTime(); //hit formation time + fMC->TrackPosition(out[0], out[1], out[2]); // take MARS position at exit + Float_t hitTime = (Float_t)fMC->TrackTime(); // hit formation time out[0] = 0.5 * (out[0] + in[0]); // - out[1] = 0.5 * (out[1] + in[1]); //take hit position at the anod plane + out[1] = 0.5 * (out[1] + in[1]); // take hit position at the anod plane out[2] = 0.5 * (out[2] + in[2]); Int_t idch; // chamber number if (volID == mHcel0VolID) { @@ -156,20 +156,20 @@ bool Detector::ProcessHits(FairVolume* v) idch = 6; } Double_t xl, yl; - o2::hmpid::Param::instance()->mars2Lors(idch, out, xl, yl); //take LORS position + o2::hmpid::Param::instance()->mars2Lors(idch, out, xl, yl); // take LORS position if (eloss > 0) { // HIT for MIP, position near anod plane, eloss will be set to Q AddHit(out[0], out[1], out[2], hitTime, eloss, tid, idch); - GenFee(eloss); //generate feedback photons + GenFee(eloss); // generate feedback photons stack->addHit(GetDetId()); eloss = 0; } } else { - //just going inside - eloss += fMC->Edep(); //collect this step eloss + // just going inside + eloss += fMC->Edep(); // collect this step eloss } return kTRUE; - } //MIP in GAP + } // MIP in GAP // later on return true if there was a hit! return false; @@ -187,14 +187,14 @@ void Detector::GenFee(Float_t qtot) // eloss=0 means photon so only pulse height distribution is to be analysed. TLorentzVector x4; fMC->TrackPosition(x4); - Int_t iNphotons = fMC->GetRandom()->Poisson(0.02 * qtot); //# of feedback photons is proportional to the charge of hit - //AliDebug(1,Form("N photons=%i",iNphotons)); + Int_t iNphotons = fMC->GetRandom()->Poisson(0.02 * qtot); // # of feedback photons is proportional to the charge of hit + // AliDebug(1,Form("N photons=%i",iNphotons)); Int_t j; Float_t cthf, phif, enfp = 0, sthf, e1[3], e2[3], e3[3], vmod, uswop, dir[3], phi, pol[3], mom[4]; - //Generate photons - for (Int_t i = 0; i < iNphotons; i++) { //feedbacks loop + // Generate photons + for (Int_t i = 0; i < iNphotons; i++) { // feedbacks loop Double_t ranf[2]; - fMC->GetRandom()->RndmArray(2, ranf); //Sample direction + fMC->GetRandom()->RndmArray(2, ranf); // Sample direction cthf = ranf[0] * 2 - 1.0; if (cthf < 0) { continue; @@ -276,8 +276,8 @@ void Detector::GenFee(Float_t qtot) } fMC->Gdtom(pol, pol, 2); Int_t outputNtracksStored; - } //feedbacks loop -} //GenerateFeedbacks() + } // feedbacks loop +} // GenerateFeedbacks() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Bool_t Detector::IsLostByFresnel() { @@ -301,7 +301,7 @@ Bool_t Detector::IsLostByFresnel() } else { return kFALSE; } -} //IsLostByFresnel() +} // IsLostByFresnel() //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) { @@ -325,8 +325,8 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) Float_t cn = csin[j] + ((csin[j + 1] - csin[j]) / 0.1) * (xe - en[j]); Float_t ck = csik[j] + ((csik[j + 1] - csik[j]) / 0.1) * (xe - en[j]); - //FORMULAE FROM HANDBOOK OF OPTICS, 33.23 OR - //W.R. HUNTER, J.O.S.A. 54 (1964),15 , J.O.S.A. 55(1965),1197 + // FORMULAE FROM HANDBOOK OF OPTICS, 33.23 OR + // W.R. HUNTER, J.O.S.A. 54 (1964),15 , J.O.S.A. 55(1965),1197 Float_t sinin = TMath::Sqrt((1. - pdoti) * (1. + pdoti)); Float_t tanin = sinin / pdoti; @@ -340,8 +340,8 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) Float_t rp = rs * ((aO - sinin * tanin) * (aO - sinin * tanin) + b2) / ((aO + sinin * tanin) * (aO + sinin * tanin) + b2); - //CORRECTION FACTOR FOR SURFACE ROUGHNESS - //B.J. STAGG APPLIED OPTICS, 30(1991),4113 + // CORRECTION FACTOR FOR SURFACE ROUGHNESS + // B.J. STAGG APPLIED OPTICS, 30(1991),4113 Float_t sigraf = 18.; Float_t lamb = 1240 / ene; @@ -351,7 +351,7 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) (4 * TMath::Pi() * pdoti * sigraf / lamb)); if (pola) { - Float_t pdotr = 0.8; //DEGREE OF POLARIZATION : 1->P , -1->S + Float_t pdotr = 0.8; // DEGREE OF POLARIZATION : 1->P , -1->S fresn = 0.5 * (rp * (1 + pdotr) + rs * (1 - pdotr)); } else { fresn = 0.5 * (rp + rs); @@ -359,7 +359,7 @@ Float_t Detector::Fresnel(Float_t ene, Float_t pdoti, Bool_t pola) fresn = fresn * rO; return fresn; -} //Fresnel() +} // Fresnel() //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ void Detector::Register() { FairRootManager::Instance()->RegisterAny(addNameTo("Hit").data(), mHits, true); } @@ -537,7 +537,15 @@ void Detector::createMaterials() Material(++matId, "Ar", aAr, zAr, dAr, radAr, absAr); Medium(kAr, "Ar", matId, unsens, itgfld, maxfld, tmaxfd, stemax, deemax, epsil, stmin); } - +//************************************************************************************************** +TGeoVolume* Detector::createAbsorber(float tickness) +{ + double cm = 1, mm = 0.1 * cm, um = 0.001 * mm; // default is cm + auto& matmgr = o2::base::MaterialManager::Instance(); + TGeoMedium* al = matmgr.getTGeoMedium("HMP_Al"); + TGeoVolume* abs = gGeoManager->MakeBox("Habs", al, tickness * mm / 2, 1300.00 * mm / 2, 1300 * mm / 2); + return abs; +} //************************************************************************************************** TGeoVolume* Detector::createChamber(int number) @@ -1252,6 +1260,25 @@ void Detector::ConstructGeometry() TGeoVolume* hmpcradle = CreateCradle(); + TGeoVolume* hmpidabs_cham2 = createAbsorber(40.0); + TGeoVolume* hmpidabs_cham4 = createAbsorber(80.0); + + double theta = 33.5; + + TGeoHMatrix* pMatrixAbs2 = new TGeoHMatrix; + const double trans2[] = {435.5, 0., -155.}; + pMatrixAbs2->SetTranslation(trans2); + pMatrixAbs2->RotateZ(theta); + + gGeoManager->GetVolume("barrel")->AddNode(hmpidabs_cham2, 0, pMatrixAbs2); + + TGeoHMatrix* pMatrixAbs4 = new TGeoHMatrix; + const double trans4[] = {435., 0., 155.}; + pMatrixAbs4->SetTranslation(trans4); + pMatrixAbs4->RotateZ(theta); + + gGeoManager->GetVolume("barrel")->AddNode(hmpidabs_cham4, 0, pMatrixAbs4); + for (Int_t iCh = 0; iCh <= 6; iCh++) { // place 7 chambers TGeoVolume* hmpid = createChamber(iCh); TGeoHMatrix* pMatrix = new TGeoHMatrix; diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx index 8a0f4c5ab3f4b..7652f175434da 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ITSStudiesConfigParam.cxx @@ -24,6 +24,7 @@ static auto& sImpactParameterParamsITS = o2::its::study::ITSImpactParameterParam O2ParamImpl(o2::its::study::ITSAvgClusSizeParamConfig); O2ParamImpl(o2::its::study::ITSCheckTracksParamConfig); O2ParamImpl(o2::its::study::ITSImpactParameterParamConfig); + } // namespace study } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx index c0476681e8c86..87c76e492e0fa 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackCheck.cxx @@ -24,16 +24,20 @@ #include "DetectorsBase/GRPGeomHelper.h" #include +#include +#include +#include #include #include #include #include +#include +#include +#include +#include +#include -namespace o2 -{ -namespace its -{ -namespace study +namespace o2::its::study { using namespace o2::framework; using namespace o2::globaltracking; @@ -50,11 +54,16 @@ class TrackCheckStudy : public Task float phi; int mother; int first; - unsigned short clusters = 0u; + float vx; + float vy; + float vz; + int unsigned short clusters = 0u; unsigned char isReco = 0u; unsigned char isFake = 0u; - bool isPrimary = 0u; + bool isPrimary = false; unsigned char storedStatus = 2; /// not stored = 2, fake = 1, good = 0 + const char* prodProcessName; + int prodProcess; o2::its::TrackITS track; }; @@ -69,6 +78,7 @@ class TrackCheckStudy : public Task LOGP(info, "Read MCKine reader with {} sources", mKineReader->getNSources()); } } + ~TrackCheckStudy() final = default; void init(InitContext&) final; void run(ProcessingContext&) final; @@ -76,6 +86,7 @@ class TrackCheckStudy : public Task void finaliseCCDB(ConcreteDataMatcher&, void*) final; void initialiseRun(o2::globaltracking::RecoContainer&); void process(); + void setEfficiencyGraph(std::unique_ptr&, const char*, const char*, const int, const double, const double, const int, const double); private: void updateTimeDependentParams(ProcessingContext& pc); @@ -88,6 +99,7 @@ class TrackCheckStudy : public Task gsl::span mTracks; gsl::span mTracksMCLabels; gsl::span mClusters; + gsl::span mInputITSidxs; const o2::dataformats::MCLabelContainer* mClustersMCLCont; // Data @@ -102,31 +114,99 @@ class TrackCheckStudy : public Task // Histos std::unique_ptr mGoodPt; std::unique_ptr mGoodEta; + std::unique_ptr mGoodPtSec; + std::unique_ptr mGoodEtaSec; std::unique_ptr mGoodChi2; std::unique_ptr mFakePt; std::unique_ptr mFakeEta; + std::unique_ptr mFakePtSec; + std::unique_ptr mFakeEtaSec; std::unique_ptr mMultiFake; std::unique_ptr mFakeChi2; - std::unique_ptr mClonePt; std::unique_ptr mCloneEta; std::unique_ptr mDenominatorPt; std::unique_ptr mDenominatorEta; + std::unique_ptr mDenominatorPtSec; + std::unique_ptr mDenominatorEtaSec; + + std::unique_ptr processvsZ; // TH2D with production process + std::unique_ptr processvsRad; + std::unique_ptr processvsRadOther; + std::unique_ptr processvsRadNotTracked; + std::unique_ptr processvsEtaNotTracked; - std::unique_ptr mEffPt; + std::unique_ptr mEffPt; // Eff vs Pt primary std::unique_ptr mEffFakePt; std::unique_ptr mEffClonesPt; - - std::unique_ptr mEffEta; + std::unique_ptr mEffEta; // Eff vs Eta primary std::unique_ptr mEffFakeEta; std::unique_ptr mEffClonesEta; - // Canvas & decorations + std::unique_ptr mEffPtSec; // Eff vs Pt secondary + std::unique_ptr mEffFakePtSec; + std::unique_ptr mEffEtaSec; // Eff vs Eta secondary + std::unique_ptr mEffFakeEtaSec; + + std::unique_ptr mPtResolution; // Pt resolution for both primary and secondary + std::unique_ptr mPtResolution2D; + std::unique_ptr mPtResolutionSec; + std::unique_ptr mPtResolutionPrim; + std::unique_ptr g1; + + const char* ParticleName[7] = {"e^{-/+}", "#pi^{-/+}", "p", "^{2}H", "^{3}He", "_{#Lambda}^{3}H", "k^{+/-}"}; + const int PdgcodeClusterFake[7] = {11, 211, 2212, 1000010020, 100002030, 1010010030, 321}; + const char* name[3] = {"_{#Lambda}^{3}H", "#Lambda", "k^{0}_{s}"}; + const char* particleToanalize[4] = {"IperT", "Lambda", "k0s", "Tot"}; // [3]=Total of secondary particle + const int PDG[3] = {1010010030, 3122, 310}; + const char* ProcessName[50]; + int colorArr[4] = {kGreen, kRed, kBlue, kOrange}; + + std::vector> histLength, histLength1Fake, histLength2Fake, histLength3Fake, histLengthNoCl, histLength1FakeNoCl, histLength2FakeNoCl, histLength3FakeNoCl; // FakeCluster Study + std::vector stackLength, stackLength1Fake, stackLength2Fake, stackLength3Fake; + std::vector legends, legends1Fake, legends2Fake, legends3Fake; + std::vector> mClusterFake; + std::vector>> mGoodPts, mFakePts, mTotPts, mGoodEtas, mTotEtas, mFakeEtas; + std::vector>> mEffGoodPts, mEffFakePts, mEffGoodEtas, mEffFakeEtas; + std::vector> mGoodRad, mFakeRad, mTotRad, mGoodZ, mFakeZ, mTotZ; + std::vector> mEffGoodRad, mEffFakeRad, mEffGoodZ, mEffFakeZ; + // Canvas & decorations std::unique_ptr mCanvasPt; + std::unique_ptr mCanvasPtSec; + std::unique_ptr mCanvasPt2; + std::unique_ptr mCanvasPt2fake; std::unique_ptr mCanvasEta; + std::unique_ptr mCanvasEtaSec; + std::unique_ptr mCanvasRad; + std::unique_ptr mCanvasZ; + std::unique_ptr mCanvasRadD; + std::unique_ptr mCanvasZD; + std::unique_ptr mCanvasPtRes; + std::unique_ptr mCanvasPtRes2; + std::unique_ptr mCanvasPtRes3; + std::unique_ptr mCanvasPtRes4; std::unique_ptr mLegendPt; std::unique_ptr mLegendEta; + std::unique_ptr mLegendPtSec; + std::unique_ptr mLegendEtaSec; + std::unique_ptr mLegendPtRes; + std::unique_ptr mLegendPtRes2; + std::unique_ptr mLegendZ; + std::unique_ptr mLegendRad; + std::unique_ptr mLegendZD; + std::unique_ptr mLegendRadD; + std::vector Histo; + + float rLayer0 = 2.34; // middle radius + float rLayer1 = 3.15; + float rLayer2 = 3.93; + float rLayer3 = 19.605; + + double sigma[100]; + double sigmaerr[100]; + double meanPt[100]; + double aa[100]; // Debug output tree std::unique_ptr mDBGOut; @@ -146,13 +226,19 @@ void TrackCheckStudy::init(InitContext& ic) for (int i{0}; i <= pars.effHistBins; i++) { xbins[i] = pars.effPtCutLow * std::exp(i * a); } - + for (int yy = 0; yy < 50; yy++) { + ProcessName[yy] = " "; + } mGoodPt = std::make_unique("goodPt", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); mGoodEta = std::make_unique("goodEta", ";#eta;Number of tracks", 60, -3, 3); + mGoodPtSec = std::make_unique("goodPtSec", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mGoodEtaSec = std::make_unique("goodEtaSec", ";#eta;Number of tracks", 60, -3, 3); mGoodChi2 = std::make_unique("goodChi2", ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", 200, 0, 100); mFakePt = std::make_unique("fakePt", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); mFakeEta = std::make_unique("fakeEta", ";#eta;Number of tracks", 60, -3, 3); + mFakePtSec = std::make_unique("fakePtSec", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); + mFakeEtaSec = std::make_unique("fakeEtaSec", ";#eta;Number of tracks", 60, -3, 3); mFakeChi2 = std::make_unique("fakeChi2", ";#it{p}_{T} (GeV/#it{c});Fak", 200, 0, 100); mMultiFake = std::make_unique("multiFake", ";#it{p}_{T} (GeV/#it{c});Fak", pars.effHistBins, xbins.data()); @@ -162,13 +248,174 @@ void TrackCheckStudy::init(InitContext& ic) mDenominatorPt = std::make_unique("denominatorPt", ";#it{p}_{T} (GeV/#it{c});Den", pars.effHistBins, xbins.data()); mDenominatorEta = std::make_unique("denominatorEta", ";#eta;Number of tracks", 60, -3, 3); + mDenominatorPtSec = std::make_unique("denominatorPtSec", ";#it{p}_{T} (GeV/#it{c});Den", pars.effHistBins, xbins.data()); + mDenominatorEtaSec = std::make_unique("denominatorEtaSec", ";#eta;Number of tracks", 60, -3, 3); + + processvsZ = std::make_unique("Process", ";z_{SV} [cm]; production process", 100, -50, 50., 50, 0, 50); + processvsRad = std::make_unique("ProcessR", ";decay radius [cm]; production process", 100, 0, 25., 50, 0, 50); + processvsRadOther = std::make_unique("ProcessRO", ";decay radius [cm]; production process", 200, 0, 25., 50, 0, 50); + processvsRadNotTracked = std::make_unique("ProcessRNoT", ";decay radius [cm]; production process", 200, 0, 25., 50, 0, 50); + processvsEtaNotTracked = std::make_unique("ProcessENoT", ";#eta; production process", 60, -3, 3, 50, 0, 50); + + mGoodPts.resize(4); + mFakePts.resize(4); + mTotPts.resize(4); + mGoodEtas.resize(4); + mFakeEtas.resize(4); + mTotEtas.resize(4); + mGoodRad.resize(4); + mFakeRad.resize(4); + mTotRad.resize(4); + mGoodZ.resize(4); + mFakeZ.resize(4); + mTotZ.resize(4); + mClusterFake.resize(4); + for (int i = 0; i < 4; i++) { + mGoodPts[i].resize(4); + mFakePts[i].resize(4); + mTotPts[i].resize(4); + mGoodEtas[i].resize(4); + mFakeEtas[i].resize(4); + mTotEtas[i].resize(4); + } + for (int ii = 0; ii < 4; ii++) { + + mGoodRad[ii] = std::make_unique(Form("goodRad_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, 0., 20.); + mFakeRad[ii] = std::make_unique(Form("FakeRad_%s", particleToanalize[ii]), ";#eta;Number of tracks", 100, 0., 20.); + mTotRad[ii] = std::make_unique(Form("TotRad_%s", particleToanalize[ii]), ";#eta;Number of tracks", 100, 0., 20.); + + mGoodZ[ii] = std::make_unique(Form("goodZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mFakeZ[ii] = std::make_unique(Form("FakeZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mTotZ[ii] = std::make_unique(Form("TotZ_%s", particleToanalize[ii]), ";z_{SV} [cm];Number of tracks", 100, -50., 50.); + mClusterFake[ii] = std::make_unique(Form("Clusters_fake_%s", ParticleName[ii]), ";particle generating fake cluster; production process", 7, 0., 7., 50, 0, 50); + + mGoodRad[ii]->Sumw2(); + mFakeRad[ii]->Sumw2(); + mTotRad[ii]->Sumw2(); + mGoodZ[ii]->Sumw2(); + mFakeZ[ii]->Sumw2(); + mTotZ[ii]->Sumw2(); + + for (int yy = 0; yy < 4; yy++) { // divided by layer + mGoodPts[ii][yy] = std::make_unique(Form("goodPts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mFakePts[ii][yy] = std::make_unique(Form("FakePts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + mTotPts[ii][yy] = std::make_unique(Form("TotPts_%s_%d", particleToanalize[ii], yy), ";#it{p}_{T} (GeV/#it{c});Efficiency (fake-track rate)", pars.effHistBins, xbins.data()); + + mGoodEtas[ii][yy] = std::make_unique(Form("goodEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + mFakeEtas[ii][yy] = std::make_unique(Form("FakeEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + mTotEtas[ii][yy] = std::make_unique(Form("TotEtas_%s_%d", particleToanalize[ii], yy), ";#eta;Number of tracks", 60, -3, 3); + + mGoodPts[ii][yy]->Sumw2(); + mFakePts[ii][yy]->Sumw2(); + mTotPts[ii][yy]->Sumw2(); + mGoodEtas[ii][yy]->Sumw2(); + mFakeEtas[ii][yy]->Sumw2(); + mTotEtas[ii][yy]->Sumw2(); + } + } + + mPtResolution = std::make_unique("PtResolution", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolutionSec = std::make_unique("PtResolutionSec", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolutionPrim = std::make_unique("PtResolutionPrim", ";#it{p}_{T} ;Den", 100, -1, 1); + mPtResolution2D = std::make_unique("#it{p}_{T} Resolution vs #it{p}_{T}", ";#it{p}_{T} (GeV/#it{c});#Delta p_{T}/p_{T_{MC}", 100, 0, 10, 100, -1, 1); + + mPtResolution->Sumw2(); + mPtResolutionSec->Sumw2(); + mPtResolutionPrim->Sumw2(); mGoodPt->Sumw2(); mGoodEta->Sumw2(); + mGoodPtSec->Sumw2(); + mGoodEtaSec->Sumw2(); + mFakePt->Sumw2(); + mFakePtSec->Sumw2(); + mFakeEta->Sumw2(); mMultiFake->Sumw2(); mClonePt->Sumw2(); mDenominatorPt->Sumw2(); + + histLength.resize(4); // fake clusters study + histLength1Fake.resize(4); + histLength2Fake.resize(4); + histLength3Fake.resize(4); + histLengthNoCl.resize(4); + histLength1FakeNoCl.resize(4); + histLength2FakeNoCl.resize(4); + histLength3FakeNoCl.resize(4); + stackLength.resize(4); + stackLength1Fake.resize(4); + stackLength2Fake.resize(4); + stackLength3Fake.resize(4); + for (int yy = 0; yy < 4; yy++) { + histLength[yy].resize(3); + histLength1Fake[yy].resize(3); + histLength2Fake[yy].resize(3); + histLength3Fake[yy].resize(3); + histLengthNoCl[yy].resize(3); + histLength1FakeNoCl[yy].resize(3); + histLength2FakeNoCl[yy].resize(3); + histLength3FakeNoCl[yy].resize(3); + } + legends.resize(4); + legends1Fake.resize(4); + legends2Fake.resize(4); + legends3Fake.resize(4); + + for (int iH{4}; iH < 8; ++iH) { + // check distributions on layers of fake clusters for tracks of different lengths. + // Different histograms if the correct cluster exist or not + for (int jj = 0; jj < 3; jj++) { + histLength[iH - 4][jj] = new TH1I(Form("trk_len_%d_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLengthNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLengthNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLengthNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength[iH - 4] = new THStack(Form("stack_trk_len_%d", iH), Form("trk_len=%d", iH)); + } + stackLength[iH - 4]->Add(histLength[iH - 4][jj]); + stackLength[iH - 4]->Add(histLengthNoCl[iH - 4][jj]); + + histLength1Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_1f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength1Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength1Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLength1FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_1f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength1FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength1FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength1Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_1f", iH), Form("trk_len=%d, 1 Fake", iH)); + } + stackLength1Fake[iH - 4]->Add(histLength1Fake[iH - 4][jj]); + stackLength1Fake[iH - 4]->Add(histLength1FakeNoCl[iH - 4][jj]); + + histLength2Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_2f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength2Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength2Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + histLength2FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_2f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength2FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength2FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength2Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_2f", iH), Form("trk_len=%d, 2 Fake", iH)); + } + stackLength2Fake[iH - 4]->Add(histLength2Fake[iH - 4][jj]); + stackLength2Fake[iH - 4]->Add(histLength2FakeNoCl[iH - 4][jj]); + + histLength3Fake[iH - 4][jj] = new TH1I(Form("trk_len_%d_3f_%s", iH, name[jj]), Form("#exists cluster %s", name[jj]), 7, -.5, 6.5); + histLength3Fake[iH - 4][jj]->SetFillColor(colorArr[jj] - 9); + histLength3Fake[iH - 4][jj]->SetLineColor(colorArr[jj] - 9); + + histLength3FakeNoCl[iH - 4][jj] = new TH1I(Form("trk_len_%d_3f_nocl_%s", iH, name[jj]), Form("#slash{#exists} cluster %s", name[jj]), 7, -.5, 6.5); + histLength3FakeNoCl[iH - 4][jj]->SetFillColor(colorArr[jj] + 1); + histLength3FakeNoCl[iH - 4][jj]->SetLineColor(colorArr[jj] + 1); + if (jj == 0) { + stackLength3Fake[iH - 4] = new THStack(Form("stack_trk_len_%d_3f", iH), Form("trk_len=%d, 3 Fake", iH)); + } + stackLength3Fake[iH - 4]->Add(histLength3Fake[iH - 4][jj]); + stackLength3Fake[iH - 4]->Add(histLength3FakeNoCl[iH - 4][jj]); + } + } } void TrackCheckStudy::run(ProcessingContext& pc) @@ -188,6 +435,7 @@ void TrackCheckStudy::initialiseRun(o2::globaltracking::RecoContainer& recoData) mTracksMCLabels = recoData.getITSTracksMCLabels(); mClusters = recoData.getITSClusters(); mClustersMCLCont = recoData.getITSClustersMCLabels(); + mInputITSidxs = recoData.getITSTracksClusterRefs(); LOGP(info, "** Found in {} rofs:\n\t- {} clusters with {} labels\n\t- {} tracks with {} labels", mTracksROFRecords.size(), mClusters.size(), mClustersMCLCont->getIndexedSize(), mTracks.size(), mTracksMCLabels.size()); @@ -209,7 +457,13 @@ void TrackCheckStudy::process() mParticleInfo[iSource][iEvent][iPart].pt = part.GetPt(); mParticleInfo[iSource][iEvent][iPart].phi = part.GetPhi(); mParticleInfo[iSource][iEvent][iPart].eta = part.GetEta(); + mParticleInfo[iSource][iEvent][iPart].vx = part.Vx(); + mParticleInfo[iSource][iEvent][iPart].vy = part.Vy(); + mParticleInfo[iSource][iEvent][iPart].vz = part.Vz(); mParticleInfo[iSource][iEvent][iPart].isPrimary = part.isPrimary(); + mParticleInfo[iSource][iEvent][iPart].mother = part.getMotherTrackId(); + mParticleInfo[iSource][iEvent][iPart].prodProcessName = part.getProdProcessAsString(); + mParticleInfo[iSource][iEvent][iPart].prodProcess = part.getProcess(); } } } @@ -231,7 +485,23 @@ void TrackCheckStudy::process() } } LOGP(info, "** Analysing tracks ... "); - int unaccounted{0}, good{0}, fakes{0}, total{0}; + int unaccounted{0}, good{0}, fakes{0}; + // ***secondary tracks*** + int nPartForSpec[4][4]; // total number [particle 0=IperT, 1=Lambda, 2=k, 3=Other][n layer] + int nPartGoodorFake[4][4][2]; // number of good or fake [particle 0=IperT, 1=Lambda, 2=k, 3=Other][n layer][good=1 fake=0] + for (int n = 0; n < 4; n++) { + for (int m = 0; m < 4; m++) { + nPartForSpec[n][m] = 0; + for (int h = 0; h < 2; h++) { + nPartGoodorFake[n][m][h] = 0; + } + } + } + int nlayer = 999; + int ngoodfake = 0; + int totsec = 0; + int totsecCont = 0; + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { auto& lab = mTracksMCLabels[iTrack]; if (!lab.isSet() || lab.isNoise()) { @@ -241,7 +511,6 @@ void TrackCheckStudy::process() int trackID, evID, srcID; bool fake; const_cast(lab).get(trackID, evID, srcID, fake); - bool pass{true}; if (srcID == 99) { // skip QED unaccounted++; @@ -262,43 +531,257 @@ void TrackCheckStudy::process() LOGP(info, "\t- Total number of tracks not corresponding to particles: {} ({:.2f} %)", unaccounted, unaccounted * 100. / mTracks.size()); LOGP(info, "\t- Total number of fakes: {} ({:.2f} %)", fakes, fakes * 100. / mTracks.size()); LOGP(info, "\t- Total number of good: {} ({:.2f} %)", good, good * 100. / mTracks.size()); - LOGP(info, "** Filling histograms ... "); + LOGP(info, "** Filling histograms ... "); + int evID = 0; + int trackID = 0; + int totP{0}, goodP{0}, fakeP{0}; // Currently process only sourceID = 0, to be extended later if needed for (auto& evInfo : mParticleInfo[0]) { + trackID = 0; for (auto& part : evInfo) { - if ((part.clusters & 0x7f) != mMask) { + + if (strcmp(ProcessName[part.prodProcess], " ")) { + ProcessName[part.prodProcess] = part.prodProcessName; + } + if ((part.clusters & 0x7f) == mMask) { // part.clusters != 0x3f && part.clusters != 0x3f << 1 && // part.clusters != 0x1f && part.clusters != 0x1f << 1 && part.clusters != 0x1f << 2 && // part.clusters != 0x0f && part.clusters != 0x0f << 1 && part.clusters != 0x0f << 2 && part.clusters != 0x0f << 3) { - continue; - } - if (!part.isPrimary) { - continue; - } - mDenominatorPt->Fill(part.pt); - mDenominatorEta->Fill(part.eta); - if (part.isReco) { - mGoodPt->Fill(part.pt); - mGoodEta->Fill(part.eta); - if (part.isReco > 1) { - for (int _i{0}; _i < part.isReco - 1; ++_i) { - mClonePt->Fill(part.pt); - mCloneEta->Fill(part.eta); + // continue; + + if (part.isPrimary) { // **Primary particle** + totP++; + mDenominatorPt->Fill(part.pt); + mDenominatorEta->Fill(part.eta); + if (part.isReco) { + mGoodPt->Fill(part.pt); + mGoodEta->Fill(part.eta); + goodP++; + if (part.isReco > 1) { + for (int _i{0}; _i < part.isReco - 1; ++_i) { + mClonePt->Fill(part.pt); + mCloneEta->Fill(part.eta); + } + } + } + if (part.isFake) { + mFakePt->Fill(part.pt); + mFakeEta->Fill(part.eta); + fakeP++; + if (part.isFake > 1) { + for (int _i{0}; _i < part.isFake - 1; ++_i) { + mMultiFake->Fill(part.pt); + } + } } } } - if (part.isFake) { - mFakePt->Fill(part.pt); - mFakeEta->Fill(part.eta); - if (part.isFake > 1) { - for (int _i{0}; _i < part.isFake - 1; ++_i) { - mMultiFake->Fill(part.pt); + + // **Secondary particle** + nlayer = 999; + ngoodfake = 2; + if (!part.isPrimary) { + int TrackID, EvID, SrcID; + int pdgcode = mParticleInfo[0][evID][part.mother].pdg; + int idxPart = 999; + float rad = sqrt(pow(part.vx, 2) + pow(part.vy, 2)); + totsec++; + + if ((rad < rLayer0) && (part.clusters == 0x7f || part.clusters == 0x3f || part.clusters == 0x1f || part.clusters == 0x0f)) { // layer 0 + nlayer = 0; + } + if (rad < rLayer1 && rad > rLayer0 && (part.clusters == 0x1e || part.clusters == 0x3e || part.clusters == 0x7e)) { // layer 1 + nlayer = 1; + } + if (rad < rLayer2 && rad > rLayer1 && (part.clusters == 0x7c || part.clusters == 0x3c)) { // layer 2 + nlayer = 2; + } + if (rad < rLayer3 && rad > rLayer2 && part.clusters == 0x78) { // layer 3 + nlayer = 3; + } + if (nlayer == 0 || nlayer == 1 || nlayer == 2 || nlayer == 3) { // check if track is trackeable + + totsecCont++; + processvsZ->Fill(part.vz, part.prodProcess); + processvsRad->Fill(rad, part.prodProcess); + mDenominatorPtSec->Fill(part.pt); + mDenominatorEtaSec->Fill(part.eta); + mTotRad[3]->Fill(rad); + mTotZ[3]->Fill(part.vz); + mTotPts[nlayer][3]->Fill(part.pt); + mTotEtas[nlayer][3]->Fill(part.eta); + mTotPts[nlayer][3]->Fill(part.pt); + mTotEtas[nlayer][3]->Fill(part.eta); + if (pdgcode == PDG[0] || pdgcode == -1 * PDG[0]) { + idxPart = 0; // IperT + } + if (pdgcode == PDG[1] || pdgcode == -1 * PDG[1]) { + idxPart = 1; // Lambda + } + if (pdgcode == PDG[2] || pdgcode == -1 * PDG[2]) { + idxPart = 2; // K0s + } + if (part.isReco) { + ngoodfake = 1; + mGoodPts[3][nlayer]->Fill(part.pt); + mGoodEtas[3][nlayer]->Fill(part.eta); + mGoodPtSec->Fill(part.pt); + mGoodEtaSec->Fill(part.eta); + mGoodRad[3]->Fill(rad); + mGoodZ[3]->Fill(part.vz); + } + if (part.isFake) { + ngoodfake = 0; + mFakePts[3][nlayer]->Fill(part.pt); + mFakeEtas[3][nlayer]->Fill(part.eta); + mFakePtSec->Fill(part.pt); + mFakeEtaSec->Fill(part.eta); + mFakeRad[3]->Fill(rad); + mFakeZ[3]->Fill(part.vz); + } + if (idxPart < 3) // to change if the number of analysing particle changes + { + mTotRad[idxPart]->Fill(rad); + mTotZ[idxPart]->Fill(part.vz); + mTotPts[idxPart][nlayer]->Fill(part.pt); + mTotEtas[idxPart][nlayer]->Fill(part.eta); + if (part.isReco) { + mGoodRad[idxPart]->Fill(rad); + mGoodZ[idxPart]->Fill(part.vz); + mGoodPts[idxPart][nlayer]->Fill(part.pt); + mGoodEtas[idxPart][nlayer]->Fill(part.eta); + } + if (part.isFake) { + mFakeRad[idxPart]->Fill(rad); + mFakeZ[idxPart]->Fill(part.vz); + mFakePts[idxPart][nlayer]->Fill(part.pt); + mFakeEtas[idxPart][nlayer]->Fill(part.eta); + } + } + + if (pdgcode != 1010010030 && pdgcode != 3122 && pdgcode != 310 && pdgcode != -1010010030 && pdgcode != -310 && pdgcode != -3122) { + idxPart = 3; + processvsRadOther->Fill(rad, part.prodProcess); + } + + if (!part.isFake && !part.isReco) { + processvsEtaNotTracked->Fill(part.eta, part.prodProcess); + processvsRadNotTracked->Fill(rad, part.prodProcess); + } + if (ngoodfake == 1 || ngoodfake == 0) { + nPartGoodorFake[idxPart][nlayer][ngoodfake]++; + } + nPartForSpec[idxPart][nlayer]++; + + // Analysing fake clusters + int nCl{0}; + for (unsigned int bit{0}; bit < sizeof(part.clusters) * 8; ++bit) { + nCl += bool(part.clusters & (1 << bit)); + } + if (nCl < 3) { + continue; + } + if (idxPart < 3) { + auto& track = part.track; + auto len = track.getNClusters(); + int nclu = track.getNumberOfClusters(); + int firstclu = track.getFirstClusterEntry(); + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (track.hasHitOnLayer(iLayer)) { + if (track.isFakeOnLayer(iLayer)) { + // Reco track has fake cluster + if (part.clusters & (0x1 << iLayer)) { // Correct cluster exists + histLength[len - 4][idxPart]->Fill(iLayer); + if (track.getNFakeClusters() == 1) { + histLength1Fake[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 2) { + histLength2Fake[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 3) { + histLength3Fake[len - 4][idxPart]->Fill(iLayer); + } + } else { + + histLengthNoCl[len - 4][idxPart]->Fill(iLayer); + if (track.getNFakeClusters() == 1) { + histLength1FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 2) { + histLength2FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + if (track.getNFakeClusters() == 3) { + histLength3FakeNoCl[len - 4][idxPart]->Fill(iLayer); + } + } + auto labs = mClustersMCLCont->getLabels(mInputITSidxs[firstclu - 1 - iLayer + track.getFirstClusterLayer() + nclu]); + + for (auto& lab : labs) { + if (!lab.isValid()) { + continue; // We want to skip channels related to noise, e.g. sID = 99: QED + } + + bool fakec; + const_cast(lab).get(TrackID, EvID, SrcID, fakec); + double intHisto = 0; + for (int hg = 0; hg < 7; hg++) { + if (mParticleInfo[SrcID][EvID][TrackID].pdg == PdgcodeClusterFake[hg] || mParticleInfo[SrcID][EvID][TrackID].pdg == -1 * (PdgcodeClusterFake[hg])) { + intHisto = hg + 0.5; + } + } + if (idxPart < 3) { + mClusterFake[idxPart]->Fill(intHisto, mParticleInfo[SrcID][EvID][TrackID].prodProcess); + } + } + } + } + } } } + nlayer = 999; } + trackID++; } + evID++; } + + int totgood{0}, totfake{0}, totI{0}, totL{0}, totK{0}, totO{0}; + for (int xx = 0; xx < 4; xx++) { + for (int yy = 0; yy < 4; yy++) { + totgood = totgood + nPartGoodorFake[xx][yy][1]; + totfake = totfake + nPartGoodorFake[xx][yy][0]; + if (xx == 0) { + totI = totI + nPartForSpec[0][yy]; + } + if (xx == 1) { + totL = totL + nPartForSpec[1][yy]; + } + if (xx == 2) { + totK = totK + nPartForSpec[2][yy]; + } + if (xx == 3) { + totO = totO + nPartForSpec[3][yy]; + } + } + } + LOGP(info, "number of primary tracks: {}, good:{}, fake:{}", totP, goodP, fakeP); + int goodI = nPartGoodorFake[0][0][1] + nPartGoodorFake[0][1][1] + nPartGoodorFake[0][2][1] + nPartGoodorFake[0][3][1]; + int goodL = nPartGoodorFake[1][0][1] + nPartGoodorFake[1][1][1] + nPartGoodorFake[1][2][1] + nPartGoodorFake[1][3][1]; + int goodK = nPartGoodorFake[2][0][1] + nPartGoodorFake[2][1][1] + nPartGoodorFake[2][2][1] + nPartGoodorFake[2][3][1]; + int fakeI = nPartGoodorFake[0][0][0] + nPartGoodorFake[0][1][0] + nPartGoodorFake[0][2][0] + nPartGoodorFake[0][3][0]; + int fakeL = nPartGoodorFake[1][0][0] + nPartGoodorFake[1][1][0] + nPartGoodorFake[1][2][0] + nPartGoodorFake[1][3][0]; + int fakeK = nPartGoodorFake[2][0][0] + nPartGoodorFake[2][1][0] + nPartGoodorFake[2][2][0] + nPartGoodorFake[2][3][0]; + LOGP(info, "** Some statistics on secondary tracks:"); + + LOGP(info, "\t- Total number of secondary tracks: {}", totsec); + LOGP(info, "\t- Total number of secondary trackeable tracks : {}", totsecCont); + LOGP(info, "\t- Total number of secondary trackeable tracks good: {}, fake: {}", totgood, totfake); + LOGP(info, "\t- Total number of secondary trackeable tracks from IperT: {} = {} %, Good={} % , fake={} %", totI, 100 * totI / totsecCont, 100 * goodI / totI, 100 * fakeI / totI); + LOGP(info, "\t- Total number of secondary trackeable tracks from Lam: {} = {} %, Good={} % , fake={} %", totL, 100 * totL / totsecCont, 100 * goodL / totL, 100 * fakeL / totL); + LOGP(info, "\t- Total number of secondary trackeable tracks from k: {} = {} %, Good={} % , fake={} %", totK, 100 * totK / totsecCont, 100 * goodK / totK, 100 * fakeK / totK); + LOGP(info, "\t- Total number of secondary trackeable tracks from Other: {} = {} %", totO, 100 * totO / totsecCont); + LOGP(info, "** Computing efficiencies ..."); mEffPt = std::make_unique(*mGoodPt, *mDenominatorPt); @@ -308,6 +791,78 @@ void TrackCheckStudy::process() mEffEta = std::make_unique(*mGoodEta, *mDenominatorEta); mEffFakeEta = std::make_unique(*mFakeEta, *mDenominatorEta); mEffClonesEta = std::make_unique(*mCloneEta, *mDenominatorEta); + + mEffPtSec = std::make_unique(*mGoodPtSec, *mDenominatorPtSec); + mEffFakePtSec = std::make_unique(*mFakePtSec, *mDenominatorPtSec); + + mEffEtaSec = std::make_unique(*mGoodEtaSec, *mDenominatorEtaSec); + mEffFakeEtaSec = std::make_unique(*mFakeEtaSec, *mDenominatorEtaSec); + + for (int ii = 0; ii < 4; ii++) { + for (int yy = 0; yy < 4; yy++) { + mEffGoodPts[ii][yy] = std::make_unique(*mGoodPts[ii][yy], *mTotPts[ii][yy]); + mEffFakePts[ii][yy] = std::make_unique(*mFakePts[ii][yy], *mTotPts[ii][yy]); + mEffGoodEtas[ii][yy] = std::make_unique(*mGoodEtas[ii][yy], *mTotEtas[ii][yy]); + mEffFakeEtas[ii][yy] = std::make_unique(*mFakeEtas[ii][yy], *mTotEtas[ii][yy]); + } + mEffGoodRad[ii] = std::make_unique(*mGoodRad[ii], *mTotRad[ii]); + mEffFakeRad[ii] = std::make_unique(*mFakeRad[ii], *mTotRad[ii]); + mEffGoodZ[ii] = std::make_unique(*mGoodZ[ii], *mTotZ[ii]); + mEffFakeZ[ii] = std::make_unique(*mFakeZ[ii], *mTotZ[ii]); + } + + LOGP(info, "** Analysing pT resolution..."); + for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { + auto& lab = mTracksMCLabels[iTrack]; + if (!lab.isSet() || lab.isNoise()) { + continue; + } + int trackID, evID, srcID; + bool fake; + const_cast(lab).get(trackID, evID, srcID, fake); + if (srcID == 99) { + continue; // skip QED + } + mPtResolution->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + mPtResolution2D->Fill(mParticleInfo[srcID][evID][trackID].pt, (mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + if (!mParticleInfo[srcID][evID][trackID].isPrimary) { + mPtResolutionSec->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + } + mPtResolutionPrim->Fill((mParticleInfo[srcID][evID][trackID].pt - mTracks[iTrack].getPt()) / mParticleInfo[srcID][evID][trackID].pt); + } + + for (int yy = 0; yy < 100; yy++) { + aa[yy] = 0.; + sigma[yy] = 0.; + sigmaerr[yy] = 0.; + meanPt[yy] = 0.; + } + + for (int yy = 0; yy < 100; yy++) { + TH1D* projh2X = mPtResolution2D->ProjectionY("projh2X", yy, yy + 1, ""); + TF1* f1 = new TF1("f1", "gaus", -0.2, 0.2); + projh2X->Fit("f1"); + if (f1->GetParameter(2) > 0. && f1->GetParameter(2) < 1. && f1->GetParameter(1) < 1.) { + sigma[yy] = f1->GetParameter(2); + sigmaerr[yy] = f1->GetParError(2); + meanPt[yy] = ((8. / 100.) * yy + (8. / 100.) * (yy + 1)) / 2; + aa[yy] = 0.0125; + } + } +} + +void TrackCheckStudy::setEfficiencyGraph(std::unique_ptr& eff, const char* name, const char* title, const int color, const double alpha = 1, const double linew = 2, const int markerStyle = kFullCircle, const double markersize = 1.7) +{ + eff->SetName(name); + eff->SetTitle(title); + eff->SetLineColor(color); + eff->SetLineColorAlpha(color, alpha); + eff->SetMarkerColor(color); + eff->SetMarkerColorAlpha(color, alpha); + eff->SetLineWidth(linew); + eff->SetMarkerStyle(markerStyle); + eff->SetMarkerSize(markersize); + eff->SetDirectory(gDirectory); } void TrackCheckStudy::updateTimeDependentParams(ProcessingContext& pc) @@ -324,73 +879,108 @@ void TrackCheckStudy::updateTimeDependentParams(ProcessingContext& pc) void TrackCheckStudy::endOfStream(EndOfStreamContext& ec) { TFile fout(mOutFileName.c_str(), "recreate"); - mEffPt->SetName("Good_pt"); - mEffPt->SetTitle(";#it{p}_{T} (GeV/#it{c});efficiency primary particle"); - mEffPt->SetLineColor(kAzure + 4); - mEffPt->SetLineColorAlpha(kAzure + 4, 0.65); - mEffPt->SetLineWidth(2); - mEffPt->SetMarkerColorAlpha(kAzure + 4, 0.65); - mEffPt->SetMarkerStyle(kFullCircle); - mEffPt->SetMarkerSize(1.35); - mEffPt->SetDirectory(gDirectory); + + setEfficiencyGraph(mEffPt, "Good_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kAzure + 4, 0.65); fout.WriteTObject(mEffPt.get()); - mEffFakePt->SetName("Fake_pt"); - mEffFakePt->SetTitle(";#it{p}_{T} (GeV/#it{c});efficiency primary particle"); - mEffFakePt->SetLineColor(kRed + 1); - mEffFakePt->SetLineColorAlpha(kRed + 1, 0.65); - mEffFakePt->SetLineWidth(2); - mEffFakePt->SetMarkerColorAlpha(kRed + 1, 0.65); - mEffFakePt->SetMarkerStyle(kFullCircle); - mEffFakePt->SetMarkerSize(1.35); - mEffFakePt->SetDirectory(gDirectory); + setEfficiencyGraph(mEffFakePt, "Fake_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kRed, 0.65); fout.WriteTObject(mEffFakePt.get()); - mEffClonesPt->SetName("Clone_pt"); - mEffClonesPt->SetTitle(";#it{p}_{T} (GeV/#it{c});efficiency primary particle"); - mEffClonesPt->SetLineColor(kGreen + 2); - mEffClonesPt->SetLineColorAlpha(kGreen + 2, 0.65); - mEffClonesPt->SetLineWidth(2); - mEffClonesPt->SetMarkerColorAlpha(kGreen + 2, 0.65); + setEfficiencyGraph(mEffPtSec, "Good_ptSec", ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle", kOrange + 7); + fout.WriteTObject(mEffPtSec.get()); - mEffClonesPt->SetMarkerStyle(kFullCircle); - mEffClonesPt->SetMarkerSize(1.35); - mEffClonesPt->SetDirectory(gDirectory); + setEfficiencyGraph(mEffFakePtSec, "Fake_ptSec", ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle", kGray + 2); + fout.WriteTObject(mEffFakePtSec.get()); + + setEfficiencyGraph(mEffClonesPt, "Clone_pt", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kGreen + 2, 0.65); fout.WriteTObject(mEffClonesPt.get()); - mEffEta->SetName("Good_eta"); - mEffEta->SetTitle(";#eta;efficiency primary particle"); - mEffEta->SetLineColor(kAzure + 4); - mEffEta->SetLineColorAlpha(kAzure + 4, 0.65); - mEffEta->SetLineWidth(2); - mEffEta->SetMarkerColorAlpha(kAzure + 4, 0.65); - mEffEta->SetMarkerStyle(kFullCircle); - mEffEta->SetMarkerSize(1.35); - mEffEta->SetDirectory(gDirectory); + setEfficiencyGraph(mEffEta, "Good_eta", ";#eta;efficiency primary particle", kAzure + 4, 0.65); fout.WriteTObject(mEffEta.get()); - mEffFakeEta->SetName("Fake_eta"); - mEffFakeEta->SetTitle(";#eta;efficiency primary particle"); - mEffFakeEta->SetLineColor(kRed + 1); - mEffFakeEta->SetLineColorAlpha(kRed + 1, 0.65); - mEffFakeEta->SetLineWidth(2); - mEffFakeEta->SetMarkerColorAlpha(kRed + 1, 0.65); - mEffFakeEta->SetMarkerStyle(kFullCircle); - mEffFakeEta->SetMarkerSize(1.35); - mEffFakeEta->SetDirectory(gDirectory); + setEfficiencyGraph(mEffFakeEta, "Fake_eta", ";#eta;efficiency primary particle", kRed + 1, 0.65); fout.WriteTObject(mEffFakeEta.get()); - mEffClonesEta->SetName("Clone_eta"); - mEffClonesEta->SetTitle(";#eta;efficiency primary particle"); - mEffClonesEta->SetLineColor(kGreen + 2); - mEffClonesEta->SetLineColorAlpha(kGreen + 2, 0.65); - mEffClonesEta->SetLineWidth(2); - mEffClonesEta->SetMarkerColorAlpha(kGreen + 2, 0.65); - mEffClonesEta->SetMarkerStyle(kFullCircle); - mEffClonesEta->SetMarkerSize(1.35); - mEffClonesEta->SetDirectory(gDirectory); + setEfficiencyGraph(mEffEtaSec, "Good_etaSec", ";#eta;efficiency secondary particle", kOrange + 7); + fout.WriteTObject(mEffEtaSec.get()); + + setEfficiencyGraph(mEffFakeEtaSec, "Fake_etaSec", ";#eta;efficiency secondary particle", kGray + 2); + fout.WriteTObject(mEffFakeEtaSec.get()); + + setEfficiencyGraph(mEffClonesEta, "Clone_eta", ";#it{p}_{T} (GeV/#it{c});efficiency primary particle", kGreen + 2, 0.65); fout.WriteTObject(mEffClonesEta.get()); + for (int aa = 0; aa < 4; aa++) { + setEfficiencyGraph(mEffGoodRad[aa], Form("Good_Rad_%s", particleToanalize[aa]), ";Radius [cm];efficiency secondary particle", colorArr[aa]); + fout.WriteTObject(mEffGoodRad[aa].get()); + + setEfficiencyGraph(mEffGoodRad[aa], Form("Fake_Rad_%s", particleToanalize[aa]), ";Radius [cm];efficiency secondary particle", colorArr[aa] - 9); + fout.WriteTObject(mEffGoodRad[aa].get()); + + setEfficiencyGraph(mEffGoodZ[aa], Form("Good_Z_%s", particleToanalize[aa]), ";Z_{sv} [cm];efficiency secondary particle", colorArr[aa]); + fout.WriteTObject(mEffGoodZ[aa].get()); + + setEfficiencyGraph(mEffGoodZ[aa], Form("Fake_Z_%s", particleToanalize[aa]), ";Z_{sv} [cm];efficiency secondary particle", colorArr[aa] - 9); + fout.WriteTObject(mEffGoodZ[aa].get()); + + for (int bb = 0; bb < 4; bb++) { + setEfficiencyGraph(mEffGoodPts[aa][bb], Form("EffPtGood_%sl%d", particleToanalize[aa], bb), Form("Good Sec Tracks_%s, L%d" + ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffFakePts[aa][bb], Form("EffPtFake_%sl%d", particleToanalize[aa], bb), Form("Fake Sec Tracks_%s, L%d" + ";#it{p}_{T} (GeV/#it{c});efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffGoodEtas[aa][bb], Form("EffEtaGood_%sl%d", particleToanalize[aa], bb), Form("Good Sec Tracks_%s, L%d" + ";#eta ;efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + setEfficiencyGraph(mEffFakeEtas[aa][bb], Form("EffEtaFake_%sl%d", particleToanalize[aa], bb), Form("Fake Sec Tracks_%s, L%d" + ";#eta ;efficiency secondary particle ", + particleToanalize[aa], bb), + colorArr[aa]); + + fout.WriteTObject(mEffGoodPts[aa][bb].get()); + fout.WriteTObject(mEffFakePts[aa][bb].get()); + fout.WriteTObject(mEffGoodEtas[aa][bb].get()); + fout.WriteTObject(mEffFakeEtas[aa][bb].get()); + } + for (int i = 0; i < 3; i++) { + fout.WriteTObject(histLength[aa][i], Form("trk_len_%d_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength1Fake[aa][i], Form("trk_len_%d_1f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength2Fake[aa][i], Form("trk_len_%d_2f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength3Fake[aa][i], Form("trk_len_%d_3f_%s", 4 + aa, name[i])); + fout.WriteTObject(histLengthNoCl[aa][i], Form("trk_len_%d_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength1FakeNoCl[aa][i], Form("trk_len_%d_1f_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength2FakeNoCl[aa][i], Form("trk_len_%d_2f_nocl_%s", 4 + aa, name[i])); + fout.WriteTObject(histLength3FakeNoCl[aa][i], Form("trk_len_%d_3f_nocl_%s", 4 + aa, name[i])); + } + } + + for (int j = 0; j < 4; j++) { + for (int i = 1; i <= 7; i++) { + mClusterFake[j]->GetXaxis()->SetBinLabel(i, ParticleName[i - 1]); + + for (int i = 1; i <= 50; i++) { + mClusterFake[j]->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + if (j == 0) { + processvsZ->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRad->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRadOther->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsRadNotTracked->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + processvsEtaNotTracked->GetYaxis()->SetBinLabel(i, ProcessName[i - 1]); + } + } + fout.WriteTObject(mClusterFake[j].get()); + } + } + fout.WriteTObject(processvsZ.get()); + fout.WriteTObject(processvsRad.get()); + fout.WriteTObject(processvsRadOther.get()); + fout.WriteTObject(processvsRadNotTracked.get()); + fout.WriteTObject(processvsEtaNotTracked.get()); + // Paint the histograms // todo: delegate to a dedicated helper gStyle->SetTitleSize(0.035, "xy"); @@ -414,13 +1004,26 @@ void TrackCheckStudy::endOfStream(EndOfStreamContext& ec) mEffFakePt->Draw("pz same"); mEffClonesPt->Draw("pz same"); mLegendPt = std::make_unique(0.19, 0.8, 0.40, 0.96); - mLegendPt->SetHeader(Form("%zu events PbPb min bias", mKineReader->getNEvents(0)), "C"); + mLegendPt->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); mLegendPt->AddEntry("Good_pt", "good (100% cluster purity)", "lep"); mLegendPt->AddEntry("Fake_pt", "fake", "lep"); mLegendPt->AddEntry("Clone_pt", "clone", "lep"); mLegendPt->Draw(); mCanvasPt->SaveAs("eff_pt.png"); + mCanvasPtSec = std::make_unique("cPtSec", "cPtSec", 1600, 1200); + mCanvasPtSec->cd(); + mCanvasPtSec->SetLogx(); + mCanvasPtSec->SetGrid(); + mEffPtSec->Draw("pz"); + mEffFakePtSec->Draw("pz same"); + mLegendPtSec = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPtSec->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPtSec->AddEntry("Good_ptSec", "good (100% cluster purity)", "lep"); + mLegendPtSec->AddEntry("Fake_tSec", "fake", "lep"); + mLegendPtSec->Draw(); + mCanvasPtSec->SaveAs("eff_ptSec.png"); + mCanvasEta = std::make_unique("cEta", "cEta", 1600, 1200); mCanvasEta->cd(); mCanvasEta->SetGrid(); @@ -428,16 +1031,321 @@ void TrackCheckStudy::endOfStream(EndOfStreamContext& ec) mEffFakeEta->Draw("pz same"); mEffClonesEta->Draw("pz same"); mLegendEta = std::make_unique(0.19, 0.8, 0.40, 0.96); - mLegendEta->SetHeader(Form("%zu events PbPb min bias", mKineReader->getNEvents(0)), "C"); + mLegendEta->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); mLegendEta->AddEntry("Good_eta", "good (100% cluster purity)", "lep"); mLegendEta->AddEntry("Fake_eta", "fake", "lep"); mLegendEta->AddEntry("Clone_eta", "clone", "lep"); mLegendEta->Draw(); mCanvasEta->SaveAs("eff_eta.png"); + mCanvasEtaSec = std::make_unique("cEtaSec", "cEtaSec", 1600, 1200); + mCanvasEtaSec->cd(); + mCanvasEtaSec->SetGrid(); + mEffEtaSec->Draw("pz"); + mEffFakeEtaSec->Draw("pz same"); + mLegendEtaSec = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendEtaSec->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendEtaSec->AddEntry("Good_etaSec", "good (100% cluster purity)", "lep"); + mLegendEtaSec->AddEntry("Fake_etaSec", "fake", "lep"); + mLegendEtaSec->Draw(); + mCanvasEtaSec->SaveAs("eff_EtaSec.png"); + + mCanvasRad = std::make_unique("cRad", "cRad", 1600, 1200); + mCanvasRad->cd(); + mCanvasRad->SetGrid(); + mEffGoodRad[3]->Draw("pz"); + mEffFakeRad[3]->Draw("pz same"); + mCanvasRad->SetLogy(); + mLegendRad = std::make_unique(0.8, 0.4, 0.95, 0.6); + mLegendRad->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + mLegendRad->AddEntry(Form("Good_Rad_%s", particleToanalize[3]), "good", "lep"); + mLegendRad->AddEntry(Form("Fake_Rad_%s", particleToanalize[3]), "fake", "lep"); + mLegendRad->Draw(); + mCanvasRad->SaveAs("eff_rad_sec.png"); + + mCanvasZ = std::make_unique("cZ", "cZ", 1600, 1200); + mCanvasZ->cd(); + mCanvasZ->SetGrid(); + mCanvasZ->SetLogy(); + mEffGoodZ[3]->Draw("pz"); + mEffFakeZ[3]->Draw("pz same"); + mCanvasZ->SetLogy(); + mLegendZ = std::make_unique(0.8, 0.4, 0.95, 0.6); + mLegendZ->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + mLegendZ->AddEntry(Form("Good_Z_%s", particleToanalize[3]), "good", "lep"); + mLegendZ->AddEntry(Form("Fake_Z_%s", particleToanalize[3]), "fake", "lep"); + mLegendZ->Draw(); + mCanvasZ->SaveAs("eff_Z_sec.png"); + ; + + mCanvasRadD = std::make_unique("cRadD", "cRadD", 1600, 1200); + mCanvasRadD->cd(); + mCanvasRadD->SetGrid(); + mCanvasRadD->SetLogy(); + mLegendRadD = std::make_unique(0.8, 0.64, 0.95, 0.8); + mLegendRadD->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + for (int i = 0; i < 3; i++) { + if (i == 0) { + mEffGoodRad[i]->Draw("pz"); + } else { + mEffGoodRad[i]->Draw("pz same"); + mEffFakeRad[i]->Draw("pz same"); + mLegendRadD->AddEntry(Form("Good_Rad%s", particleToanalize[i]), Form("%s_good", name[i]), "lep"); + mLegendRadD->AddEntry(Form("Fake_Rad%s", particleToanalize[i]), Form("%s_fake", name[i]), "lep"); + } + } + mLegendRadD->Draw(); + mCanvasRadD->SaveAs("eff_RadD_sec.png"); + + mCanvasZD = std::make_unique("cZD", "cZD", 1600, 1200); + mCanvasZD->cd(); + mCanvasZD->SetGrid(); + mCanvasZD->SetLogy(); + mLegendZD = std::make_unique(0.8, 0.64, 0.95, 0.8); + mLegendZD->SetHeader(Form("%zu events PP ", mKineReader->getNEvents(0)), "C"); + for (int i = 0; i < 3; i++) { + if (i == 0) { + mEffGoodZ[i]->Draw("pz"); + } else { + mEffGoodZ[i]->Draw("pz same"); + mEffFakeZ[i]->Draw("pz same"); + mLegendZD->AddEntry(Form("Good_Z%s", particleToanalize[i]), Form("%s_good", name[i]), "lep"); + mLegendZD->AddEntry(Form("Fake_Z%s", particleToanalize[i]), Form("%s_fake", name[i]), "lep"); + } + } + mLegendZD->Draw(); + mCanvasZD->SaveAs("eff_ZD_sec.png"); + + mPtResolution->SetName("#it{p}_{T} resolution"); + mPtResolution->SetTitle(";#Delta p_{T}/p_{T_{MC}} ;Entries"); + mPtResolution->SetFillColor(kAzure + 4); + mPtResolutionPrim->SetFillColor(kRed); + mPtResolutionSec->SetFillColor(kOrange); + mPtResolutionPrim->SetTitle(";#Delta p_{T}/p_{T_{MC}} ;Entries"); + mPtResolutionSec->SetTitle(";#Delta #it{p}_{T}/#it{p}_{T_{MC}} ;Entries"); + mPtResolution2D->SetTitle(";#it{p}_{T_{MC}} [GeV];#Delta #it{p}_{T}/#it{p}_{T_{MC}}"); + + fout.WriteTObject(mPtResolution.get()); + fout.WriteTObject(mPtResolutionPrim.get()); + fout.WriteTObject(mPtResolutionSec.get()); + fout.WriteTObject(mPtResolution2D.get()); + + mCanvasPtRes = std::make_unique("cPtr", "cPtr", 1600, 1200); + mCanvasPtRes->cd(); + mPtResolution->Draw("HIST"); + mLegendPtRes = std::make_unique(0.19, 0.8, 0.40, 0.96); + mLegendPtRes->SetHeader(Form("%zu events PP min bias", mKineReader->getNEvents(0)), "C"); + mLegendPtRes->AddEntry("mPtResolution", "All events", "lep"); + mLegendPtRes->Draw(); + mCanvasPtRes->SaveAs("ptRes.png"); + + mCanvasPtRes2 = std::make_unique("cPtr2", "cPtr2", 1600, 1200); + mCanvasPtRes2->cd(); + mPtResolution2D->Draw(); + mCanvasPtRes2->SaveAs("ptRes2.png"); + + mCanvasPtRes3 = std::make_unique("cPtr3", "cPtr3", 1600, 1200); + mCanvasPtRes3->cd(); + + auto* g1 = new TGraphErrors(100, meanPt, sigma, aa, sigmaerr); + g1->SetMarkerStyle(8); + g1->SetMarkerColor(kGreen); + g1->GetXaxis()->SetTitle(" #it{p}_{T} [GeV]"); + g1->GetYaxis()->SetTitle("#sigma #Delta #it{p}_{T}/#it{p}_{T_{MC}}"); + g1->GetYaxis()->SetLimits(0, 1); + g1->GetXaxis()->SetLimits(0, 10.); + g1->Draw("AP"); + g1->GetYaxis()->SetRangeUser(0, 1); + g1->GetXaxis()->SetRangeUser(0, 10.); + mCanvasPtRes3->SaveAs("ptRes3.png"); + + mCanvasPtRes4 = std::make_unique("cPt4", "cPt4", 1600, 1200); + mCanvasPtRes4->cd(); + mPtResolutionPrim->SetName("mPtResolutionPrim"); + mPtResolutionSec->SetName("mPtResolutionSec"); + mPtResolutionPrim->Draw("same hist"); + mPtResolutionSec->Draw("same hist"); + mLegendPtRes2 = std::make_unique(0.19, 0.8, 0.40, 0.96); + + mLegendPtRes2->SetHeader(Form("%zu events PP", mKineReader->getNEvents(0)), "C"); + mLegendPtRes2->AddEntry("mPtResolutionPrim", "Primary events", "f"); + mLegendPtRes2->AddEntry("mPtResolutionSec", "Secondary events", "f"); + mLegendPtRes2->Draw("same"); + mLegendPtRes2->SaveAs("ptRes4.png"); + + auto canvas = new TCanvas("fc_canvas", "Fake clusters", 1600, 1000); + canvas->Divide(4, 2); + for (int iH{0}; iH < 4; ++iH) { + canvas->cd(iH + 1); + stackLength[iH]->Draw(); + stackLength[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + for (int iH{0}; iH < 4; ++iH) { + canvas->cd(iH + 5); + stackLength1Fake[iH]->Draw(); + stackLength1Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + + canvas->SaveAs("fakeClusters2.png", "recreate"); + + auto canvas2 = new TCanvas("fc_canvas2", "Fake clusters", 1600, 1000); + canvas2->Divide(4, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvas2->cd(iH + 1); + stackLength2Fake[iH]->Draw(); + stackLength2Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + for (int iH{0}; iH < 4; ++iH) { + canvas2->cd(iH + 5); + stackLength3Fake[iH]->Draw(); + stackLength3Fake[iH]->GetXaxis()->SetTitle("Layer"); + gPad->BuildLegend(); + } + canvas2->SaveAs("fakeClusters3.png", "recreate"); + + auto canvasPtfake = new TCanvas("canvasPtfake", "Fake pt", 1600, 1000); + canvasPtfake->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasPtfake->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasPtfake->cd(iH + 1); + } + if (v == 0) { + mEffFakePts[v][iH]->Draw(); + } else { + mEffFakePts[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#it{p}_{T}, Fake Tracks, layer %d", iH)); + gPad->SetName(Form("#it{p}_{T}, Fake Tracks, layer %d", iH)); + } + canvasPtfake->SaveAs("PtforPartFake.png", "recreate"); + + auto canvasPtGood = new TCanvas("canvasPtGood", "Good pt", 1600, 1000); + canvasPtGood->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasPtGood->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasPtGood->cd(iH + 1); + } + if (v == 0) { + mEffGoodPts[v][iH]->Draw(); + } else { + mEffGoodPts[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#it{p}_{T}, Good Tracks, layer %d", iH)); + gPad->SetName(Form("#it{p}_{T}, Good Tracks, layer %d", iH)); + } + + auto canvasEtafake = new TCanvas("canvasEtafake", "Fake Eta", 1600, 1000); + canvasEtafake->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasEtafake->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasEtafake->cd(iH + 1); + } + if (v == 0) { + mEffFakeEtas[v][iH]->Draw(); + } else { + mEffFakeEtas[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#eta, Fake Tracks, layer %d", iH)); + gPad->SetName(Form("#eta, Fake Tracks, layer %d", iH)); + } + auto canvasEtaGood = new TCanvas("canvasEtaGood", "Good Eta", 1600, 1000); + canvasEtaGood->Divide(2, 2); + + for (int iH{0}; iH < 4; ++iH) { + canvasEtaGood->cd(iH + 1); + for (int v = 0; v < 4; v++) { + if (v == 0) { + canvasEtaGood->cd(iH + 1); + } + if (v == 0) { + mEffGoodEtas[v][iH]->Draw(); + } else { + mEffGoodEtas[v][iH]->Draw("same"); + } + } + gPad->BuildLegend(); + gPad->SetGrid(); + gPad->SetTitle(Form("#eta, Good Tracks, layer %d", iH)); + gPad->SetName(Form("#eta, Good Tracks, layer %d", iH)); + } + + auto canvasI = new TCanvas("canvasI", "canvasI", 1600, 1000); + canvasI->cd(); + mClusterFake[0]->Draw("COLZ"); + canvasI->SaveAs("Iper2D.png", "recreate"); + + auto canvasL = new TCanvas("canvasL", "canvasL", 1600, 1000); + canvasL->cd(); + mClusterFake[1]->Draw("COLZ"); + canvasL->SaveAs("Lam2D.png", "recreate"); + + auto canvasK = new TCanvas("canvasK", "canvasK", 1600, 1000); + canvasK->cd(); + mClusterFake[2]->Draw("COLZ"); + canvasK->SaveAs("K2D.png", "recreate"); + + auto canvasZProd = new TCanvas("canvasZProd", "canvasZProd", 1600, 1000); + canvasZProd->cd(); + processvsZ->Draw("COLZ"); + canvasZProd->SaveAs("prodvsZ.png", "recreate"); + auto canvasRadProd = new TCanvas("canvasRadProd", "canvasRadProd", 1600, 1000); + canvasRadProd->cd(); + processvsRad->Draw("COLZ"); + canvasRadProd->SaveAs("prodvsRad.png", "recreate"); + auto canvasRadProdO = new TCanvas("canvasRadProdO", "canvasRadProdO", 1600, 1000); + canvasRadProdO->cd(); + processvsRadOther->Draw("COLZ"); + canvasRadProdO->SaveAs("prodvsRadO.png", "recreate"); + auto canvasRadProNOTr = new TCanvas("canvasRadProNOTr", "canvasRadProNOTr", 1600, 1000); + canvasRadProNOTr->cd(); + processvsRadNotTracked->Draw("COLZ"); + canvasRadProNOTr->SaveAs("prodvsRadNoTr.png", "recreate"); + auto canvasEtaProNOTr = new TCanvas("canvasEtaProNOTr", "canvasEtaProNOTr", 1600, 1000); + canvasEtaProNOTr->cd(); + processvsEtaNotTracked->Draw("COLZ"); + canvasEtaProNOTr->SaveAs("prodvsEtaNoTr.png", "recreate"); + fout.cd(); mCanvasPt->Write(); mCanvasEta->Write(); + mCanvasPtSec->Write(); + mCanvasEtaSec->Write(); + mCanvasPtRes->Write(); + mCanvasPtRes2->Write(); + mCanvasPtRes3->Write(); + mCanvasPtRes4->Write(); + mCanvasRad->Write(); + mCanvasZ->Write(); + mCanvasRadD->Write(); + mCanvasZD->Write(); + canvas->Write(); + canvas2->Write(); + canvasPtfake->Write(); + canvasI->Write(); + canvasL->Write(); + canvasK->Write(); fout.Close(); } @@ -469,6 +1377,4 @@ DataProcessorSpec getTrackCheckStudy(mask_t srcTracksMask, mask_t srcClustersMas Options{}}; } -} // namespace study -} // namespace its -} // namespace o2 \ No newline at end of file +} // namespace o2::its::study diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx index 75eef5a561b91..d760d2aeb075d 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -65,8 +65,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); 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); + // o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); + // o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); std::shared_ptr mcKinematicsReader; if (useMC) { diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index 7989568db80fc..05c3818032aa0 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -662,12 +662,12 @@ void Detector::createMaterials() // ERG Duocel o2::base::Detector::Material(39, "ERGDUOCEL$", 12.0107, 6, 0.06, 999, 999); - o2::base::Detector::Medium(39, "ERGDUOCEL$", 33, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Medium(39, "ERGDUOCEL$", 39, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); // Impregnated carbon fleece // (as educated guess we assume 50% carbon fleece 50% Araldite glue) o2::base::Detector::Material(40, "IMPREG_FLEECE$", 12.0107, 6, 0.5 * (dAraldite + 0.4), 999, 999); - o2::base::Detector::Medium(40, "IMPREG_FLEECE$", 34, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); + o2::base::Detector::Medium(40, "IMPREG_FLEECE$", 40, 0, ifield, fieldm, tmaxfdSi, stemaxSi, deemaxSi, epsilSi, stminSi); } void Detector::EndOfEvent() { Reset(); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt index 6ab442f3d33b8..a389cb3dd4c11 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -12,7 +12,6 @@ # CUDA if(CUDA_ENABLED) find_package(CUDAToolkit) - message(STATUS "Building ITS CUDA tracker") o2_add_library(ITStrackingCUDA @@ -33,5 +32,4 @@ o2_add_library(ITStrackingCUDA set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) target_compile_definitions(${targetName} PRIVATE GPUCA_O2_LIB $) - endif() \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore b/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore index 5854f8830a79b..74255eaece87c 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/.gitignore @@ -1,2 +1,3 @@ # Don't track generated hip sources -*.hip.cxx \ No newline at end of file +*.hip.cxx +*.hip \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index cba23d5119e41..59911e57b3239 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -10,37 +10,17 @@ # or submit itself to any jurisdiction. if(HIP_ENABLED) - # Hipify-perl to generate HIP sources - set(HIPIFY_EXECUTABLE "/opt/rocm/bin/hipify-perl") - file(GLOB CUDA_SOURCES_FULL_PATH "../cuda/*.cu") - foreach(file ${CUDA_SOURCES_FULL_PATH}) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${file}) - get_filename_component(CUDA_SOURCE ${file} NAME) - string(REPLACE ".cu" "" CUDA_SOURCE_NAME ${CUDA_SOURCE}) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - COMMAND ${HIPIFY_EXECUTABLE} --quiet-warnings ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} | sed '1{/\#include \"hip\\/hip_runtime.h\"/d}' > ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} - ) - endforeach() - - install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${baseTargetName} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) - - set(CMAKE_CXX_COMPILER ${hip_HIPCC_EXECUTABLE}) - set(CMAKE_CXX_EXTENSIONS OFF) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-rdc") - message(STATUS "Building ITS HIP tracker") - o2_add_library(ITStrackingHIP - SOURCES ClusterLinesGPU.hip.cxx - Context.hip.cxx - TimeFrameGPU.hip.cxx - Stream.hip.cxx - TrackerTraitsGPU.hip.cxx - TracerGPU.hip.cxx - VertexerTraitsGPU.hip.cxx - Utils.hip.cxx + set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-rdc") + o2_add_hipified_library(ITStrackingHIP + SOURCES ../cuda/ClusterLinesGPU.cu + ../cuda/Context.cu + ../cuda/TimeFrameGPU.cu + ../cuda/Stream.cu + ../cuda/TrackerTraitsGPU.cu + ../cuda/TracerGPU.cu + ../cuda/VertexerTraitsGPU.cu + ../cuda/Utils.cu PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking hip::host @@ -48,11 +28,8 @@ if(HIP_ENABLED) hip::hipcub TARGETVARNAME targetName) - target_compile_definitions( - ${targetName} PRIVATE $) - if(O2_HIP_CMAKE_LINK_FLAGS) # Need to add gpu target also to link flags due to gpu-rdc option - target_link_options(${targetName} PUBLIC ${O2_HIP_CMAKE_LINK_FLAGS}) + target_link_options(${targetName} PRIVATE ${O2_HIP_CMAKE_LINK_FLAGS}) endif() endif() diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index da35b1714978a..eda156a910bed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -129,6 +129,7 @@ class TimeFrame const gsl::span getClusterLabels(int layerId, const Cluster& cl) const; const gsl::span getClusterLabels(int layerId, const int clId) const; int getClusterExternalIndex(int layerId, const int clId) const; + int getClusterSize(int clusterId); std::vector& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } std::vector& getCellsLabel(int layer) { return mCellLabels[layer]; } @@ -155,7 +156,8 @@ class TimeFrame std::vector>& getCellSeedsChi2() { return mCellSeedsChi2; } std::vector>& getCellsLookupTable(); - std::vector>>& getCellsNeighbours(); + std::vector>& getCellsNeighbours(); + std::vector>& getCellsNeighboursLUT(); std::vector>& getRoads(); std::vector& getTracks(int rof) { return mTracks[rof]; } std::vector& getTracksLabel(const int rof) { return mTracksLabel[rof]; } @@ -165,7 +167,8 @@ class TimeFrame int getNumberOfClusters() const; int getNumberOfCells() const; int getNumberOfTracklets() const; - int getNumberOfTracks() const; + size_t getNumberOfTracks() const; + size_t getNumberOfUsedClusters() const; bool checkMemory(unsigned long max) { return getArtefactsMemory() < max; } unsigned long getArtefactsMemory(); @@ -244,6 +247,7 @@ class TimeFrame std::vector mMSangles; std::vector mPhiCuts; std::vector mPositionResolution; + std::vector mClusterSize; std::vector mMultiplicityCutMask; std::vector> mPValphaX; /// PV x and alpha for track propagation std::vector> mUnsortedClusters; @@ -253,7 +257,8 @@ class TimeFrame std::vector> mCellSeeds; std::vector> mCellSeedsChi2; std::vector> mCellsLookupTable; - std::vector>> mCellsNeighbours; + std::vector> mCellsNeighbours; + std::vector> mCellsNeighboursLUT; std::vector> mRoads; std::vector> mTracksLabel; std::vector> mTracks; @@ -424,6 +429,11 @@ inline const gsl::span TimeFrame::getClusterLabels(int layerI return mClusterLabels->getLabels(mClusterExternalIndices[layerId][clId]); } +inline int TimeFrame::getClusterSize(int clusterId) +{ + return mClusterSize[clusterId]; +} + inline int TimeFrame::getClusterExternalIndex(int layerId, const int clId) const { return mClusterExternalIndices[layerId][clId]; @@ -542,10 +552,8 @@ inline std::vector>& TimeFrame::getCellsLookupTable() return mCellsLookupTable; } -inline std::vector>>& TimeFrame::getCellsNeighbours() -{ - return mCellsNeighbours; -} +inline std::vector>& TimeFrame::getCellsNeighbours() { return mCellsNeighbours; } +inline std::vector>& TimeFrame::getCellsNeighboursLUT() { return mCellsNeighboursLUT; } inline std::vector>& TimeFrame::getRoads() { return mRoads; } @@ -603,7 +611,7 @@ inline int TimeFrame::getNumberOfTracklets() const return nTracklets; } -inline int TimeFrame::getNumberOfTracks() const +inline size_t TimeFrame::getNumberOfTracks() const { int nTracks = 0; for (auto& t : mTracks) { @@ -612,6 +620,15 @@ inline int TimeFrame::getNumberOfTracks() const return nTracks; } +inline size_t TimeFrame::getNumberOfUsedClusters() const +{ + size_t nClusters = 0; + for (auto& layer : mUsedClusters) { + nClusters += std::count(layer.begin(), layer.end(), true); + } + return nClusters; +} + } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 24349e30b8017..bdf76ae9bdd23 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -175,6 +175,8 @@ int TimeFrame::loadROFrameData(gsl::span rofs, geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); mNrof = 0; + mClusterSize.clear(); + mClusterSize.reserve(clusters.size()); for (auto& rof : rofs) { for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { auto& c = clusters[clusterId]; @@ -184,18 +186,27 @@ int TimeFrame::loadROFrameData(gsl::span rofs, auto pattID = c.getPatternID(); o2::math_utils::Point3D locXYZ; float sigmaY2 = DefClusError2Row, sigmaZ2 = DefClusError2Col, sigmaYZ = 0; // Dummy COG errors (about half pixel size) + unsigned int clusterSize{0}; if (pattID != itsmft::CompCluster::InvalidPatternID) { sigmaY2 = dict->getErr2X(pattID); sigmaZ2 = dict->getErr2Z(pattID); if (!dict->isGroup(pattID)) { locXYZ = dict->getClusterCoordinates(c); + clusterSize = dict->getNpixels(pattID); } else { o2::itsmft::ClusterPattern patt(pattIt); locXYZ = dict->getClusterCoordinates(c, patt); + clusterSize = patt.getNPixels(); } } else { o2::itsmft::ClusterPattern patt(pattIt); locXYZ = dict->getClusterCoordinates(c, patt, false); + clusterSize = patt.getNPixels(); + } + if (clusterSize < 255) { + mClusterSize.push_back(clusterSize); + } else { + mClusterSize.push_back(255); } auto sensorID = c.getSensorID(); // Inverse transformation to the local --> tracking @@ -255,6 +266,7 @@ void TimeFrame::initialise(const int iteration, const TrackingParameters& trkPar mCellSeedsChi2.resize(trkParam.CellsPerRoad()); mCellsLookupTable.resize(trkParam.CellsPerRoad() - 1); mCellsNeighbours.resize(trkParam.CellsPerRoad() - 1); + mCellsNeighboursLUT.resize(trkParam.CellsPerRoad() - 1); mCellLabels.resize(trkParam.CellsPerRoad()); mTracklets.resize(std::min(trkParam.TrackletsPerRoad(), maxLayers - 1)); mTrackletLabels.resize(trkParam.TrackletsPerRoad()); @@ -395,6 +407,7 @@ void TimeFrame::initialise(const int iteration, const TrackingParameters& trkPar if (iLayer < (int)mCells.size() - 1) { mCellsLookupTable[iLayer].clear(); mCellsNeighbours[iLayer].clear(); + mCellsNeighboursLUT[iLayer].clear(); } } } @@ -409,9 +422,7 @@ unsigned long TimeFrame::getArtefactsMemory() size += sizeof(Cell) * cells.size(); } for (auto& cellsN : mCellsNeighbours) { - for (auto& vec : cellsN) { - size += sizeof(int) * vec.size(); - } + size += sizeof(int) * cellsN.size(); } return size + sizeof(Road<5>) * mRoads.size(); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 2a657866b8f76..60ae4163bfaaf 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -54,24 +54,24 @@ void Tracker::clustersToTracks(std::function logger, std::f total += evaluateTask(&Tracker::computeTracklets, "Tracklet finding", logger, iteration); logger(fmt::format("\t- Number of tracklets: {}", mTraits->getTFNumberOfTracklets())); if (!mTimeFrame->checkMemory(mTrkParams[iteration].MaxMemory)) { - error("Too much memory used during trackleting, check the detector status and/or the selections."); + error(fmt::format("Too much memory used during trackleting in iteration {}, check the detector status and/or the selections.", iteration)); break; } float trackletsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfTracklets()) / mTraits->getTFNumberOfClusters() : 0.f; if (trackletsPerCluster > mTrkParams[iteration].TrackletsPerClusterLimit) { - error(fmt::format("Too many tracklets per cluster ({}), check the detector status and/or the selections.", trackletsPerCluster)); + error(fmt::format("Too many tracklets per cluster ({}) in iteration {}, check the detector status and/or the selections.", trackletsPerCluster, iteration)); break; } total += evaluateTask(&Tracker::computeCells, "Cell finding", logger, iteration); logger(fmt::format("\t- Number of Cells: {}", mTraits->getTFNumberOfCells())); if (!mTimeFrame->checkMemory(mTrkParams[iteration].MaxMemory)) { - error("Too much memory used during cell finding, check the detector status and/or the selections."); + error(fmt::format("Too much memory used during cell finding in iteration {}, check the detector status and/or the selections.", iteration)); break; } float cellsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfCells()) / mTraits->getTFNumberOfClusters() : 0.f; if (cellsPerCluster > mTrkParams[iteration].CellsPerClusterLimit) { - error(fmt::format("Too many cells per cluster ({}), check the detector status and/or the selections.", cellsPerCluster)); + error(fmt::format("Too many cells per cluster ({}) in iteration {}, check the detector status and/or the selections.", cellsPerCluster, iteration)); break; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index f6f34cae67422..8149a9a7ce38c 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -15,6 +15,7 @@ #include "ITStracking/TrackerTraits.h" +#include #include #include @@ -394,8 +395,13 @@ void TrackerTraits::findCellsNeighbours(const int iteration) int layerCellsNum{static_cast(mTimeFrame->getCells()[iLayer].size())}; const int nextLayerCellsNum{static_cast(mTimeFrame->getCells()[iLayer + 1].size())}; - mTimeFrame->getCellsNeighbours()[iLayer].resize(nextLayerCellsNum); + mTimeFrame->getCellsNeighboursLUT()[iLayer].clear(); + mTimeFrame->getCellsNeighboursLUT()[iLayer].resize(nextLayerCellsNum, 0); + std::vector> cellsNeighbours; + cellsNeighbours.reserve(nextLayerCellsNum); + + std::vector> easyWay(nextLayerCellsNum); for (int iCell{0}; iCell < layerCellsNum; ++iCell) { const Cell& currentCell{mTimeFrame->getCells()[iLayer][iCell]}; @@ -426,8 +432,9 @@ void TrackerTraits::findCellsNeighbours(const int iteration) continue; } - mTimeFrame->getCellsNeighbours()[iLayer][iNextCell].push_back(iCell); - + mTimeFrame->getCellsNeighboursLUT()[iLayer][iNextCell]++; + cellsNeighbours.push_back(std::make_pair(iCell, iNextCell)); + easyWay[iNextCell].push_back(iCell); const int currentCellLevel{currentCell.getLevel()}; if (currentCellLevel >= nextCell.getLevel()) { @@ -435,6 +442,15 @@ void TrackerTraits::findCellsNeighbours(const int iteration) } } } + std::sort(cellsNeighbours.begin(), cellsNeighbours.end(), [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + mTimeFrame->getCellsNeighbours()[iLayer].clear(); + mTimeFrame->getCellsNeighbours()[iLayer].reserve(cellsNeighbours.size()); + for (auto& cellNeighboursIndex : cellsNeighbours) { + mTimeFrame->getCellsNeighbours()[iLayer].push_back(cellNeighboursIndex.first); + } + std::inclusive_scan(mTimeFrame->getCellsNeighboursLUT()[iLayer].begin(), mTimeFrame->getCellsNeighboursLUT()[iLayer].end(), mTimeFrame->getCellsNeighboursLUT()[iLayer].begin()); } } @@ -457,11 +473,11 @@ void TrackerTraits::findRoads(const int iteration) if (iLevel == 1) { continue; } - const int cellNeighboursNum{static_cast( - mTimeFrame->getCellsNeighbours()[iLayer - 1][iCell].size())}; + const int startNeighbourId{iCell ? mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][iCell - 1] : 0}; + const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][iCell]}; bool isFirstValidNeighbour = true; - for (int iNeighbourCell{0}; iNeighbourCell < cellNeighboursNum; ++iNeighbourCell) { - const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iCell][iNeighbourCell]; + for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { + const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iNeighbourCell]; const Cell& neighbourCell = mTimeFrame->getCells()[iLayer - 1][neighbourCellId]; if (iLevel - 1 != neighbourCell.getLevel()) { continue; @@ -486,11 +502,9 @@ void TrackerTraits::findRoads(const int iteration) void TrackerTraits::findTracks() { - std::vector> tracks(mNThreads); - for (auto& tracksV : tracks) { - tracksV.reserve(mTimeFrame->getRoads().size() / mNThreads); - } + std::vector tracks(mTimeFrame->getRoads().size()); + std::atomic trackIndex{0}; #pragma omp parallel for num_threads(mNThreads) for (size_t ri = 0; ri < mTimeFrame->getRoads().size(); ++ri) { auto& road = mTimeFrame->getRoads()[ri]; @@ -555,12 +569,6 @@ void TrackerTraits::findTracks() } } - /// Track seed preparation. Clusters are numbered progressively from the innermost going outward. - const auto& cluster1_glo = mTimeFrame->getUnsortedClusters()[lastCellLevel].at(clusters[lastCellLevel]); - const auto& cluster2_glo = mTimeFrame->getUnsortedClusters()[lastCellLevel + 1].at(clusters[lastCellLevel + 1]); - const auto& cluster3_glo = mTimeFrame->getUnsortedClusters()[lastCellLevel + 2].at(clusters[lastCellLevel + 2]); - const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(lastCellLevel + 2).at(clusters[lastCellLevel + 2]); - TrackITSExt temporaryTrack{mTimeFrame->getCellSeeds()[lastCellLevel][lastCellIndex]}; temporaryTrack.setChi2(mTimeFrame->getCellSeedsChi2()[lastCellLevel][lastCellIndex]); for (size_t iC = 0; iC < clusters.size(); ++iC) { @@ -586,25 +594,18 @@ void TrackerTraits::findTracks() continue; } // temporaryTrack.setROFrame(rof); -#ifdef WITH_OPENMP - int iThread = omp_get_thread_num(); -#else - int iThread = 0; -#endif - tracks[iThread].emplace_back(temporaryTrack); + tracks[trackIndex++] = temporaryTrack; } - for (int iV{1}; iV < mNThreads; ++iV) { - tracks[0].insert(tracks[0].end(), tracks[iV].begin(), tracks[iV].end()); - } + tracks.resize(trackIndex); if (mApplySmoothing) { // Smoothing tracks } - std::sort(tracks[0].begin(), tracks[0].end(), + std::sort(tracks.begin(), tracks.end(), [](TrackITSExt& track1, TrackITSExt& track2) { return track1.isBetter(track2, 1.e6f); }); - for (auto& track : tracks[0]) { + for (auto& track : tracks) { int nShared = 0; for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::its::UnusedIndex) { @@ -988,14 +989,11 @@ void TrackerTraits::traverseCellsTree(const int currentCellId, const int current mTimeFrame->getRoads().back().addCell(currentLayerId, currentCellId); if (currentLayerId > 0 && currentCellLevel > 1) { - const int cellNeighboursNum{static_cast( - mTimeFrame->getCellsNeighbours()[currentLayerId - 1][currentCellId].size())}; bool isFirstValidNeighbour = true; - - for (int iNeighbourCell{0}; iNeighbourCell < cellNeighboursNum; ++iNeighbourCell) { - - const int neighbourCellId = - mTimeFrame->getCellsNeighbours()[currentLayerId - 1][currentCellId][iNeighbourCell]; + const int startNeighbourId = currentCellId ? mTimeFrame->getCellsNeighboursLUT()[currentLayerId - 1][currentCellId - 1] : 0; + const int endNeighbourId = mTimeFrame->getCellsNeighboursLUT()[currentLayerId - 1][currentCellId]; + for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { + const int neighbourCellId = mTimeFrame->getCellsNeighbours()[currentLayerId - 1][iNeighbourCell]; const Cell& neighbourCell = mTimeFrame->getCells()[currentLayerId - 1][neighbourCellId]; if (currentCellLevel - 1 != neighbourCell.getLevel()) { diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h index cd9be3afc4c95..bcc19ff15b85d 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h @@ -85,6 +85,10 @@ class ITSDCSAdaposParser : public Task std::string mCcdbFetchUrl = "http://ccdb-test.cern.ch:8080"; o2::ccdb::BasicCCDBManager& mMgr = o2::ccdb::BasicCCDBManager::instance(); long int startTime; + + // to avoid several pushes to CCDB + long int pushTime = 0; + long int lastPushTime = 0; }; // Create a processor spec diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h index 0f452dcd7e0d8..29cb5a660dab1 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSParserSpec.h @@ -142,6 +142,9 @@ class ITSDCSParser : public Task // ITS Map o2::itsmft::ChipMappingITS mp; + + // CCDB url for RCT headers + std::string mCcdbUrlRct = "http://o2-ccdb.internal"; }; // Create a processor spec diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h index 46703c8ae0e91..07761f20fd872 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ThresholdCalibratorSpec.h @@ -63,6 +63,7 @@ namespace its { int nInj = 50; +int nInjScaled = 50; // different from nInj only if mMeb > 0, in this case it is nInj/3. // List of the possible run types for reference enum RunTypes { @@ -72,6 +73,7 @@ enum RunTypes { THR_SCAN_SHORT_2_10HZ = 18, THR_SCAN_SHORT_100HZ = 19, THR_SCAN_SHORT_200HZ = 20, + THR_SCAN_SHORT_150INJ = 55, VCASN150 = 23, VCASN100 = 10, VCASN100_100HZ = 21, @@ -164,7 +166,8 @@ class ITSThresholdCalibrator : public Task short int vRow[N_COL]; short int vThreshold[N_COL]; bool vSuccess[N_COL]; - unsigned char vNoise[N_COL]; + float vNoise[N_COL]; + unsigned char vPoints[N_COL]; short int vMixData[N_COL]; unsigned char vCharge[N_COL]; float vSlope[N_COL]; @@ -187,9 +190,9 @@ class ITSThresholdCalibrator : public Task // Helper functions related to threshold extraction void initThresholdTree(bool recreate = true); bool findUpperLower(std::vector>, const short int&, short int&, short int&, bool, int); - bool findThreshold(const short int&, std::vector>, const float*, short int&, float&, float&, int); - bool findThresholdFit(const short int&, std::vector>, const float*, const short int&, float&, float&, int); - bool findThresholdDerivative(std::vector>, const float*, const short int&, float&, float&, int); + bool findThreshold(const short int&, std::vector>, const float*, short int&, float&, float&, int&, int); + bool findThresholdFit(const short int&, std::vector>, const float*, const short int&, float&, float&, int&, int); + bool findThresholdDerivative(std::vector>, const float*, const short int&, float&, float&, int&, int); bool findThresholdHitcounting(std::vector>, const float*, const short int&, float&, int); bool isScanFinished(const short int&, const short int&, const short int&); void findAverage(const std::array&, float&, float&, float&, float&); @@ -287,6 +290,7 @@ class ITSThresholdCalibrator : public Task // parameters for manual mode: if run type is not among the listed one bool isManualMode = false; bool saveTree; + bool scaleNinj = false; short int manualMin, manualMin2 = 0; short int manualMax, manualMax2 = 0; short int manualStep = 1, manualStep2 = 1; @@ -305,6 +309,7 @@ class ITSThresholdCalibrator : public Task int maxDumpS = -1; // maximum number of s-curves to be dumped, default -1 = dump all std::string chipDumpS = ""; // list of comma-separated O2 chipIDs to be dumped, default is empty = dump all int dumpCounterS[24120] = {0}; // count dumps for every chip + int countCdw[24120] = {0}; // count how many CDWs have been processed with the maximum charge injected: usefull for s-curve dump when hits do not arrive in order TFile* fileDumpS; // file where to store the s-curves on disk std::vector chipDumpList; // vector of chips to dump @@ -316,6 +321,9 @@ class ITSThresholdCalibrator : public Task bool mCalculate2DParams = true; int chargeA = 0; int chargeB = 0; + + // Variable to select from which MEB to consider the hits. + int mMeb = -1; }; // Create a processor spec diff --git a/Detectors/ITSMFT/ITS/workflow/src/DCSAdaposParserSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/DCSAdaposParserSpec.cxx index 3d14778053529..05bf3cf9b99bb 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/DCSAdaposParserSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/DCSAdaposParserSpec.cxx @@ -111,7 +111,12 @@ void ITSDCSAdaposParser::process(const gsl::span dps) } if (mapel->second.payload_pt1 + 8 != mCcdbAlpideParam->roFrameLengthInBC) { mStrobeToUpload = mapel->second.payload_pt1; - doStrobeUpload = true; + pushTime = o2::ccdb::getCurrentTimestamp(); + if (pushTime - lastPushTime > 30000) { // push to CCDB only if at least 30s passed from the last push + doStrobeUpload = true; + } else { + doStrobeUpload = false; + } } else { doStrobeUpload = false; } @@ -196,6 +201,9 @@ void ITSDCSAdaposParser::pushToCCDB(ProcessingContext& pc) info.getMetaData(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()); } + // uptade lastPushTime + lastPushTime = o2::ccdb::getCurrentTimestamp(); + return; } diff --git a/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx index 53618ae55851b..8cd631d0a719d 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/DCSParserSpec.cxx @@ -36,6 +36,8 @@ void ITSDCSParser::init(InitContext& ic) this->mCcdbUrl = ic.options().get("ccdb-url"); + this->mCcdbUrlRct = ic.options().get("ccdb-url-rct"); + this->mVerboseOutput = ic.options().get("verbose"); return; @@ -469,7 +471,7 @@ void ITSDCSParser::pushToCCDB(ProcessingContext& pc) long tstart = 0, tend = 0; // retireve run start/stop times from CCDB o2::ccdb::CcdbApi api; - api.init("http://alice-ccdb.cern.ch"); + api.init(mCcdbUrlRct); // Initialize empty metadata object for search std::map metadata; std::map headers = api.retrieveHeaders( @@ -582,7 +584,9 @@ void ITSDCSParser::appendDeadChipObj() unsigned short int globchipID = getGlobalChipID(hicPos, hS, chipInMod); this->mDeadMap.maskFullChip(globchipID); - LOG(info) << "Masking dead chip " << globchipID; + if (mVerboseOutput) { + LOG(info) << "Masking dead chip " << globchipID; + } } } @@ -660,7 +664,8 @@ DataProcessorSpec getITSDCSParserSpec() AlgorithmSpec{adaptFromTask()}, Options{ {"verbose", VariantType::Bool, false, {"Use verbose output mode"}}, - {"ccdb-url", VariantType::String, "", {"CCDB url, default is empty (i.e. send output to CCDB populator workflow)"}}}}; + {"ccdb-url", VariantType::String, "", {"CCDB url, default is empty (i.e. send output to CCDB populator workflow)"}}, + {"ccdb-url-rct", VariantType::String, "", {"CCDB url from where to get RCT object for headers, default is o2-ccdb.internal. Use http://alice-ccdb.cern.ch for local tests"}}}}; } } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx index cac93a30e6e86..391ef5c38677c 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx @@ -28,13 +28,13 @@ namespace its // Define error function for ROOT fitting double erf(double* xx, double* par) { - return (nInj / 2) * TMath::Erf((xx[0] - par[0]) / (sqrt(2) * par[1])) + (nInj / 2); + return (nInjScaled / 2) * TMath::Erf((xx[0] - par[0]) / (sqrt(2) * par[1])) + (nInjScaled / 2); } // ITHR erf is reversed double erf_ithr(double* xx, double* par) { - return (nInj / 2) * (1 - TMath::Erf((xx[0] - par[0]) / (sqrt(2) * par[1]))); + return (nInjScaled / 2) * (1 - TMath::Erf((xx[0] - par[0]) / (sqrt(2) * par[1]))); } ////////////////////////////////////////////////////////////////////////////// @@ -183,6 +183,9 @@ void ITSThresholdCalibrator::init(InitContext& ic) // this is not mandatory since it's 5 by default manualStrobeWindow = ic.options().get("manual-strobewindow"); + + // Flag to scale the number of injections by 3 in case --meb-select is used + scaleNinj = ic.options().get("scale-ninj"); } // Flag to enable the analysis of CRU_ITS data @@ -190,6 +193,7 @@ void ITSThresholdCalibrator::init(InitContext& ic) // Number of injections nInj = ic.options().get("ninj"); + nInjScaled = nInj; // Flag to enable the call of the finalize() method at end of stream isFinalizeEos = ic.options().get("finalize-at-eos"); @@ -242,6 +246,12 @@ void ITSThresholdCalibrator::init(InitContext& ic) } } + // Variable to select from which multi-event buffer select the hits + mMeb = ic.options().get("meb-select"); + if (mMeb > 2) { + LOG(error) << "MEB cannot be greater than 2. Please check your command line."; + } + return; } @@ -357,7 +367,8 @@ void ITSThresholdCalibrator::initThresholdTree(bool recreate /*=true*/) this->mThresholdTree->Branch("row", &vRow, "vRow[1024]/S"); if (this->mScanType == 'T') { this->mThresholdTree->Branch("thr", &vThreshold, "vThreshold[1024]/S"); - this->mThresholdTree->Branch("noise", &vNoise, "vNoise[1024]/b"); + this->mThresholdTree->Branch("noise", &vNoise, "vNoise[1024]/F"); + this->mThresholdTree->Branch("spoints", &vPoints, "vPoints[1024]/b"); this->mThresholdTree->Branch("success", &vSuccess, "vSuccess[1024]/O"); } else if (mScanType == 'D' || mScanType == 'A') { // this->mScanType == 'D' and this->mScanType == 'A' this->mThresholdTree->Branch("n_hits", &vThreshold, "vThreshold[1024]/S"); @@ -380,6 +391,7 @@ void ITSThresholdCalibrator::initThresholdTree(bool recreate /*=true*/) this->mThresholdTree->Branch("vresetd", &vMixData, "vMixData[1024]/S"); } else if (mScanType == 'r') { this->mThresholdTree->Branch("thr", &vThreshold, "vThreshold[1024]/S"); + this->mThresholdTree->Branch("noise", &vNoise, "vNoise[1024]/F"); this->mThresholdTree->Branch("success", &vSuccess, "vSuccess[1024]/O"); this->mThresholdTree->Branch("vresetd", &vMixData, "vMixData[1024]/S"); } @@ -415,7 +427,7 @@ bool ITSThresholdCalibrator::findUpperLower( } for (int i = upper; i > 0; i--) { int comp = mScanType != 'r' ? data[iloop2][i] : data[i][iloop2]; - if (comp >= nInj) { + if (comp >= nInjScaled) { lower = i; break; } @@ -425,7 +437,7 @@ bool ITSThresholdCalibrator::findUpperLower( for (int i = 0; i < NPoints; i++) { int comp = mScanType != 'r' ? data[iloop2][i] : data[i][iloop2]; - if (comp >= nInj) { + if (comp >= nInjScaled) { upper = i; break; } @@ -454,17 +466,17 @@ bool ITSThresholdCalibrator::findUpperLower( // Main findThreshold function which calls one of the three methods bool ITSThresholdCalibrator::findThreshold( const short int& chipID, std::vector> data, const float* x, short int& NPoints, - float& thresh, float& noise, int iloop2) + float& thresh, float& noise, int& spoints, int iloop2) { bool success = false; switch (this->mFitType) { case DERIVATIVE: // Derivative method - success = this->findThresholdDerivative(data, x, NPoints, thresh, noise, iloop2); + success = this->findThresholdDerivative(data, x, NPoints, thresh, noise, spoints, iloop2); break; case FIT: // Fit method - success = this->findThresholdFit(chipID, data, x, NPoints, thresh, noise, iloop2); + success = this->findThresholdFit(chipID, data, x, NPoints, thresh, noise, spoints, iloop2); break; case HITCOUNTING: // Hit-counting method @@ -482,10 +494,11 @@ bool ITSThresholdCalibrator::findThreshold( // x is the array of charge injected values; // NPoints is the length of both arrays. // thresh, noise, chi2 pointers are updated with results from the fit +// spoints: number of points in the S of the S-curve (with n_hits between 0 and 50, excluding first and last point) // iloop2 is 0 for thr scan but is equal to vresetd index in 2D vresetd scan bool ITSThresholdCalibrator::findThresholdFit( const short int& chipID, std::vector> data, const float* x, const short int& NPoints, - float& thresh, float& noise, int iloop2) + float& thresh, float& noise, int& spoints, int iloop2) { // Find lower & upper values of the S-curve region short int lower, upper; @@ -540,6 +553,7 @@ bool ITSThresholdCalibrator::findThresholdFit( noise = this->mFitFunction->GetParameter(1); thresh = this->mFitFunction->GetParameter(0); float chi2 = this->mFitFunction->GetChisquare() / this->mFitFunction->GetNDF(); + spoints = upper - lower - 1; // Clean up histogram for next time it is used this->mFitHist->Reset(); @@ -552,9 +566,10 @@ bool ITSThresholdCalibrator::findThresholdFit( // data is the number of trigger counts per charge injected; // x is the array of charge injected values; // NPoints is the length of both arrays. +// spoints: number of points in the S of the S-curve (with n_hits between 0 and 50, excluding first and last point) // iloop2 is 0 for thr scan but is equal to vresetd index in 2D vresetd scan bool ITSThresholdCalibrator::findThresholdDerivative(std::vector> data, const float* x, const short int& NPoints, - float& thresh, float& noise, int iloop2) + float& thresh, float& noise, int& spoints, int iloop2) { // Find lower & upper values of the S-curve region short int lower, upper; @@ -587,6 +602,7 @@ bool ITSThresholdCalibrator::findThresholdDerivative(std::vector 0.; } @@ -605,7 +621,7 @@ bool ITSThresholdCalibrator::findThresholdHitcounting( for (unsigned short int i = 0; i < NPoints; i++) { numberOfHits += (mScanType != 'r') ? data[iloop2][i] : data[i][iloop2]; int comp = (mScanType != 'r') ? data[iloop2][i] : data[i][iloop2]; - if (!is50 && comp == nInj) { + if (!is50 && comp == nInjScaled) { is50 = true; } } @@ -619,11 +635,11 @@ bool ITSThresholdCalibrator::findThresholdHitcounting( } if (this->mScanType == 'T') { - thresh = this->mX[N_RANGE - 1] - numberOfHits / float(nInj); + thresh = this->mX[N_RANGE - 1] - numberOfHits / float(nInjScaled); } else if (this->mScanType == 'V') { - thresh = (this->mX[N_RANGE - 1] * nInj - numberOfHits) / float(nInj); + thresh = (this->mX[N_RANGE - 1] * nInjScaled - numberOfHits) / float(nInjScaled); } else if (this->mScanType == 'I') { - thresh = (numberOfHits + nInj * this->mX[0]) / float(nInj); + thresh = (numberOfHits + nInjScaled * this->mX[0]) / float(nInjScaled); } else { LOG(error) << "Unexpected runtype encountered in findThresholdHitcounting()"; return false; @@ -731,18 +747,21 @@ void ITSThresholdCalibrator::extractThresholdRow(const short int& chipID, const // Do the threshold fit float thresh = 0., noise = 0.; bool success = false; + int spoints = 0; if (isDumpS) { // already protected for multi-thread in the init mFitHist->SetName(Form("scurve_chip%d_row%d_col%d_scani%d", chipID, row, col_i, scan_i)); } success = this->findThreshold(chipID, mPixelHits[chipID][row][col_i], - this->mX, mScanType == 'r' ? N_RANGE2 : N_RANGE, thresh, noise, scan_i); + this->mX, mScanType == 'r' ? N_RANGE2 : N_RANGE, thresh, noise, spoints, scan_i); vChipid[col_i] = chipID; vRow[col_i] = row; vThreshold[col_i] = (mScanType == 'T' || mScanType == 'r') ? (short int)(thresh * 10.) : (short int)(thresh); - vNoise[col_i] = (unsigned char)(noise * 10.); // always factor 10 also for ITHR/VCASN to not have all zeros + vNoise[col_i] = (float)(noise * 10.); // always factor 10 also for ITHR/VCASN to not have all zeros vSuccess[col_i] = success; + vPoints[col_i] = spoints > 0 ? (unsigned char)(spoints) : 0; + if (mScanType == 'r') { vMixData[col_i] = (scan_i * this->mStep) + mMin; } @@ -895,7 +914,7 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) this->mCheckExactRow = true; } else if (runtype == THR_SCAN_SHORT || runtype == THR_SCAN_SHORT_100HZ || - runtype == THR_SCAN_SHORT_200HZ || runtype == THR_SCAN_SHORT_33 || runtype == THR_SCAN_SHORT_2_10HZ) { + runtype == THR_SCAN_SHORT_200HZ || runtype == THR_SCAN_SHORT_33 || runtype == THR_SCAN_SHORT_2_10HZ || runtype == THR_SCAN_SHORT_150INJ) { // threshold_scan_short -- just extract thresholds for each pixel and write to TTree // 10 rows per chip this->mScanType = 'T'; @@ -904,7 +923,12 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) this->mMax = 50; this->N_RANGE = 51; this->mCheckExactRow = true; - + if (runtype == THR_SCAN_SHORT_150INJ) { + nInj = 150; + if (mMeb >= 0) { + nInjScaled = nInj / 3; + } + } } else if (runtype == VCASN150 || runtype == VCASN100 || runtype == VCASN100_100HZ || runtype == VCASN130 || runtype == VCASNBB) { // VCASN tuning for different target thresholds // Store average VCASN for each chip into CCDB @@ -998,7 +1022,7 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) this->initThresholdTree(); } else { // No other run type recognized by this workflow - LOG(warning) << "Runtype " << runtype << " not recognized by calibration workflow."; + LOG(error) << "Runtype " << runtype << " not recognized by calibration workflow (ignore if you are in manual mode)"; if (isManualMode) { LOG(info) << "Entering manual mode: be sure to have set all parameters correctly"; this->mScanType = manualScanType[0]; @@ -1016,6 +1040,9 @@ void ITSThresholdCalibrator::setRunType(const short int& runtype) } this->mFitType = (mScanType == 'D' || mScanType == 'A' || mScanType == 'P' || mScanType == 'p') ? NO_FIT : mFitType; this->mCheckExactRow = (mScanType == 'D' || mScanType == 'A') ? false : true; + if (scaleNinj) { + nInjScaled = nInj / 3; + } } else { throw runtype; } @@ -1053,7 +1080,7 @@ bool ITSThresholdCalibrator::isScanFinished(const short int& chipID, const short short int chg = (mScanType == 'I' || mScanType == 'D' || mScanType == 'A') ? 0 : (N_RANGE - 1); // check 2 pixels in case one of them is dead - return ((this->mPixelHits[chipID][row][col][0][chg] >= nInj || this->mPixelHits[chipID][row][col + 100][0][chg] >= nInj) && (!mCheckCw || cwcnt == nInj - 1)); + return ((this->mPixelHits[chipID][row][col][0][chg] >= nInjScaled || this->mPixelHits[chipID][row][col + 100][0][chg] >= nInjScaled) && (!mCheckCw || cwcnt == nInj - 1)); } ////////////////////////////////////////////////////////////////////////////// @@ -1420,7 +1447,7 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) continue; } - if (!mChipsForbRows[chipID] && (!mCheckExactRow || d.getRow() == row)) { // row has NOT to be forbidden and we ignore hits coming from other rows (potential masking issue on chip) + if (!mChipsForbRows[chipID] && (!mCheckExactRow || d.getRow() == row) && (mMeb < 0 || cwcnt % 3 == mMeb)) { // row has NOT to be forbidden and we ignore hits coming from other rows (potential masking issue on chip) // Increment the number of counts for this pixel this->mPixelHits[chipID][d.getRow()][col][chgPoint][loopPoint]++; } @@ -1441,7 +1468,10 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) if (isDumpS) { auto fndVal = std::find(chipDumpList.begin(), chipDumpList.end(), chipID); int checkR = (mScanType == 'I') ? mMin : mMax; - passCondition = (cwcnt == nInj - 1) && (loopval == checkR) && (fndVal != chipDumpList.end() || !chipDumpList.size()); // in this way we dump any s-curve, bad and good + if (loopval == checkR) { + countCdw[chipID]++; + } + passCondition = (countCdw[chipID] == nInj) && (loopval == checkR) && (fndVal != chipDumpList.end() || !chipDumpList.size()); // in this way we dump any s-curve, bad and good if (mVerboseOutput) { LOG(info) << "Loopval: " << loopval << " counter: " << cwcnt << " checkR: " << checkR << " chipID: " << chipID << " pass: " << passCondition; } @@ -1458,6 +1488,7 @@ void ITSThresholdCalibrator::run(ProcessingContext& pc) if (mScanType != 'D' && mScanType != 'A' && mScanType != 'P' && mScanType != 'p' && mScanType != 'R' && mScanType != 'r' && passCondition) { // for D,A,P we do it at the end in finalize() this->extractAndUpdate(chipID, row); + countCdw[chipID] = 0; // remove entry for this row whose scan is completed mPixelHits[chipID].erase(row); mForbiddenRows[chipID].push_back(row); // due to the loose cut in isScanFinished, extra hits may come for this deleted row. In this way the row is ignored afterwards @@ -1576,7 +1607,7 @@ void ITSThresholdCalibrator::addDatabaseEntry( this->mp.expandChipInfoHW(chipID, lay, sta, ssta, mod, chipInMod); char stave[6]; - sprintf(stave, "L%d_%02d", lay, sta); + snprintf(stave, 6, "L%d_%02d", lay, sta); if (isQC) { o2::dcs::addConfigItem(this->mChipDoneQc, "O2ChipID", std::to_string(chipID)); @@ -1973,6 +2004,7 @@ DataProcessorSpec getITSThresholdCalibratorSpec(const ITSCalibInpConf& inpConf) {"manual-scantype", VariantType::String, "T", {"scan type, can be D, T, I, V, P, p: use only in manual mode"}}, {"manual-strobewindow", VariantType::Int, 5, {"strobe duration in clock cycles, default is 5 = 125 ns: use only in manual mode"}}, {"save-tree", VariantType::Bool, false, {"Flag to save ROOT tree on disk: use only in manual mode"}}, + {"scale-ninj", VariantType::Bool, false, {"Flag to activate the scale of the number of injects to be used to count hits from specific MEBs: use only in manual mode and in combination with --meb-select"}}, {"enable-mpv", VariantType::Bool, false, {"Flag to enable calculation of most-probable value in vcasn/ithr scans"}}, {"enable-cru-its", VariantType::Bool, false, {"Flag to enable the analysis of raw data on disk produced by CRU_ITS IB commissioning tools"}}, {"ninj", VariantType::Int, 50, {"Number of injections per change, default is 50"}}, @@ -1982,7 +2014,8 @@ DataProcessorSpec getITSThresholdCalibratorSpec(const ITSCalibInpConf& inpConf) {"calculate-slope", VariantType::Bool, false, {"For Pulse Shape 2D: if enabled it calculate the slope of the charge vs strobe delay trend for each pixel and fill it in the output tree"}}, {"finalize-at-eos", VariantType::Bool, false, {"Call the finalize() method at the end of stream: to be used in case end-of-run flags are not available so to force calculations at end of run"}}, {"charge-a", VariantType::Int, 0, {"To use with --calculate-slope, it defines the charge (in DAC) for the 1st point used for the slope calculation"}}, - {"charge-b", VariantType::Int, 0, {"To use with --calculate-slope, it defines the charge (in DAC) for the 2nd point used for the slope calculation"}}}}; + {"charge-b", VariantType::Int, 0, {"To use with --calculate-slope, it defines the charge (in DAC) for the 2nd point used for the slope calculation"}}, + {"meb-select", VariantType::Int, -1, {"Select from which multi-event buffer consider the hits: 0,1 or 2"}}}}; } } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index a61f454822681..6f6be501403ad 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx @@ -147,40 +147,35 @@ void TrackerDPL::run(ProcessingContext& pc) physTriggers = pc.inputs().get>("phystrig"); } - // code further down does assignment to the rofs and the altered object is used for output - // we therefore need a copy of the vector rather than an object created directly on the input data, - // the output vector however is created directly inside the message memory thus avoiding copy by - // snapshot auto rofsinput = pc.inputs().get>("ROframes"); auto& rofs = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0, Lifetime::Timeframe}, rofsinput.begin(), rofsinput.end()); - auto& irFrames = pc.outputs().make>(Output{"ITS", "IRFRAMES", 0, Lifetime::Timeframe}); - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); // RS: this should come from CCDB + + irFrames.reserve(rofs.size()); int nBCPerTF = alpParams.roFrameLengthInBC; - LOG(info) << "ITSTracker pulled " << compClusters.size() << " clusters, " << rofs.size() << " RO frames"; + LOGP(info, "ITSTracker pulled {} clusters, {} RO frames", compClusters.size(), rofs.size()); const dataformats::MCTruthContainer* labels = nullptr; gsl::span mc2rofs; if (mIsMC) { labels = pc.inputs().get*>("itsmclabels").release(); - // get the array as read-only span, a snapshot is send forward - mc2rofs = pc.inputs().get>("ITSMC2ROframes"); + // get the array as read-only span, a snapshot is sent forward + pc.outputs().snapshot(Output{"ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe}, pc.inputs().get>("ITSMC2ROframes")); LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; } - std::vector tracks; auto& allClusIdx = pc.outputs().make>(Output{"ITS", "TRACKCLSID", 0, Lifetime::Timeframe}); - std::vector trackLabels; - std::vector verticesLabels; auto& allTracks = pc.outputs().make>(Output{"ITS", "TRACKS", 0, Lifetime::Timeframe}); - std::vector allTrackLabels; - std::vector allVerticesLabels; - auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0, Lifetime::Timeframe}); auto& vertices = pc.outputs().make>(Output{"ITS", "VERTICES", 0, Lifetime::Timeframe}); + // MC + static pmr::vector dummyMCLabTracks, dummyMCLabVerts; + auto& allTrackLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "TRACKSMCTR", 0, Lifetime::Timeframe}) : dummyMCLabTracks; + auto& allVerticesLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCTR", 0, Lifetime::Timeframe}) : dummyMCLabVerts; + std::uint32_t roFrame = 0; bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS); @@ -211,6 +206,7 @@ void TrackerDPL::run(ProcessingContext& pc) mTimeFrame->setMultiplicityCutMask(processingMask); float vertexerElapsedTime{0.f}; if (mRunVertexer) { + vertROFvec.reserve(rofs.size()); // Run seeding vertexer vertexerElapsedTime = mVertexer->clustersToVertices(logger); } else { // cosmics @@ -234,6 +230,7 @@ void TrackerDPL::run(ProcessingContext& pc) vertices.push_back(v); if (mIsMC) { auto vLabels = mTimeFrame->getPrimaryVerticesLabels(iRof)[iV]; + allVerticesLabels.reserve(allVerticesLabels.size() + vLabels.size()); std::copy(vLabels.begin(), vLabels.end(), std::back_inserter(allVerticesLabels)); } } @@ -270,14 +267,17 @@ void TrackerDPL::run(ProcessingContext& pc) mTimeFrame->setMultiplicityCutMask(processingMask); // Run CA tracker mTracker->clustersToTracks(logger, errorLogger); + size_t totTracks{mTimeFrame->getNumberOfTracks()}, totClusIDs{mTimeFrame->getNumberOfUsedClusters()}; + allTracks.reserve(totTracks); + allClusIdx.reserve(totClusIDs); + if (mTimeFrame->hasBogusClusters()) { LOG(warning) << fmt::format(" - The processed timeframe had {} clusters with wild z coordinates, check the dictionaries", mTimeFrame->hasBogusClusters()); } for (unsigned int iROF{0}; iROF < rofs.size(); ++iROF) { auto& rof{rofs[iROF]}; - tracks = mTimeFrame->getTracks(iROF); - trackLabels = mTimeFrame->getTracksLabel(iROF); + auto& tracks = mTimeFrame->getTracks(iROF); auto number{tracks.size()}; auto first{allTracks.size()}; int offset = -rof.getFirstEntry(); // cluster entry!!! @@ -287,8 +287,8 @@ void TrackerDPL::run(ProcessingContext& pc) if (processingMask[iROF]) { irFrames.emplace_back(rof.getBCData(), rof.getBCData() + nBCPerTF - 1).info = tracks.size(); } - - std::copy(trackLabels.begin(), trackLabels.end(), std::back_inserter(allTrackLabels)); + allTrackLabels.reserve(mTimeFrame->getTracksLabel(iROF).size()); // should be 0 if not MC + std::copy(mTimeFrame->getTracksLabel(iROF).begin(), mTimeFrame->getTracksLabel(iROF).end(), std::back_inserter(allTrackLabels)); // Some conversions that needs to be moved in the tracker internals for (unsigned int iTrk{0}; iTrk < tracks.size(); ++iTrk) { auto& trc{tracks[iTrk]}; @@ -297,6 +297,7 @@ void TrackerDPL::run(ProcessingContext& pc) for (int ic = TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! auto clid = trc.getClusterIndex(ic); if (clid >= 0) { + trc.setClusterSize(ic, mTimeFrame->getClusterSize(clid)); allClusIdx.push_back(clid); nclf++; } @@ -309,10 +310,6 @@ void TrackerDPL::run(ProcessingContext& pc) if (mIsMC) { LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); - - pc.outputs().snapshot(Output{"ITS", "TRACKSMCTR", 0, Lifetime::Timeframe}, allTrackLabels); - pc.outputs().snapshot(Output{"ITS", "VERTICESMCTR", 0, Lifetime::Timeframe}, allVerticesLabels); - pc.outputs().snapshot(Output{"ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe}, mc2rofs); } } mTimer.Stop(); diff --git a/Detectors/ITSMFT/MFT/assessment/include/MFTAssessment/MFTAssessment.h b/Detectors/ITSMFT/MFT/assessment/include/MFTAssessment/MFTAssessment.h index 132a58722eb5b..3c7238980cf23 100644 --- a/Detectors/ITSMFT/MFT/assessment/include/MFTAssessment/MFTAssessment.h +++ b/Detectors/ITSMFT/MFT/assessment/include/MFTAssessment/MFTAssessment.h @@ -138,6 +138,7 @@ class MFTAssessment std::unique_ptr mPositiveTrackPhi = nullptr; std::unique_ptr mNegativeTrackPhi = nullptr; std::unique_ptr mTrackEta = nullptr; + std::unique_ptr mTrackChi2pT = nullptr; std::unique_ptr mMFTClsZ = nullptr; std::unique_ptr mMFTClsOfTracksZ = nullptr; diff --git a/Detectors/ITSMFT/MFT/assessment/src/MFTAssessment.cxx b/Detectors/ITSMFT/MFT/assessment/src/MFTAssessment.cxx index 0967f314e8b51..2184bcaef0cde 100644 --- a/Detectors/ITSMFT/MFT/assessment/src/MFTAssessment.cxx +++ b/Detectors/ITSMFT/MFT/assessment/src/MFTAssessment.cxx @@ -46,6 +46,7 @@ void MFTAssessment::reset() mPositiveTrackPhi->Reset(); mNegativeTrackPhi->Reset(); mTrackEta->Reset(); + mTrackChi2pT->Reset(); mMFTClsZ->Reset(); mMFTClsOfTracksZ->Reset(); @@ -151,6 +152,8 @@ void MFTAssessment::createHistos() mTrackEta = std::make_unique("mMFTTrackEta", "Track #eta; #eta; # entries", 50, -4, -2); + mTrackChi2pT = std::make_unique("mMFTTrackChi2pT", "Track #chi^{2}/NDF vs p_{T}; #it{p}_{T} (GeV/c); #chi^{2}/NDF", 210, -0.5, 20.5, 210, -0.5, 20.5); + //---------------------------------------------------------------------------- mMFTClsZ = std::make_unique("mMFTClsZ", "Z of all clusters; Z (cm); # entries", 400, -80, -40); @@ -368,6 +371,7 @@ void MFTAssessment::runASyncQC(o2::framework::ProcessingContext& ctx) mTrackPhi->Fill(oneTrack.getPhi()); mTrackEta->Fill(oneTrack.getEta()); mTrackCotl->Fill(1. / oneTrack.getTanl()); + mTrackChi2pT->Fill(oneTrack.getPt(), oneTrack.getChi2OverNDF()); for (auto idisk = 0; idisk < 5; idisk++) { clsEntriesForRedundancy[idisk] = {-1, -1}; @@ -676,6 +680,7 @@ void MFTAssessment::getHistos(TObjArray& objar) objar.Add(mPositiveTrackPhi.get()); objar.Add(mNegativeTrackPhi.get()); objar.Add(mTrackEta.get()); + objar.Add(mTrackChi2pT.get()); //------ objar.Add(mMFTClsZ.get()); @@ -870,6 +875,8 @@ bool MFTAssessment::loadHistos() mTrackEta = std::unique_ptr((TH1F*)f->Get("mMFTTrackEta")); + mTrackChi2pT = std::unique_ptr((TH2F*)f->Get("mMFTTrackChi2pT")); + //--------------------------------------------------------------------------- mMFTClsZ = std::unique_ptr((TH1F*)f->Get("mMFTClsZ")); diff --git a/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx b/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx index ced7476d020f3..08cb444c983b6 100644 --- a/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx +++ b/Detectors/ITSMFT/MFT/base/src/HeatExchanger.cxx @@ -9,24 +9,22 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file HeatExchanger.cxx -/// \brief Class building the MFT heat exchanger -/// \author P. Demongodin, and Raphael Tieulent +/// \author P. Demongodin, Raphael Tieulent, Franck Manso -#include "TMath.h" -#include "TGeoManager.h" +#include "MFTBase/HeatExchanger.h" +#include "MFTBase/Constants.h" +#include "MFTBase/Geometry.h" +#include "MFTBase/MFTBaseParam.h" +#include "TGeoBBox.h" +#include "TGeoBoolNode.h" #include "TGeoCompositeShape.h" -#include "TGeoTube.h" -#include "TGeoTorus.h" #include "TGeoCone.h" -#include "TGeoBoolNode.h" -#include "TGeoBBox.h" +#include "TGeoManager.h" +#include "TGeoTorus.h" +#include "TGeoTube.h" #include "TGeoVolume.h" +#include "TMath.h" #include -#include "MFTBase/Constants.h" -#include "MFTBase/HeatExchanger.h" -#include "MFTBase/Geometry.h" -#include "MFTBase/MFTBaseParam.h" using namespace o2::mft; @@ -34,57 +32,15 @@ ClassImp(o2::mft::HeatExchanger); //_____________________________________________________________________________ HeatExchanger::HeatExchanger() - : mHalfDisk(nullptr), - mHalfDiskRotation(nullptr), - mHalfDiskTransformation(nullptr), - mRWater(0.), - mDRPipe(0.), - mHeatExchangerThickness(0.), - mCarbonThickness(0.), - mHalfDiskGap(0.), - mRohacellThickness(0.), - mNPart(), - mRMin(), - mZPlan(), - mSupportXDimensions(), - mSupportYDimensions(), - mLWater0(), - mXPosition0(), - mAngle0(), - mRadius0(), - mLpartial0(), - mLWater1(), - mXPosition1(), - mAngle1(), - mRadius1(), - mLpartial1(), - mLWater2(), - mXPosition2(), - mAngle2(), - mRadius2(), - mLpartial2(), - mLWater3(), - mXPosition3(), - mAngle3(), - mRadius3(), - mLpartial3(), - mRadius3fourth(), - mAngle3fourth(), - mBeta3fourth(), - mLWater4(), - mXPosition4(), - mAngle4(), - mRadius4(), - mLpartial4(), - mAngle4fifth(), - mRadius4fifth() + : mHalfDisk(nullptr), mHalfDiskRotation(nullptr), mHalfDiskTransformation(nullptr), mRWater(0.), mDRPipe(0.), mHeatExchangerThickness(0.), mCarbonThickness(0.), mHalfDiskGap(0.), mRohacellThickness(0.), mNPart(), mRMin(), mZPlan(), mSupportXDimensions(), mSupportYDimensions(), mLWater0(), mXPosition0(), mAngle0(), mRadius0(), mLpartial0(), mLWater1(), mXPosition1(), mAngle1(), mRadius1(), mLpartial1(), mLWater2(), mXPosition2(), mAngle2(), mRadius2(), mLpartial2(), mLWater3(), mXPosition3(), mAngle3(), mRadius3(), mLpartial3(), mRadius3fourth(), mAngle3fourth(), mBeta3fourth(), mLWater4(), mXPosition4(), mAngle4(), mRadius4(), mLpartial4(), mAngle4fifth(), mRadius4fifth() { auto& mftBaseParam = MFTBaseParam::Instance(); mRWater = 0.1 / 2.; // water pipe diameter 1 mm mDRPipe = 0.0025; // water pipe thickness 25 microns - // for the chips aligment --> decrease of the HE thickness then increase of the rohacell density + // for the chips aligment --> decrease of the HE thickness then increase of + // the rohacell density Double_t decreaseHE = 0.0; if (mftBaseParam.buildAlignment) { decreaseHE = 0.4; @@ -95,51 +51,10 @@ HeatExchanger::HeatExchanger() } //_____________________________________________________________________________ -HeatExchanger::HeatExchanger(Double_t rWater, Double_t dRPipe, Double_t heatExchangerThickness, +HeatExchanger::HeatExchanger(Double_t rWater, Double_t dRPipe, + Double_t heatExchangerThickness, Double_t carbonThickness) - : mHalfDisk(nullptr), - mHalfDiskRotation(nullptr), - mHalfDiskTransformation(nullptr), - mRWater(rWater), - mDRPipe(dRPipe), - mHeatExchangerThickness(heatExchangerThickness), - mCarbonThickness(carbonThickness), - mHalfDiskGap(0.), - mRohacellThickness(0.), - mNPart(), - mRMin(), - mZPlan(), - mSupportXDimensions(), - mSupportYDimensions(), - mXPosition0(), - mAngle0(), - mRadius0(), - mLpartial0(), - mLWater1(), - mXPosition1(), - mAngle1(), - mRadius1(), - mLpartial1(), - mLWater2(), - mXPosition2(), - mAngle2(), - mRadius2(), - mLpartial2(), - mLWater3(), - mXPosition3(), - mAngle3(), - mRadius3(), - mLpartial3(), - mRadius3fourth(), - mAngle3fourth(), - mBeta3fourth(), - mLWater4(), - mXPosition4(), - mAngle4(), - mRadius4(), - mLpartial4(), - mAngle4fifth(), - mRadius4fifth() + : mHalfDisk(nullptr), mHalfDiskRotation(nullptr), mHalfDiskTransformation(nullptr), mRWater(rWater), mDRPipe(dRPipe), mHeatExchangerThickness(heatExchangerThickness), mCarbonThickness(carbonThickness), mHalfDiskGap(0.), mRohacellThickness(0.), mNPart(), mRMin(), mZPlan(), mSupportXDimensions(), mSupportYDimensions(), mXPosition0(), mAngle0(), mRadius0(), mLpartial0(), mLWater1(), mXPosition1(), mAngle1(), mRadius1(), mLpartial1(), mLWater2(), mXPosition2(), mAngle2(), mRadius2(), mLpartial2(), mLWater3(), mXPosition3(), mAngle3(), mRadius3(), mLpartial3(), mRadius3fourth(), mAngle3fourth(), mBeta3fourth(), mLWater4(), mXPosition4(), mAngle4(), mRadius4(), mLpartial4(), mAngle4fifth(), mRadius4fifth() { initParameters(); } @@ -257,27 +172,55 @@ void HeatExchanger::createManifold(Int_t disk) Double_t lengthBottom1; lengthBottom1 = lengthMiddle1[disk]; - auto* Top1 = new TGeoBBox(Form("Top1MF%d", disk), lengthTop1[disk] / 2, widthTop1[disk] / 2, thicknessTop[disk] / 2); - auto* Top2 = new TGeoBBox(Form("Top2MF%d", disk), lengthTop2 / 2, widthTop2 / 2, thicknessTop[disk] / 2); - auto* Top3 = new TGeoTube(Form("Top3MF%d", disk), 0, cornerRadiusTop[disk], thicknessTop[disk] / 2); - auto* Middle1 = new TGeoBBox(Form("Middle1MF%d", disk), lengthMiddle1[disk] / 2, widthMiddle1[disk] / 2, thicknessMiddle[disk] / 2); - auto* Bottom1 = new TGeoBBox(Form("Bottom1MF%d", disk), lengthBottom1 / 2, widthBottom1[disk] / 2, thicknessBottom[disk] / 2); + auto* Top1 = new TGeoBBox(Form("Top1MF%d", disk), lengthTop1[disk] / 2, + widthTop1[disk] / 2, thicknessTop[disk] / 2); + auto* Top2 = new TGeoBBox(Form("Top2MF%d", disk), lengthTop2 / 2, + widthTop2 / 2, thicknessTop[disk] / 2); + auto* Top3 = new TGeoTube(Form("Top3MF%d", disk), 0, cornerRadiusTop[disk], + thicknessTop[disk] / 2); + auto* Middle1 = + new TGeoBBox(Form("Middle1MF%d", disk), lengthMiddle1[disk] / 2, + widthMiddle1[disk] / 2, thicknessMiddle[disk] / 2); + auto* Bottom1 = + new TGeoBBox(Form("Bottom1MF%d", disk), lengthBottom1 / 2, + widthBottom1[disk] / 2, thicknessBottom[disk] / 2); TGeoTranslation* tTop[7]; - tTop[0] = new TGeoTranslation(Form("tTop1MF%d", disk), 0., 0., thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); - tTop[1] = new TGeoTranslation(Form("tTop2MF%d", disk), lengthTop1[disk] / 2 + lengthTop2 / 2, 0., thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); - tTop[2] = new TGeoTranslation(Form("tTop3MF%d", disk), lengthTop1[disk] / 2, widthTop1[disk] / 2 - cornerRadiusTop[disk], thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); - tTop[3] = new TGeoTranslation(Form("tTop4MF%d", disk), lengthTop1[disk] / 2, -(widthTop1[disk] / 2 - cornerRadiusTop[disk]), thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); - tTop[4] = new TGeoTranslation(Form("tTop5MF%d", disk), -(lengthTop1[disk] / 2 + lengthTop2 / 2), 0., thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); - tTop[5] = new TGeoTranslation(Form("tTop6MF%d", disk), -lengthTop1[disk] / 2, widthTop1[disk] / 2 - cornerRadiusTop[disk], thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); - tTop[6] = new TGeoTranslation(Form("tTop7MF%d", disk), -lengthTop1[disk] / 2, -(widthTop1[disk] / 2 - cornerRadiusTop[disk]), thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); + tTop[0] = + new TGeoTranslation(Form("tTop1MF%d", disk), 0., 0., + thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); + tTop[1] = new TGeoTranslation( + Form("tTop2MF%d", disk), lengthTop1[disk] / 2 + lengthTop2 / 2, 0., + thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); + tTop[2] = + new TGeoTranslation(Form("tTop3MF%d", disk), lengthTop1[disk] / 2, + widthTop1[disk] / 2 - cornerRadiusTop[disk], + thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); + tTop[3] = + new TGeoTranslation(Form("tTop4MF%d", disk), lengthTop1[disk] / 2, + -(widthTop1[disk] / 2 - cornerRadiusTop[disk]), + thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); + tTop[4] = new TGeoTranslation( + Form("tTop5MF%d", disk), -(lengthTop1[disk] / 2 + lengthTop2 / 2), 0., + thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); + tTop[5] = + new TGeoTranslation(Form("tTop6MF%d", disk), -lengthTop1[disk] / 2, + widthTop1[disk] / 2 - cornerRadiusTop[disk], + thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); + tTop[6] = + new TGeoTranslation(Form("tTop7MF%d", disk), -lengthTop1[disk] / 2, + -(widthTop1[disk] / 2 - cornerRadiusTop[disk]), + thicknessMiddle[disk] / 2 + thicknessTop[disk] / 2); for (Int_t i = 0; i < 7; ++i) { tTop[i]->RegisterYourself(); } - TGeoTranslation* tMiddle1 = new TGeoTranslation(Form("tMiddle1MF%d", disk), 0, 0, 0); - TGeoTranslation* tBottom1 = new TGeoTranslation(Form("tBottom1MF%d", disk), 0, 0, -(thicknessMiddle[disk] / 2 + thicknessBottom[disk] / 2)); + TGeoTranslation* tMiddle1 = + new TGeoTranslation(Form("tMiddle1MF%d", disk), 0, 0, 0); + TGeoTranslation* tBottom1 = new TGeoTranslation( + Form("tBottom1MF%d", disk), 0, 0, + -(thicknessMiddle[disk] / 2 + thicknessBottom[disk] / 2)); tMiddle1->RegisterYourself(); tBottom1->RegisterYourself(); @@ -302,17 +245,28 @@ void HeatExchanger::createManifold(Int_t disk) lengthPipeHole1[3] = 0.95; lengthPipeHole1[4] = 0.95; - Double_t lengthPipeHole2 = (thicknessTop[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) - lengthPipeHole1[disk]; - - TGeoTube* Pipe1 = new TGeoTube("Pipe1", 0, radiusPipeHole1[disk], lengthPipeHole1[disk] / 2 + Geometry::sEpsilon); - TGeoCone* Pipe2 = new TGeoCone("Pipe2", lengthPipeHole2 / 2 + Geometry::sEpsilon, 0, radiusPipeHole1[disk], 0, radiusPipeHole2[disk]); - - TGeoTranslation* tPipePart1 = new TGeoTranslation(Form("tPipePart1MF%d", disk), 0, 0, 0); - TGeoTranslation* tPipePart2 = new TGeoTranslation(Form("tPipePart2MF%d", disk), 0, 0, lengthPipeHole1[disk] / 2 + lengthPipeHole2 / 2); + Double_t lengthPipeHole2 = + (thicknessTop[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) - + lengthPipeHole1[disk]; + + TGeoTube* Pipe1 = + new TGeoTube("Pipe1", 0, radiusPipeHole1[disk], + lengthPipeHole1[disk] / 2 + Geometry::sEpsilon); + TGeoCone* Pipe2 = + new TGeoCone("Pipe2", lengthPipeHole2 / 2 + Geometry::sEpsilon, 0, + radiusPipeHole1[disk], 0, radiusPipeHole2[disk]); + + TGeoTranslation* tPipePart1 = + new TGeoTranslation(Form("tPipePart1MF%d", disk), 0, 0, 0); + TGeoTranslation* tPipePart2 = + new TGeoTranslation(Form("tPipePart2MF%d", disk), 0, 0, + lengthPipeHole1[disk] / 2 + lengthPipeHole2 / 2); tPipePart1->RegisterYourself(); tPipePart2->RegisterYourself(); - TGeoCompositeShape* shapePipe = new TGeoCompositeShape(Form("shapePipeMF%d", disk), Form("Pipe1:tPipePart1MF%d + Pipe2:tPipePart2MF%d", disk, disk)); + TGeoCompositeShape* shapePipe = new TGeoCompositeShape( + Form("shapePipeMF%d", disk), + Form("Pipe1:tPipePart1MF%d + Pipe2:tPipePart2MF%d", disk, disk)); Int_t nPipeRow[5]; nPipeRow[0] = 3; @@ -323,7 +277,8 @@ void HeatExchanger::createManifold(Int_t disk) TGeoTranslation* tPipe; - // The lengthBulge is a parameter for manifold2. This is required to tune the water pipe position. + // The lengthBulge is a parameter for manifold2. This is required to tune the + // water pipe position. Double_t lengthBulge[5]; lengthBulge[0] = 0.395; lengthBulge[1] = 0.395; @@ -355,19 +310,38 @@ void HeatExchanger::createManifold(Int_t disk) offsetPipeRow[4][3] = mXPosition4[3] - lengthBulge[4] / 2; offsetPipeRow[4][4] = mXPosition4[4] - lengthBulge[4] / 2; - Double_t deltaz = mHeatExchangerThickness - Geometry::sKaptonOnCarbonThickness * 4 - 2 * mCarbonThickness; + Double_t deltaz = mHeatExchangerThickness - + Geometry::sKaptonOnCarbonThickness * 4 - + 2 * mCarbonThickness; Double_t lengthTwoPipeCol[5]; - lengthTwoPipeCol[0] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness; - lengthTwoPipeCol[1] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness; - lengthTwoPipeCol[2] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness; - lengthTwoPipeCol[3] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness; - lengthTwoPipeCol[4] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness; + lengthTwoPipeCol[0] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - + mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness - + 2 * Geometry::sKaptonOnCarbonThickness; + lengthTwoPipeCol[1] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - + mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness - + 2 * Geometry::sKaptonOnCarbonThickness; + lengthTwoPipeCol[2] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - + mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness - + 2 * Geometry::sKaptonOnCarbonThickness; + lengthTwoPipeCol[3] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - + mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness - + 2 * Geometry::sKaptonOnCarbonThickness; + lengthTwoPipeCol[4] = mZPlan[disk] + deltaz / 2. - mCarbonThickness - + mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness - + 2 * Geometry::sKaptonOnCarbonThickness; TString namePipe = ""; for (Int_t iPipeRow = 0; iPipeRow < nPipeRow[disk]; iPipeRow++) { - tPipe = new TGeoTranslation(Form("tPipe%dMF%d", iPipeRow + 1, disk), lengthMiddle1[disk] / 2 - offsetPipeRow[disk][iPipeRow], 0, 0); + tPipe = new TGeoTranslation( + Form("tPipe%dMF%d", iPipeRow + 1, disk), + lengthMiddle1[disk] / 2 - offsetPipeRow[disk][iPipeRow], 0, 0); tPipe->RegisterYourself(); if (iPipeRow == nPipeRow[disk] - 1) { @@ -377,22 +351,45 @@ void HeatExchanger::createManifold(Int_t disk) } } - TGeoCompositeShape* posiPipeOneCol = new TGeoCompositeShape(Form("posiPipeOneColMF%d", disk), namePipe); + TGeoCompositeShape* posiPipeOneCol = + new TGeoCompositeShape(Form("posiPipeOneColMF%d", disk), namePipe); - tPipe = new TGeoTranslation(Form("tPipeLeftMF%d", disk), 0, -lengthTwoPipeCol[disk], 0); + tPipe = new TGeoTranslation(Form("tPipeLeftMF%d", disk), 0, + -lengthTwoPipeCol[disk], 0); tPipe->RegisterYourself(); - tPipe = new TGeoTranslation(Form("tPipeRightMF%d", disk), 0, lengthTwoPipeCol[disk], 0); + tPipe = new TGeoTranslation(Form("tPipeRightMF%d", disk), 0, + lengthTwoPipeCol[disk], 0); tPipe->RegisterYourself(); - TGeoCompositeShape* shapePipes = new TGeoCompositeShape(Form("shapePipesMF%d", disk), Form("posiPipeOneColMF%d:tPipeLeftMF%d + posiPipeOneColMF%d:tPipeRightMF%d", disk, disk, disk, disk)); - TGeoCompositeShape* shapeBase = new TGeoCompositeShape(Form("shapeBaseMF%d", disk), Form("Top1MF%d:tTop1MF%d + Top2MF%d:tTop2MF%d + Top3MF%d:tTop3MF%d + Top3MF%d:tTop4MF%d + Top2MF%d:tTop5MF%d + Top3MF%d:tTop6MF%d + Top3MF%d:tTop7MF%d + Middle1MF%d:tMiddle1MF%d + Bottom1MF%d:tBottom1MF%d", disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk)); - - TGeoTranslation* tBase = new TGeoTranslation(Form("tBaseMF%d", disk), 0, 0, -((thicknessTop[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2 - (thicknessBottom[disk] + thicknessMiddle[disk] / 2))); + TGeoCompositeShape* shapePipes = new TGeoCompositeShape( + Form("shapePipesMF%d", disk), Form("posiPipeOneColMF%d:tPipeLeftMF%d + " + "posiPipeOneColMF%d:tPipeRightMF%d", + disk, disk, disk, disk)); + TGeoCompositeShape* shapeBase = new TGeoCompositeShape( + Form("shapeBaseMF%d", disk), + Form("Top1MF%d:tTop1MF%d + Top2MF%d:tTop2MF%d + Top3MF%d:tTop3MF%d + " + "Top3MF%d:tTop4MF%d + Top2MF%d:tTop5MF%d + Top3MF%d:tTop6MF%d + " + "Top3MF%d:tTop7MF%d + Middle1MF%d:tMiddle1MF%d + " + "Bottom1MF%d:tBottom1MF%d", + disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, + disk, disk, disk, disk, disk, disk, disk)); + + TGeoTranslation* tBase = new TGeoTranslation( + Form("tBaseMF%d", disk), 0, 0, + -((thicknessTop[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / + 2 - + (thicknessBottom[disk] + thicknessMiddle[disk] / 2))); tBase->RegisterYourself(); - TGeoTranslation* tPipes = new TGeoTranslation(Form("tPiesMF%d", disk), 0, 0, -((lengthPipeHole1[disk] + lengthPipeHole2) / 2 - lengthPipeHole1[disk] / 2)); + TGeoTranslation* tPipes = + new TGeoTranslation(Form("tPiesMF%d", disk), 0, 0, + -((lengthPipeHole1[disk] + lengthPipeHole2) / 2 - + lengthPipeHole1[disk] / 2)); tPipes->RegisterYourself(); - TGeoCompositeShape* shapeManifold1 = new TGeoCompositeShape(Form("shapeManifold1MF%d", disk), Form("shapeBaseMF%d:tBaseMF%d - shapePipesMF%d:tPiesMF%d", disk, disk, disk, disk)); + TGeoCompositeShape* shapeManifold1 = new TGeoCompositeShape( + Form("shapeManifold1MF%d", disk), + Form("shapeBaseMF%d:tBaseMF%d - shapePipesMF%d:tPiesMF%d", disk, disk, + disk, disk)); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Manifold2 @@ -438,33 +435,67 @@ void HeatExchanger::createManifold(Int_t disk) thicknessBodyBathtub2[3] = 0.4; thicknessBodyBathtub2[4] = 0.4; - auto* coverBody1 = new TGeoBBox(Form("coverBody1MF%d", disk), lengthBody / 2, widthBody / 2, thicknessBody[disk] / 2); - - auto* coverBodyBathtub1 = new TGeoBBox(Form("coverBodyBathtub1MF%d", disk), lengthBodyBathtub1 / 2 + Geometry::sEpsilon, widthBodyBathtub1 / 2 + Geometry::sEpsilon, - thicknessBodyBathtub1[disk] / 2 + Geometry::sEpsilon); - auto* coverBodyBathtub2 = new TGeoBBox(Form("coverBodyBathtub2MF%d", disk), lengthBodyBathtub2 / 2 + Geometry::sEpsilon, widthBodyBathtub2 / 2 + Geometry::sEpsilon, - thicknessBodyBathtub2[disk] / 2 + Geometry::sEpsilon); - auto* coverBodyBathtub3 = new TGeoTube(Form("coverBodyBathtub3MF%d", disk), 0, cornerRadiusBodyBathtub1[disk] + Geometry::sEpsilon, - thicknessBodyBathtub2[disk] / 2 + Geometry::sEpsilon); + auto* coverBody1 = new TGeoBBox(Form("coverBody1MF%d", disk), lengthBody / 2, + widthBody / 2, thicknessBody[disk] / 2); + + auto* coverBodyBathtub1 = + new TGeoBBox(Form("coverBodyBathtub1MF%d", disk), + lengthBodyBathtub1 / 2 + Geometry::sEpsilon, + widthBodyBathtub1 / 2 + Geometry::sEpsilon, + thicknessBodyBathtub1[disk] / 2 + Geometry::sEpsilon); + auto* coverBodyBathtub2 = + new TGeoBBox(Form("coverBodyBathtub2MF%d", disk), + lengthBodyBathtub2 / 2 + Geometry::sEpsilon, + widthBodyBathtub2 / 2 + Geometry::sEpsilon, + thicknessBodyBathtub2[disk] / 2 + Geometry::sEpsilon); + auto* coverBodyBathtub3 = + new TGeoTube(Form("coverBodyBathtub3MF%d", disk), 0, + cornerRadiusBodyBathtub1[disk] + Geometry::sEpsilon, + thicknessBodyBathtub2[disk] / 2 + Geometry::sEpsilon); TGeoTranslation* tcoverBodyBathtub[7]; - tcoverBodyBathtub[0] = new TGeoTranslation(Form("tcoverBodyBathtub1MF%d", disk), 0., 0., 0); - - tcoverBodyBathtub[1] = new TGeoTranslation(Form("tcoverBodyBathtub2MF%d", disk), lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2, 0., 0.); - tcoverBodyBathtub[2] = new TGeoTranslation(Form("tcoverBodyBathtub3MF%d", disk), lengthBodyBathtub1 / 2, widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); - tcoverBodyBathtub[3] = new TGeoTranslation(Form("tcoverBodyBathtub4MF%d", disk), lengthBodyBathtub1 / 2, -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); - - tcoverBodyBathtub[4] = new TGeoTranslation(Form("tcoverBodyBathtub5MF%d", disk), -(lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2), 0., 0.); - tcoverBodyBathtub[5] = new TGeoTranslation(Form("tcoverBodyBathtub6MF%d", disk), -(lengthBodyBathtub1 / 2), widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); - tcoverBodyBathtub[6] = new TGeoTranslation(Form("tcoverBodyBathtub7MF%d", disk), -(lengthBodyBathtub1 / 2), -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); + tcoverBodyBathtub[0] = + new TGeoTranslation(Form("tcoverBodyBathtub1MF%d", disk), 0., 0., 0); + + tcoverBodyBathtub[1] = new TGeoTranslation( + Form("tcoverBodyBathtub2MF%d", disk), + lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2, 0., 0.); + tcoverBodyBathtub[2] = new TGeoTranslation( + Form("tcoverBodyBathtub3MF%d", disk), lengthBodyBathtub1 / 2, + widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); + tcoverBodyBathtub[3] = new TGeoTranslation( + Form("tcoverBodyBathtub4MF%d", disk), lengthBodyBathtub1 / 2, + -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); + + tcoverBodyBathtub[4] = new TGeoTranslation( + Form("tcoverBodyBathtub5MF%d", disk), + -(lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2), 0., 0.); + tcoverBodyBathtub[5] = new TGeoTranslation( + Form("tcoverBodyBathtub6MF%d", disk), -(lengthBodyBathtub1 / 2), + widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); + tcoverBodyBathtub[6] = new TGeoTranslation( + Form("tcoverBodyBathtub7MF%d", disk), -(lengthBodyBathtub1 / 2), + -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); for (Int_t i = 0; i < 7; ++i) { tcoverBodyBathtub[i]->RegisterYourself(); } - TGeoCompositeShape* shapeCoverBathtub = new TGeoCompositeShape(Form("shapeCoverBathtubMF%d", disk), Form("coverBodyBathtub1MF%d + coverBodyBathtub2MF%d:tcoverBodyBathtub2MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub3MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub4MF%d + coverBodyBathtub2MF%d:tcoverBodyBathtub5MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub6MF%d + coverBodyBathtub3MF%d:tcoverBodyBathtub7MF%d", disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk)); - - TGeoTranslation* tcoverBathtub = new TGeoTranslation(Form("tcoverBathtubMF%d", disk), 0, 0, thicknessBody[disk] / 2 - thicknessBodyBathtub1[disk] / 2); + TGeoCompositeShape* shapeCoverBathtub = new TGeoCompositeShape( + Form("shapeCoverBathtubMF%d", disk), + Form("coverBodyBathtub1MF%d + " + "coverBodyBathtub2MF%d:tcoverBodyBathtub2MF%d + " + "coverBodyBathtub3MF%d:tcoverBodyBathtub3MF%d + " + "coverBodyBathtub3MF%d:tcoverBodyBathtub4MF%d + " + "coverBodyBathtub2MF%d:tcoverBodyBathtub5MF%d + " + "coverBodyBathtub3MF%d:tcoverBodyBathtub6MF%d + " + "coverBodyBathtub3MF%d:tcoverBodyBathtub7MF%d", + disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, + disk, disk)); + + TGeoTranslation* tcoverBathtub = new TGeoTranslation( + Form("tcoverBathtubMF%d", disk), 0, 0, + thicknessBody[disk] / 2 - thicknessBodyBathtub1[disk] / 2); tcoverBathtub->RegisterYourself(); Double_t cornerRadiusStep1[5]; @@ -512,36 +543,68 @@ void HeatExchanger::createManifold(Int_t disk) Double_t thicknessStep3; thicknessStep3 = thicknessStep1[disk]; - auto* coverBodyStep1 = new TGeoBBox(Form("coverBodyStep1MF%d", disk), (lengthStep1[disk] - cornerRadiusStep1[disk]) / 2 + Geometry::sEpsilon, widthStep1[disk] / 2 + Geometry::sEpsilon, - thicknessStep1[disk] / 2 + Geometry::sEpsilon); - auto* coverBodyStep2 = new TGeoBBox(Form("coverBodyStep2MF%d", disk), lengthStep2 / 2 + Geometry::sEpsilon, widthStep2 / 2 + Geometry::sEpsilon, + auto* coverBodyStep1 = new TGeoBBox( + Form("coverBodyStep1MF%d", disk), + (lengthStep1[disk] - cornerRadiusStep1[disk]) / 2 + Geometry::sEpsilon, + widthStep1[disk] / 2 + Geometry::sEpsilon, + thicknessStep1[disk] / 2 + Geometry::sEpsilon); + auto* coverBodyStep2 = new TGeoBBox(Form("coverBodyStep2MF%d", disk), + lengthStep2 / 2 + Geometry::sEpsilon, + widthStep2 / 2 + Geometry::sEpsilon, thicknessStep2 / 2 + Geometry::sEpsilon); - auto* coverBodyStep3 = new TGeoTube(Form("coverBodyStep3MF%d", disk), 0, cornerRadiusStep1[disk] + Geometry::sEpsilon, - thicknessStep1[disk] / 2 + Geometry::sEpsilon); - auto* coverBodyStep4 = new TGeoBBox(Form("coverBodyStep4MF%d", disk), lengthStep3[disk] / 2 + Geometry::sEpsilon, widthStep3 / 2 + Geometry::sEpsilon, - thicknessStep3 / 2 + Geometry::sEpsilon); + auto* coverBodyStep3 = + new TGeoTube(Form("coverBodyStep3MF%d", disk), 0, + cornerRadiusStep1[disk] + Geometry::sEpsilon, + thicknessStep1[disk] / 2 + Geometry::sEpsilon); + auto* coverBodyStep4 = + new TGeoBBox(Form("coverBodyStep4MF%d", disk), + lengthStep3[disk] / 2 + Geometry::sEpsilon, + widthStep3 / 2 + Geometry::sEpsilon, + thicknessStep3 / 2 + Geometry::sEpsilon); TGeoTranslation* tcoverBodyStep[4]; - tcoverBodyStep[0] = new TGeoTranslation(Form("tcoverBodyStep1MF%d", disk), 0., 0., 0); - tcoverBodyStep[1] = new TGeoTranslation(Form("tcoverBodyStep2MF%d", disk), (lengthStep1[disk] - cornerRadiusStep1[disk]) / 2 + lengthStep2 / 2, cornerRadiusStep1[disk] / 2, 0.); - tcoverBodyStep[2] = new TGeoTranslation(Form("tcoverBodyStep3MF%d", disk), (lengthStep1[disk] - cornerRadiusStep1[disk]) / 2, -(widthStep1[disk] / 2 - cornerRadiusStep1[disk]), 0.); - tcoverBodyStep[3] = new TGeoTranslation(Form("tcoverBodyStep4MF%d", disk), -(lengthStep1[disk] - lengthStep2) / 2, -widthStep1[disk] / 2, 0.); - - TGeoRotation* rcoverBodyStep = new TGeoRotation(Form("rcoverBodyStep4MF%d", disk), 45, 0, 0); + tcoverBodyStep[0] = + new TGeoTranslation(Form("tcoverBodyStep1MF%d", disk), 0., 0., 0); + tcoverBodyStep[1] = new TGeoTranslation( + Form("tcoverBodyStep2MF%d", disk), + (lengthStep1[disk] - cornerRadiusStep1[disk]) / 2 + lengthStep2 / 2, + cornerRadiusStep1[disk] / 2, 0.); + tcoverBodyStep[2] = new TGeoTranslation( + Form("tcoverBodyStep3MF%d", disk), + (lengthStep1[disk] - cornerRadiusStep1[disk]) / 2, + -(widthStep1[disk] / 2 - cornerRadiusStep1[disk]), 0.); + tcoverBodyStep[3] = new TGeoTranslation( + Form("tcoverBodyStep4MF%d", disk), -(lengthStep1[disk] - lengthStep2) / 2, + -widthStep1[disk] / 2, 0.); + + TGeoRotation* rcoverBodyStep = + new TGeoRotation(Form("rcoverBodyStep4MF%d", disk), 45, 0, 0); rcoverBodyStep->RegisterYourself(); TGeoCombiTrans* combtcoverBodyStep[4]; - combtcoverBodyStep[3] = new TGeoCombiTrans(Form("combtcoverBodyStep4MF%d", disk), -(lengthStep1[disk] - lengthStep2) / 2, -widthStep1[disk] / 2, 0., rcoverBodyStep); + combtcoverBodyStep[3] = + new TGeoCombiTrans(Form("combtcoverBodyStep4MF%d", disk), + -(lengthStep1[disk] - lengthStep2) / 2, + -widthStep1[disk] / 2, 0., rcoverBodyStep); combtcoverBodyStep[3]->RegisterYourself(); for (Int_t i = 0; i < 4; ++i) { tcoverBodyStep[i]->RegisterYourself(); } - TGeoCompositeShape* shapeStep = new TGeoCompositeShape(Form("shapeStepMF%d", disk), Form("coverBodyStep1MF%d:tcoverBodyStep1MF%d + coverBodyStep2MF%d:tcoverBodyStep2MF%d + coverBodyStep3MF%d:tcoverBodyStep3MF%d + coverBodyStep4MF%d:combtcoverBodyStep4MF%d", disk, disk, disk, disk, disk, disk, disk, disk)); - - TGeoTranslation* tcoverStep = new TGeoTranslation(Form("tcoverStepMF%d", disk), -(lengthMiddle1[disk] / 2 - (lengthStep1[disk] / 2 - lengthStep2 / 2)), - (widthBody / 2 - widthStep1[disk] / 2), -(thicknessBody[disk] / 2 - thicknessStep1[disk] / 2)); + TGeoCompositeShape* shapeStep = new TGeoCompositeShape( + Form("shapeStepMF%d", disk), + Form("coverBodyStep1MF%d:tcoverBodyStep1MF%d + " + "coverBodyStep2MF%d:tcoverBodyStep2MF%d + " + "coverBodyStep3MF%d:tcoverBodyStep3MF%d + " + "coverBodyStep4MF%d:combtcoverBodyStep4MF%d", + disk, disk, disk, disk, disk, disk, disk, disk)); + + TGeoTranslation* tcoverStep = new TGeoTranslation( + Form("tcoverStepMF%d", disk), + -(lengthMiddle1[disk] / 2 - (lengthStep1[disk] / 2 - lengthStep2 / 2)), + (widthBody / 2 - widthStep1[disk] / 2), + -(thicknessBody[disk] / 2 - thicknessStep1[disk] / 2)); tcoverStep->RegisterYourself(); Double_t widthBulge[5]; @@ -567,25 +630,41 @@ void HeatExchanger::createManifold(Int_t disk) thicknessBulgeSub[3] = 0.5; thicknessBulgeSub[4] = 0.5; - auto* coverBodyBulge = new TGeoBBox(Form("coverBodyBulgeMF%d", disk), lengthBulge[disk] / 2, widthBulge[disk] / 2, thicknessBulge / 2); - auto* coverBodyBulgeSub = new TGeoBBox(Form("coverBodyBulgeSubMF%d", disk), lengthBulgeSub[disk] / 2, widthBulgeSub / 2, thicknessBulgeSub[disk] / 2 + Geometry::sEpsilon); + auto* coverBodyBulge = + new TGeoBBox(Form("coverBodyBulgeMF%d", disk), lengthBulge[disk] / 2, + widthBulge[disk] / 2, thicknessBulge / 2); + auto* coverBodyBulgeSub = new TGeoBBox( + Form("coverBodyBulgeSubMF%d", disk), lengthBulgeSub[disk] / 2, + widthBulgeSub / 2, thicknessBulgeSub[disk] / 2 + Geometry::sEpsilon); - TGeoRotation* rcoverBodyBulgeSub = new TGeoRotation(Form("rcoverBodyBulgeSubMF%d", disk), 0, 90, 45); + TGeoRotation* rcoverBodyBulgeSub = + new TGeoRotation(Form("rcoverBodyBulgeSubMF%d", disk), 0, 90, 45); rcoverBodyBulgeSub->RegisterYourself(); - TGeoTranslation* tcoverBodyBulgeSub = new TGeoTranslation(Form("tcoverBodyBulgeSubMF%d", disk), -lengthBulge[disk] / 2, 0, 0); + TGeoTranslation* tcoverBodyBulgeSub = new TGeoTranslation( + Form("tcoverBodyBulgeSubMF%d", disk), -lengthBulge[disk] / 2, 0, 0); tcoverBodyBulgeSub->RegisterYourself(); - TGeoCombiTrans* combtcoverBodyBulgeSub = new TGeoCombiTrans(Form("combtcoverBodyBulgeSubMF%d", disk), -lengthBulge[disk] / 2, 0, 0, rcoverBodyBulgeSub); + TGeoCombiTrans* combtcoverBodyBulgeSub = + new TGeoCombiTrans(Form("combtcoverBodyBulgeSubMF%d", disk), + -lengthBulge[disk] / 2, 0, 0, rcoverBodyBulgeSub); combtcoverBodyBulgeSub->RegisterYourself(); - TGeoCompositeShape* shapeBulge = new TGeoCompositeShape(Form("shapeBulgeMF%d", disk), Form("coverBodyBulgeMF%d - coverBodyBulgeSubMF%d:combtcoverBodyBulgeSubMF%d", disk, disk, disk)); + TGeoCompositeShape* shapeBulge = new TGeoCompositeShape( + Form("shapeBulgeMF%d", disk), + Form("coverBodyBulgeMF%d - " + "coverBodyBulgeSubMF%d:combtcoverBodyBulgeSubMF%d", + disk, disk, disk)); - TGeoTranslation* tcoverBulge = new TGeoTranslation(Form("tcoverBulgeMF%d", disk), -(lengthMiddle1[disk] / 2 + lengthBulge[disk] / 2), -(widthBody / 2 - widthBulge[disk] / 2), 0); + TGeoTranslation* tcoverBulge = + new TGeoTranslation(Form("tcoverBulgeMF%d", disk), + -(lengthMiddle1[disk] / 2 + lengthBulge[disk] / 2), + -(widthBody / 2 - widthBulge[disk] / 2), 0); tcoverBulge->RegisterYourself(); Double_t holeRadius[5]; - holeRadius[0] = 0.25; // 0.207; overlap issue of thread, increase up to 0.25, fm + holeRadius[0] = + 0.25; // 0.207; overlap issue of thread, increase up to 0.25, fm holeRadius[1] = 0.25; // 0.207; holeRadius[2] = 0.25; // 0.207; holeRadius[3] = 0.25; // 0.207; @@ -597,26 +676,52 @@ void HeatExchanger::createManifold(Int_t disk) holeOffset[3] = 0.65; holeOffset[4] = 0.65; - auto* coverBodyHole = new TGeoTube(Form("coverBodyHoleMF%d", disk), 0, holeRadius[disk], thicknessBody[disk] / 2 + Geometry::sEpsilon); - TGeoTranslation* tcoverBodyHole = new TGeoTranslation(Form("tcoverBodyHoleMF%d", disk), lengthBody / 2 - holeOffset[disk], 0, 0); + auto* coverBodyHole = + new TGeoTube(Form("coverBodyHoleMF%d", disk), 0, holeRadius[disk], + thicknessBody[disk] / 2 + Geometry::sEpsilon); + TGeoTranslation* tcoverBodyHole = + new TGeoTranslation(Form("tcoverBodyHoleMF%d", disk), + lengthBody / 2 - holeOffset[disk], 0, 0); tcoverBodyHole->RegisterYourself(); - TGeoCompositeShape* shapeManifold2 = new TGeoCompositeShape(Form("shapeManifold2MF%d", disk), Form("coverBody1MF%d - coverBodyHoleMF%d:tcoverBodyHoleMF%d - shapeCoverBathtubMF%d:tcoverBathtubMF%d - shapeStepMF%d:tcoverStepMF%d + shapeBulgeMF%d:tcoverBulgeMF%d", disk, disk, disk, disk, disk, disk, disk, disk, disk)); + TGeoCompositeShape* shapeManifold2 = new TGeoCompositeShape( + Form("shapeManifold2MF%d", disk), + Form("coverBody1MF%d - coverBodyHoleMF%d:tcoverBodyHoleMF%d - " + "shapeCoverBathtubMF%d:tcoverBathtubMF%d - " + "shapeStepMF%d:tcoverStepMF%d + shapeBulgeMF%d:tcoverBulgeMF%d", + disk, disk, disk, disk, disk, disk, disk, disk, disk)); - TGeoRotation* rshapeManifold2 = new TGeoRotation(Form("rshapeManifold2MF%d", disk), 0, 180, 0); + TGeoRotation* rshapeManifold2 = + new TGeoRotation(Form("rshapeManifold2MF%d", disk), 0, 180, 0); rshapeManifold2->RegisterYourself(); - TGeoTranslation* tshapeManifold1 = new TGeoTranslation(Form("tshapeManifold1MF%d", disk), 0, 0, - -((thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2 - (thicknessTop[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2)); + TGeoTranslation* tshapeManifold1 = new TGeoTranslation( + Form("tshapeManifold1MF%d", disk), 0, 0, + -((thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / + 2 - + (thicknessTop[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / + 2)); tshapeManifold1->RegisterYourself(); - TGeoTranslation* tshapeManifold2 = new TGeoTranslation(Form("tshapeManifold2MF%d", disk), 0, 0, (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2 - thicknessBody[disk] / 2); + TGeoTranslation* tshapeManifold2 = new TGeoTranslation( + Form("tshapeManifold2MF%d", disk), 0, 0, + (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / + 2 - + thicknessBody[disk] / 2); tshapeManifold2->RegisterYourself(); - TGeoCombiTrans* combtshapeManifold2 = new TGeoCombiTrans(Form("combtshapeManifold2MF%d", disk), 0, 0, (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / 2 - thicknessBody[disk] / 2, rshapeManifold2); + TGeoCombiTrans* combtshapeManifold2 = new TGeoCombiTrans( + Form("combtshapeManifold2MF%d", disk), 0, 0, + (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]) / + 2 - + thicknessBody[disk] / 2, + rshapeManifold2); combtshapeManifold2->RegisterYourself(); - TGeoCompositeShape* shapeManifold = new TGeoCompositeShape("shapeManifold", Form("shapeManifold1MF%d:tshapeManifold1MF%d + shapeManifold2MF%d:combtshapeManifold2MF%d", disk, disk, disk, disk)); + TGeoCompositeShape* shapeManifold = new TGeoCompositeShape( + "shapeManifold", Form("shapeManifold1MF%d:tshapeManifold1MF%d + " + "shapeManifold2MF%d:combtshapeManifold2MF%d", + disk, disk, disk, disk)); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Manifold3 @@ -626,32 +731,38 @@ void HeatExchanger::createManifold(Int_t disk) Double_t outerRadiusPlug1 = 0.6 / 2.; // fm 0.99 / 2; //0.8 Double_t thicknessPlug1 = 0.105; - auto* plug1 = new TGeoTube(Form("plug1MF%d", disk), innerRadiusPlug1, outerRadiusPlug1, thicknessPlug1 / 2); + auto* plug1 = new TGeoTube(Form("plug1MF%d", disk), innerRadiusPlug1, + outerRadiusPlug1, thicknessPlug1 / 2); Double_t innerRadiusPlug2 = innerRadiusPlug1; Double_t outerMinRadiusPlug2 = 0.6 / 2.; // fm 0.94 / 2; // 0.85 Double_t outerMaxRadiusPlug2 = outerRadiusPlug1; Double_t thicknessPlug2 = 0.025; - auto* plug2 = new TGeoCone(Form("plug2MF%d", disk), thicknessPlug2 / 2, innerRadiusPlug2, outerMaxRadiusPlug2, innerRadiusPlug2, outerMinRadiusPlug2); + auto* plug2 = new TGeoCone(Form("plug2MF%d", disk), thicknessPlug2 / 2, + innerRadiusPlug2, outerMaxRadiusPlug2, + innerRadiusPlug2, outerMinRadiusPlug2); Double_t innerRadiusPlug3 = innerRadiusPlug1; Double_t outerRadiusPlug3 = 0.5 / 2; // fm 0.720 / 2; Double_t thicknessPlug3 = 0.086; - auto* plug3 = new TGeoTube(Form("plug3MF%d", disk), innerRadiusPlug3, outerRadiusPlug3, thicknessPlug3 / 2); + auto* plug3 = new TGeoTube(Form("plug3MF%d", disk), innerRadiusPlug3, + outerRadiusPlug3, thicknessPlug3 / 2); Double_t innerRadiusPlug4 = innerRadiusPlug1; Double_t outerRadiusPlug4 = 0.7 / 2.; // fm 1.100 / 2; // 0.7 Double_t thicknessPlug4 = 0.1; // fm 0.534; // 0.05 - auto* plug4 = new TGeoTube(Form("plug4MF%d", disk), innerRadiusPlug4, outerRadiusPlug4, thicknessPlug4 / 2); + auto* plug4 = new TGeoTube(Form("plug4MF%d", disk), innerRadiusPlug4, + outerRadiusPlug4, thicknessPlug4 / 2); Double_t innerRadiusPlug5 = innerRadiusPlug1; Double_t outerRadiusPlug5 = 0.9 / 2.; // fm 1.270 / 2; // 0.9 Double_t thicknessPlug5 = 0.700; - auto* plug5main = new TGeoTube(Form("plug5mainMF%d", disk), innerRadiusPlug5, outerRadiusPlug5, thicknessPlug5 / 2); + auto* plug5main = new TGeoTube(Form("plug5mainMF%d", disk), innerRadiusPlug5, + outerRadiusPlug5, thicknessPlug5 / 2); const Int_t nSidePlug5 = 6; Double_t anglesubPlug5 = 360. / nSidePlug5 / 2. * TMath::Pi() / 180.; @@ -659,9 +770,14 @@ void HeatExchanger::createManifold(Int_t disk) Double_t widthPlug5sub = outerRadiusPlug5 * (1 - TMath::Cos(anglesubPlug5)); Double_t thicknessPlug5sub = thicknessPlug5 + Geometry::sEpsilon; - auto* plug5sub = new TGeoBBox(Form("plug5subMF%d", disk), lengthPlug5sub / 2, widthPlug5sub / 2, thicknessPlug5sub / 2); + auto* plug5sub = new TGeoBBox(Form("plug5subMF%d", disk), lengthPlug5sub / 2, + widthPlug5sub / 2, thicknessPlug5sub / 2); - TGeoTranslation* tPlug5sub = new TGeoTranslation(Form("tPlug5subMF%d", disk), 0, outerRadiusPlug5 * TMath::Cos(anglesubPlug5) + outerRadiusPlug5 * (1 - TMath::Cos(anglesubPlug5)) / 2, 0.); + TGeoTranslation* tPlug5sub = new TGeoTranslation( + Form("tPlug5subMF%d", disk), 0, + outerRadiusPlug5 * TMath::Cos(anglesubPlug5) + + outerRadiusPlug5 * (1 - TMath::Cos(anglesubPlug5)) / 2, + 0.); tPlug5sub->RegisterYourself(); TGeoRotation* rPlug5sub[nSidePlug5]; @@ -669,69 +785,94 @@ void HeatExchanger::createManifold(Int_t disk) TString namePlug5 = Form("plug5mainMF%d", disk); for (Int_t index = 0; index < nSidePlug5; ++index) { - rPlug5sub[index] = new TGeoRotation(Form("rPlug5sub%dMF%d", index, disk), index * 60, 0, 0); + rPlug5sub[index] = new TGeoRotation(Form("rPlug5sub%dMF%d", index, disk), + index * 60, 0, 0); rPlug5sub[index]->RegisterYourself(); - TGeoCombiTrans* combtPlug5sub = new TGeoCombiTrans(Form("combtPlug5subMF%d", disk), 0, outerRadiusPlug5 * TMath::Cos(anglesubPlug5) + outerRadiusPlug5 * (1 - TMath::Cos(anglesubPlug5)) / 2, 0., rPlug5sub[index]); + TGeoCombiTrans* combtPlug5sub = new TGeoCombiTrans( + Form("combtPlug5subMF%d", disk), 0, + outerRadiusPlug5 * TMath::Cos(anglesubPlug5) + + outerRadiusPlug5 * (1 - TMath::Cos(anglesubPlug5)) / 2, + 0., rPlug5sub[index]); combtPlug5sub->RegisterYourself(); namePlug5 += Form(" - plug5subMF%d:combtPlug5subMF%d", disk, disk); } - TGeoCompositeShape* plug5 = new TGeoCompositeShape(Form("plug5MF%d", disk), namePlug5); + TGeoCompositeShape* plug5 = + new TGeoCompositeShape(Form("plug5MF%d", disk), namePlug5); Double_t innerRadiusPlug6 = 0; Double_t outerRadiusPlug6 = 0.780 / 2; Double_t thicknessPlug6 = 0.150; - auto* plug6 = new TGeoTube(Form("plug6MF%d", disk), innerRadiusPlug6, outerRadiusPlug6, thicknessPlug6 / 2); + auto* plug6 = new TGeoTube(Form("plug6MF%d", disk), innerRadiusPlug6, + outerRadiusPlug6, thicknessPlug6 / 2); Double_t innerRadiusPlug7 = innerRadiusPlug6; Double_t outerMinRadiusPlug7 = 0.520 / 2; Double_t outerMaxRadiusPlug7 = 0.638 / 2; Double_t thicknessPlug7 = 0.050; - auto* plug7 = new TGeoCone(Form("plug7MF%d", disk), thicknessPlug7 / 2, innerRadiusPlug7, outerMaxRadiusPlug7, innerRadiusPlug7, outerMinRadiusPlug7); + auto* plug7 = new TGeoCone(Form("plug7MF%d", disk), thicknessPlug7 / 2, + innerRadiusPlug7, outerMaxRadiusPlug7, + innerRadiusPlug7, outerMinRadiusPlug7); Double_t innerRadiusPlug8 = innerRadiusPlug6; Double_t outerRadiusPlug8 = 0.413 / 2; Double_t thicknessPlug8 = 0.042; - auto* plug8 = new TGeoTube(Form("plug8MF%d", disk), innerRadiusPlug8, outerRadiusPlug8, thicknessPlug8 / 2); + auto* plug8 = new TGeoTube(Form("plug8MF%d", disk), innerRadiusPlug8, + outerRadiusPlug8, thicknessPlug8 / 2); Double_t innerRadiusPlug9 = innerRadiusPlug6; Double_t outerMinRadiusPlug9 = outerRadiusPlug8; Double_t outerMaxRadiusPlug9 = 0.500 / 2; Double_t thicknessPlug9 = 0.040; - auto* plug9 = new TGeoCone(Form("plug9MF%d", disk), thicknessPlug9 / 2, innerRadiusPlug9, outerMinRadiusPlug9, innerRadiusPlug9, outerMaxRadiusPlug9); + auto* plug9 = new TGeoCone(Form("plug9MF%d", disk), thicknessPlug9 / 2, + innerRadiusPlug9, outerMinRadiusPlug9, + innerRadiusPlug9, outerMaxRadiusPlug9); Double_t innerRadiusPlug10 = innerRadiusPlug6; Double_t outerRadiusPlug10 = outerMaxRadiusPlug9; Double_t thicknessPlug10 = 0.125; - auto* plug10 = new TGeoTube(Form("plug10MF%d", disk), innerRadiusPlug10, outerRadiusPlug10, thicknessPlug10 / 2); + auto* plug10 = new TGeoTube(Form("plug10MF%d", disk), innerRadiusPlug10, + outerRadiusPlug10, thicknessPlug10 / 2); Double_t innerRadiusPlug11 = innerRadiusPlug6; Double_t outerMinRadiusPlug11 = outerRadiusPlug8; Double_t outerMaxRadiusPlug11 = 0.500 / 2; Double_t thicknessPlug11 = 0.043; - auto* plug11 = new TGeoCone(Form("plug11MF%d", disk), thicknessPlug11 / 2, innerRadiusPlug11, outerMaxRadiusPlug11, innerRadiusPlug11, outerMinRadiusPlug11); + auto* plug11 = new TGeoCone(Form("plug11MF%d", disk), thicknessPlug11 / 2, + innerRadiusPlug11, outerMaxRadiusPlug11, + innerRadiusPlug11, outerMinRadiusPlug11); Double_t innerRadiusPlug12 = 0; Double_t outerRadiusPlug12 = 0.289 / 2; - Double_t thicknessPlug12 = thicknessPlug6 + thicknessPlug7 + thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11; + Double_t thicknessPlug12 = thicknessPlug6 + thicknessPlug7 + thicknessPlug8 + + thicknessPlug9 + thicknessPlug10 + thicknessPlug11; - auto* plug12main = new TGeoTube(Form("plug12mainMF%d", disk), innerRadiusPlug12, outerRadiusPlug12, thicknessPlug12 / 2 + Geometry::sEpsilon); + auto* plug12main = + new TGeoTube(Form("plug12mainMF%d", disk), innerRadiusPlug12, + outerRadiusPlug12, thicknessPlug12 / 2 + Geometry::sEpsilon); const Int_t nSidePlug12 = 6; Double_t anglesubPlug12 = 360. / nSidePlug12 / 2. * TMath::Pi() / 180.; Double_t lengthPlug12sub = outerRadiusPlug12 * TMath::Cos(anglesubPlug12) * 2; - Double_t widthPlug12sub = outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)); + Double_t widthPlug12sub = + outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)); Double_t thicknessPlug12sub = thicknessPlug12; - auto* plug12sub = new TGeoBBox(Form("plug12subMF%d", disk), lengthPlug12sub / 2, widthPlug12sub / 2, thicknessPlug12sub / 2 + Geometry::sEpsilon); + auto* plug12sub = new TGeoBBox(Form("plug12subMF%d", disk), + lengthPlug12sub / 2, widthPlug12sub / 2, + thicknessPlug12sub / 2 + Geometry::sEpsilon); - TGeoTranslation* tPlug12sub = new TGeoTranslation(Form("tPlug12subMF%d", disk), 0, outerRadiusPlug12 * TMath::Cos(anglesubPlug12) + outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)) / 2, 0.); + TGeoTranslation* tPlug12sub = new TGeoTranslation( + Form("tPlug12subMF%d", disk), 0, + outerRadiusPlug12 * TMath::Cos(anglesubPlug12) + + outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)) / 2, + 0.); tPlug12sub->RegisterYourself(); TGeoRotation* rPlug12sub[nSidePlug12]; @@ -739,30 +880,77 @@ void HeatExchanger::createManifold(Int_t disk) TString namePlug12 = Form("plug12mainMF%d", disk); for (Int_t index = 0; index < nSidePlug12; ++index) { - rPlug12sub[index] = new TGeoRotation(Form("rPlug12sub%dMF%d", index, disk), index * 60, 0, 0); + rPlug12sub[index] = new TGeoRotation(Form("rPlug12sub%dMF%d", index, disk), + index * 60, 0, 0); rPlug12sub[index]->RegisterYourself(); - TGeoCombiTrans* combtPlug12sub = new TGeoCombiTrans(Form("combtPlug12subMF%d", disk), 0, outerRadiusPlug12 * TMath::Cos(anglesubPlug12) + outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)) / 2, 0., rPlug12sub[index]); + TGeoCombiTrans* combtPlug12sub = new TGeoCombiTrans( + Form("combtPlug12subMF%d", disk), 0, + outerRadiusPlug12 * TMath::Cos(anglesubPlug12) + + outerRadiusPlug12 * (1 - TMath::Cos(anglesubPlug12)) / 2, + 0., rPlug12sub[index]); combtPlug12sub->RegisterYourself(); namePlug12 += Form(" - plug12subMF%d:combtPlug12subMF%d", disk, disk); } - TGeoCompositeShape* plug12 = new TGeoCompositeShape(Form("plug12MF%d", disk), namePlug12); + TGeoCompositeShape* plug12 = + new TGeoCompositeShape(Form("plug12MF%d", disk), namePlug12); - Double_t refposPlug = (thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11) / 2; + Double_t refposPlug = + (thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + thicknessPlug8 + + thicknessPlug9 + thicknessPlug10 + thicknessPlug11) / + 2; TGeoTranslation* tPlug[12]; - tPlug[0] = new TGeoTranslation(Form("tPlug1MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 / 2); - tPlug[1] = new TGeoTranslation(Form("tPlug2MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 / 2); - tPlug[2] = new TGeoTranslation(Form("tPlug3MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 / 2); - tPlug[3] = new TGeoTranslation(Form("tPlug4MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 / 2); - tPlug[4] = new TGeoTranslation(Form("tPlug5MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 / 2); - tPlug[5] = new TGeoTranslation(Form("tPlug6MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 / 2); - tPlug[6] = new TGeoTranslation(Form("tPlug7MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 / 2); - tPlug[7] = new TGeoTranslation(Form("tPlug8MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + thicknessPlug8 / 2); - tPlug[8] = new TGeoTranslation(Form("tPlug9MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + thicknessPlug8 + thicknessPlug9 / 2); - tPlug[9] = new TGeoTranslation(Form("tPlug10MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + thicknessPlug8 + thicknessPlug9 + thicknessPlug10 / 2); - tPlug[10] = new TGeoTranslation(Form("tPlug11MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11 / 2); - tPlug[11] = new TGeoTranslation(Form("tPlug12MF%d", disk), 0, 0, -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + thicknessPlug12 / 2); + tPlug[0] = new TGeoTranslation(Form("tPlug1MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 / 2); + tPlug[1] = + new TGeoTranslation(Form("tPlug2MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 / 2); + tPlug[2] = new TGeoTranslation(Form("tPlug3MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + + thicknessPlug3 / 2); + tPlug[3] = new TGeoTranslation(Form("tPlug4MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + + thicknessPlug3 + thicknessPlug4 / 2); + tPlug[4] = new TGeoTranslation(Form("tPlug5MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + + thicknessPlug3 + thicknessPlug4 + + thicknessPlug5 / 2); + tPlug[5] = new TGeoTranslation(Form("tPlug6MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + + thicknessPlug3 + thicknessPlug4 + + thicknessPlug5 + thicknessPlug6 / 2); + tPlug[6] = + new TGeoTranslation(Form("tPlug7MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + + thicknessPlug3 + thicknessPlug4 + thicknessPlug5 + + thicknessPlug6 + thicknessPlug7 / 2); + tPlug[7] = new TGeoTranslation(Form("tPlug8MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + + thicknessPlug3 + thicknessPlug4 + + thicknessPlug5 + thicknessPlug6 + + thicknessPlug7 + thicknessPlug8 / 2); + tPlug[8] = new TGeoTranslation( + Form("tPlug9MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + + thicknessPlug8 + thicknessPlug9 / 2); + tPlug[9] = new TGeoTranslation( + Form("tPlug10MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + + thicknessPlug8 + thicknessPlug9 + thicknessPlug10 / 2); + tPlug[10] = new TGeoTranslation( + Form("tPlug11MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + + thicknessPlug4 + thicknessPlug5 + thicknessPlug6 + thicknessPlug7 + + thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + + thicknessPlug11 / 2); + tPlug[11] = new TGeoTranslation( + Form("tPlug12MF%d", disk), 0, 0, + -refposPlug + thicknessPlug1 + thicknessPlug2 + thicknessPlug3 + + thicknessPlug4 + thicknessPlug5 + thicknessPlug12 / 2); TString namePlug = ""; for (Int_t ipart = 0; ipart < 12; ++ipart) { @@ -770,13 +958,16 @@ void HeatExchanger::createManifold(Int_t disk) if (ipart == 0) { namePlug += Form("plug1MF%d:tPlug1MF%d", disk, disk); } else if (ipart == 11) { - namePlug += Form(" - plug%dMF%d:tPlug%dMF%d", ipart + 1, disk, ipart + 1, disk); + namePlug += + Form(" - plug%dMF%d:tPlug%dMF%d", ipart + 1, disk, ipart + 1, disk); } else { - namePlug += Form(" + plug%dMF%d:tPlug%dMF%d", ipart + 1, disk, ipart + 1, disk); + namePlug += + Form(" + plug%dMF%d:tPlug%dMF%d", ipart + 1, disk, ipart + 1, disk); } } - TGeoCompositeShape* shapePlug = new TGeoCompositeShape(Form("shapePlugMF%d", disk), namePlug); + TGeoCompositeShape* shapePlug = + new TGeoCompositeShape(Form("shapePlugMF%d", disk), namePlug); /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Water @@ -789,29 +980,48 @@ void HeatExchanger::createManifold(Int_t disk) watherThickness[3] = thicknessBodyBathtub2[3] - thicknessTop[3]; watherThickness[4] = thicknessBodyBathtub2[4] - thicknessTop[4]; - auto* water1 = new TGeoBBox(Form("water1MF%d", disk), lengthBodyBathtub1 / 2, widthBodyBathtub1 / 2, watherThickness[disk] / 2); - auto* water2 = new TGeoBBox(Form("water2MF%d", disk), lengthBodyBathtub2 / 2, widthBodyBathtub2 / 2, watherThickness[disk] / 2); - auto* water3 = new TGeoTube(Form("water3MF%d", disk), 0, cornerRadiusBodyBathtub1[disk], watherThickness[disk] / 2); + auto* water1 = new TGeoBBox(Form("water1MF%d", disk), lengthBodyBathtub1 / 2, + widthBodyBathtub1 / 2, watherThickness[disk] / 2); + auto* water2 = new TGeoBBox(Form("water2MF%d", disk), lengthBodyBathtub2 / 2, + widthBodyBathtub2 / 2, watherThickness[disk] / 2); + auto* water3 = + new TGeoTube(Form("water3MF%d", disk), 0, cornerRadiusBodyBathtub1[disk], + watherThickness[disk] / 2); TGeoTranslation* twater[7]; twater[0] = new TGeoTranslation(Form("twater1MF%d", disk), 0., 0., 0); - twater[1] = new TGeoTranslation(Form("twater2MF%d", disk), lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2, 0., 0.); - twater[2] = new TGeoTranslation(Form("twater3MF%d", disk), lengthBodyBathtub1 / 2, widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); - twater[3] = new TGeoTranslation(Form("twater4MF%d", disk), lengthBodyBathtub1 / 2, -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); - - twater[4] = new TGeoTranslation(Form("twater5MF%d", disk), -(lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2), 0., 0.); - twater[5] = new TGeoTranslation(Form("twater6MF%d", disk), -(lengthBodyBathtub1 / 2), widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); - twater[6] = new TGeoTranslation(Form("twater7MF%d", disk), -(lengthBodyBathtub1 / 2), -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); + twater[1] = new TGeoTranslation( + Form("twater2MF%d", disk), + lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2, 0., 0.); + twater[2] = new TGeoTranslation( + Form("twater3MF%d", disk), lengthBodyBathtub1 / 2, + widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); + twater[3] = new TGeoTranslation( + Form("twater4MF%d", disk), lengthBodyBathtub1 / 2, + -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); + + twater[4] = new TGeoTranslation( + Form("twater5MF%d", disk), + -(lengthBodyBathtub1 / 2 + lengthBodyBathtub2 / 2), 0., 0.); + twater[5] = new TGeoTranslation( + Form("twater6MF%d", disk), -(lengthBodyBathtub1 / 2), + widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk], 0.); + twater[6] = new TGeoTranslation( + Form("twater7MF%d", disk), -(lengthBodyBathtub1 / 2), + -(widthBodyBathtub1 / 2 - cornerRadiusBodyBathtub1[disk]), 0.); for (Int_t i = 0; i < 7; ++i) { twater[i]->RegisterYourself(); } - TGeoCompositeShape* shapeWater = new TGeoCompositeShape(Form("shapeCoverBathtubMF%d", disk), - Form("water1MF%d + water2MF%d:twater2MF%d + water3MF%d:twater3MF%d + water3MF%d:twater4MF%d + water2MF%d:twater5MF%d +" - "water3MF%d:twater6MF%d + water3MF%d:twater7MF%d", - disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk)); + TGeoCompositeShape* shapeWater = new TGeoCompositeShape( + Form("shapeCoverBathtubMF%d", disk), + Form("water1MF%d + water2MF%d:twater2MF%d + water3MF%d:twater3MF%d + " + "water3MF%d:twater4MF%d + water2MF%d:twater5MF%d +" + "water3MF%d:twater6MF%d + water3MF%d:twater7MF%d", + disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, disk, + disk, disk)); TGeoCombiTrans* transformation1 = nullptr; TGeoCombiTrans* transformation2 = nullptr; @@ -825,59 +1035,94 @@ void HeatExchanger::createManifold(Int_t disk) TGeoMedium* kMed_plug = gGeoManager->GetMedium("MFT_Alu$"); TGeoMedium* kMed_Water = gGeoManager->GetMedium("MFT_Water$"); - Double_t thicknessTotMF = (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]); + Double_t thicknessTotMF = + (thicknessBody[disk] + thicknessMiddle[disk] + thicknessBottom[disk]); - Double_t deltay = 0.2; // shift respect to the median plan of the MFT - Double_t mfX = 2.2; // width - Double_t mfY = 6.8 - 0.1; // height, decrease to avoid overlap with support, to be solved, fm - Double_t mfZ = 1.7 - 0.85; // thickness, decrease to avoid overlap with support, to be solved, fm + Double_t deltay = 0.2; // shift respect to the median plan of the MFT + Double_t mfX = 2.2; // width + Double_t mfY = + 6.8 - + 0.1; // height, decrease to avoid overlap with support, to be solved, fm + Double_t mfZ = 1.7 - 0.85; // thickness, decrease to avoid overlap with + // support, to be solved, fm Double_t fShift = 0; if (disk == 3 || disk == 4) { - fShift = 0.015; // to avoid overlap with the 2 curved water pipes on the 2 upstream chambers + fShift = 0.015; // to avoid overlap with the 2 curved water pipes on the 2 + // upstream chambers } auto* MF01 = new TGeoVolume(Form("MF%d1", disk), shapeManifold, kMedPeek); - auto* MFplug01 = new TGeoVolume(Form("MFplug%d1", disk), shapePlug, kMed_plug); - auto* MFwater01 = new TGeoVolume(Form("MFwater%d1", disk), shapeWater, kMed_Water); - TGeoRotation* rotation1 = new TGeoRotation(Form("rotation1MF%d", disk), 90, 90., 180.); + auto* MFplug01 = + new TGeoVolume(Form("MFplug%d1", disk), shapePlug, kMed_plug); + auto* MFwater01 = + new TGeoVolume(Form("MFwater%d1", disk), shapeWater, kMed_Water); + TGeoRotation* rotation1 = + new TGeoRotation(Form("rotation1MF%d", disk), 90, 90., 180.); transformation1 = - // new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift, mfY / 2 + deltay, mZPlan[disk], rotation1); - new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2, - mHalfDiskGap + mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation1); + // new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift, + // mfY / 2 + deltay, mZPlan[disk], rotation1); + new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + + thicknessTotMF / 2, + mHalfDiskGap + mSupportYDimensions[disk][0] / 2, + mZPlan[disk], rotation1); mHalfDisk->AddNode(MF01, 1, transformation1); - TGeoRotation* rotationplug1 = new TGeoRotation(Form("rotationplug1MF%d", disk), -90, 90., 90.); + TGeoRotation* rotationplug1 = + new TGeoRotation(Form("rotationplug1MF%d", disk), -90, 90., 90.); transformationplug1 = - // new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift + thicknessTotMF/2 + refposPlug - (thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11),mfY / 2 + deltay - ((lengthBody)/2 - holeOffset[3]), mZPlan[disk], rotationplug1); - new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + thicknessTotMF / 2 + refposPlug - (thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11), - mHalfDiskGap + mSupportYDimensions[disk][0] / 2 - ((lengthBody) / 2 - holeOffset[3]), mZPlan[disk], rotationplug1); + // new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + mfZ / 2 + fShift + // + thicknessTotMF/2 + refposPlug - (thicknessPlug8 + thicknessPlug9 + + // thicknessPlug10 + thicknessPlug11),mfY / 2 + deltay - ((lengthBody)/2 - + // holeOffset[3]), mZPlan[disk], rotationplug1); + new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + + thicknessTotMF / 2 + thicknessTotMF / 2 + + refposPlug - + (thicknessPlug8 + thicknessPlug9 + + thicknessPlug10 + thicknessPlug11), + mHalfDiskGap + mSupportYDimensions[disk][0] / 2 - + ((lengthBody) / 2 - holeOffset[3]), + mZPlan[disk], rotationplug1); mHalfDisk->AddNode(MFplug01, 1, transformationplug1); - transformationwater1 = - new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + (thicknessTotMF / 2 - watherThickness[disk] / 2) - (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk]), - mHalfDiskGap + mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation1); + transformationwater1 = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + + (thicknessTotMF / 2 - watherThickness[disk] / 2) - + (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk]), + mHalfDiskGap + mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation1); mHalfDisk->AddNode(MFwater01, 1, transformationwater1); auto* MF02 = new TGeoVolume(Form("MF%d2", disk), shapeManifold, kMedPeek); - auto* MFplug02 = new TGeoVolume(Form("MFplug%d2", disk), shapePlug, kMed_plug); - auto* MFwater02 = new TGeoVolume(Form("MFwater%d2", disk), shapeWater, kMed_Water); - TGeoRotation* rotation2 = new TGeoRotation(Form("rotation2MF%d", disk), 90, 90., 0.); - - transformation2 = - new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2, - -mHalfDiskGap - mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation2); + auto* MFplug02 = + new TGeoVolume(Form("MFplug%d2", disk), shapePlug, kMed_plug); + auto* MFwater02 = + new TGeoVolume(Form("MFwater%d2", disk), shapeWater, kMed_Water); + TGeoRotation* rotation2 = + new TGeoRotation(Form("rotation2MF%d", disk), 90, 90., 0.); + + transformation2 = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2, + -mHalfDiskGap - mSupportYDimensions[disk][0] / 2, mZPlan[disk], + rotation2); mHalfDisk->AddNode(MF02, 1, transformation2); - TGeoRotation* rotationplug2 = new TGeoRotation(Form("rotationplug1MF%d", disk), -90, 90., 90.); - transformationplug2 = - new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + thicknessTotMF / 2 + refposPlug - (thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11), - -mHalfDiskGap - mSupportYDimensions[disk][0] / 2 + ((lengthBody) / 2 - holeOffset[3]), mZPlan[disk], rotationplug2); + TGeoRotation* rotationplug2 = + new TGeoRotation(Form("rotationplug1MF%d", disk), -90, 90., 90.); + transformationplug2 = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + + thicknessTotMF / 2 + refposPlug - + (thicknessPlug8 + thicknessPlug9 + thicknessPlug10 + thicknessPlug11), + -mHalfDiskGap - mSupportYDimensions[disk][0] / 2 + + ((lengthBody) / 2 - holeOffset[3]), + mZPlan[disk], rotationplug2); mHalfDisk->AddNode(MFplug02, 1, transformationplug2); - transformationwater2 = - new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + ((thicknessTotMF / 2 - watherThickness[disk] / 2) - (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk])), - -mHalfDiskGap - mSupportYDimensions[disk][0] / 2, mZPlan[disk], rotation2); + transformationwater2 = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2 + 0.1 + thicknessTotMF / 2 + + ((thicknessTotMF / 2 - watherThickness[disk] / 2) - + (thicknessBody[disk] - thicknessTop[disk] - watherThickness[disk])), + -mHalfDiskGap - mSupportYDimensions[disk][0] / 2, mZPlan[disk], + rotation2); mHalfDisk->AddNode(MFwater02, 1, transformationwater2); } @@ -907,131 +1152,251 @@ void HeatExchanger::createHalfDisk0(Int_t half) TGeoRotation* rotation = nullptr; TGeoCombiTrans* transformation = nullptr; - // **************************************** Water part **************************************** - // ********************** Four parameters mLwater0, mRadius0, mAngle0, mLpartial0 ************* + // **************************************** Water part + // **************************************** + // ********************** Four parameters mLwater0, mRadius0, mAngle0, + // mLpartial0 ************* Double_t ivolume = 0; // offset chamber 0 Double_t mRadiusCentralTore[4]; Double_t xPos0[4]; Double_t yPos0[4]; for (Int_t itube = 0; itube < 3; itube++) { - TGeoVolume* waterTube1 = gGeoManager->MakeTube(Form("waterTube1%d_D0_H%d", itube, half), mWater, 0., mRWater, mLWater0[itube] / 2.); - translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube] / 2.); + TGeoVolume* waterTube1 = + gGeoManager->MakeTube(Form("waterTube1%d_D0_H%d", itube, half), mWater, + 0., mRWater, mLWater0[itube] / 2.); + translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., + mSupportXDimensions[0][0] / 2. + + mMoreLength01 - mLWater0[itube] / 2.); cooling->AddNode(waterTube1, ivolume++, translation); - TGeoVolume* waterTorus1 = gGeoManager->MakeTorus(Form("waterTorus1%d_D0_H%d", itube, half), mWater, mRadius0[itube], 0., mRWater, 0., mAngle0[itube]); + TGeoVolume* waterTorus1 = gGeoManager->MakeTorus( + Form("waterTorus1%d_D0_H%d", itube, half), mWater, mRadius0[itube], 0., + mRWater, 0., mAngle0[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., + mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube], + rotation); cooling->AddNode(waterTorus1, ivolume++, transformation); - TGeoVolume* waterTube2 = gGeoManager->MakeTube(Form("waterTube2%d_D0_H%d", itube, half), mWater, 0., mRWater, mLpartial0[itube] / 2.); + TGeoVolume* waterTube2 = + gGeoManager->MakeTube(Form("waterTube2%d_D0_H%d", itube, half), mWater, + 0., mRWater, mLpartial0[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle0[itube], 0.); - xPos0[itube] = mLWater0[itube] + mRadius0[itube] * TMath::Sin(mAngle0[itube] * TMath::DegToRad()) + mLpartial0[itube] / 2 * TMath::Cos(mAngle0[itube] * TMath::DegToRad()); - yPos0[itube] = mXPosition0[itube] - mHalfDiskGap + mRadius0[itube] * (1 - TMath::Cos(mAngle0[itube] * TMath::DegToRad())) + mLpartial0[itube] / 2 * TMath::Sin(mAngle0[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos0[itube], 0., mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube], rotation); + xPos0[itube] = + mLWater0[itube] + + mRadius0[itube] * TMath::Sin(mAngle0[itube] * TMath::DegToRad()) + + mLpartial0[itube] / 2 * TMath::Cos(mAngle0[itube] * TMath::DegToRad()); + yPos0[itube] = + mXPosition0[itube] - mHalfDiskGap + + mRadius0[itube] * (1 - TMath::Cos(mAngle0[itube] * TMath::DegToRad())) + + mLpartial0[itube] / 2 * TMath::Sin(mAngle0[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans(yPos0[itube], 0., + mSupportXDimensions[0][0] / 2. + + mMoreLength01 - xPos0[itube], + rotation); cooling->AddNode(waterTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube] - mLpartial0[itube] / 2 * TMath::Cos(mAngle0[itube] * TMath::DegToRad())) / TMath::Sin(mAngle0[itube] * TMath::DegToRad()); - TGeoVolume* waterTorusCentral = gGeoManager->MakeTorus(Form("waterTorusCentral%d_D0_H%d", itube, half), mWater, mRadiusCentralTore[itube], 0., mRWater, -mAngle0[itube], 2. * mAngle0[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube] - + mLpartial0[itube] / 2 * + TMath::Cos(mAngle0[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle0[itube] * TMath::DegToRad()); + TGeoVolume* waterTorusCentral = + gGeoManager->MakeTorus(Form("waterTorusCentral%d_D0_H%d", itube, half), + mWater, mRadiusCentralTore[itube], 0., mRWater, + -mAngle0[itube], 2. * mAngle0[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos0[itube] + mLpartial0[itube] / 2 * TMath::Sin(mAngle0[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle0[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos0[itube] + + mLpartial0[itube] / 2 * + TMath::Sin(mAngle0[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle0[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(waterTorusCentral, ivolume++, transformation); - TGeoVolume* waterTube3 = gGeoManager->MakeTube(Form("waterTube3%d_D0_H%d", 2, half), mWater, 0., mRWater, mLpartial0[itube] / 2.); + TGeoVolume* waterTube3 = + gGeoManager->MakeTube(Form("waterTube3%d_D0_H%d", 2, half), mWater, 0., + mRWater, mLpartial0[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle0[itube], 0.); - transformation = new TGeoCombiTrans(yPos0[itube], 0., -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos0[itube], 0., + -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube]), + rotation); cooling->AddNode(waterTube3, ivolume++, transformation); - TGeoVolume* waterTorus2 = gGeoManager->MakeTorus(Form("waterTorus2%d_D0_H%d", itube, half), mWater, mRadius0[itube], 0., mRWater, 0., mAngle0[itube]); + TGeoVolume* waterTorus2 = gGeoManager->MakeTorus( + Form("waterTorus2%d_D0_H%d", itube, half), mWater, mRadius0[itube], 0., + mRWater, 0., mAngle0[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube]), + rotation); cooling->AddNode(waterTorus2, ivolume++, transformation); - TGeoVolume* waterTube4 = gGeoManager->MakeTube(Form("waterTube4%d_D0_H%d", itube, half), mWater, 0., mRWater, mLWater0[itube] / 2.); - translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube] / 2.)); + TGeoVolume* waterTube4 = + gGeoManager->MakeTube(Form("waterTube4%d_D0_H%d", itube, half), mWater, + 0., mRWater, mLWater0[itube] / 2.); + translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[0][0] / 2. + + mMoreLength01 - mLWater0[itube] / 2.)); cooling->AddNode(waterTube4, ivolume++, translation); } - // **************************************************** Tube part ************************************************ - // ****************************** Four parameters mLwater0, mRadius0, mAngle0, mLpartial0 ************************ + // **************************************************** Tube part + // ************************************************ + // ****************************** Four parameters mLwater0, mRadius0, mAngle0, + // mLpartial0 ************************ for (Int_t itube = 0; itube < 3; itube++) { - TGeoVolume* pipeTube1 = gGeoManager->MakeTube(Form("pipeTube1%d_D0_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater0[itube] / 2.); - translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube] / 2.); + TGeoVolume* pipeTube1 = + gGeoManager->MakeTube(Form("pipeTube1%d_D0_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater0[itube] / 2.); + translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., + mSupportXDimensions[0][0] / 2. + + mMoreLength01 - mLWater0[itube] / 2.); cooling->AddNode(pipeTube1, ivolume++, translation); - TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus(Form("pipeTorus1%d_D0_H%d", itube, half), mPipe, mRadius0[itube], mRWater, mRWater + mDRPipe, 0., mAngle0[itube]); + TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus( + Form("pipeTorus1%d_D0_H%d", itube, half), mPipe, mRadius0[itube], + mRWater, mRWater + mDRPipe, 0., mAngle0[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., + mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube], + rotation); cooling->AddNode(pipeTorus1, ivolume++, transformation); - TGeoVolume* pipeTube2 = gGeoManager->MakeTube(Form("pipeTube2%d_D0_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial0[itube] / 2.); + TGeoVolume* pipeTube2 = gGeoManager->MakeTube( + Form("pipeTube2%d_D0_H%d", itube, half), mPipe, mRWater, + mRWater + mDRPipe, mLpartial0[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle0[itube], 0.); - xPos0[itube] = mLWater0[itube] + mRadius0[itube] * TMath::Sin(mAngle0[itube] * TMath::DegToRad()) + mLpartial0[itube] / 2 * TMath::Cos(mAngle0[itube] * TMath::DegToRad()); - yPos0[itube] = mXPosition0[itube] - mHalfDiskGap + mRadius0[itube] * (1 - TMath::Cos(mAngle0[itube] * TMath::DegToRad())) + mLpartial0[itube] / 2 * TMath::Sin(mAngle0[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos0[itube], 0., mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube], rotation); + xPos0[itube] = + mLWater0[itube] + + mRadius0[itube] * TMath::Sin(mAngle0[itube] * TMath::DegToRad()) + + mLpartial0[itube] / 2 * TMath::Cos(mAngle0[itube] * TMath::DegToRad()); + yPos0[itube] = + mXPosition0[itube] - mHalfDiskGap + + mRadius0[itube] * (1 - TMath::Cos(mAngle0[itube] * TMath::DegToRad())) + + mLpartial0[itube] / 2 * TMath::Sin(mAngle0[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans(yPos0[itube], 0., + mSupportXDimensions[0][0] / 2. + + mMoreLength01 - xPos0[itube], + rotation); cooling->AddNode(pipeTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube] - mLpartial0[itube] / 2 * TMath::Cos(mAngle0[itube] * TMath::DegToRad())) / TMath::Sin(mAngle0[itube] * TMath::DegToRad()); - TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus(Form("pipeTorusCentral%d_D0_H%d", itube, half), mPipe, mRadiusCentralTore[itube], mRWater, - mRWater + mDRPipe, -mAngle0[itube], 2. * mAngle0[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube] - + mLpartial0[itube] / 2 * + TMath::Cos(mAngle0[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle0[itube] * TMath::DegToRad()); + TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus( + Form("pipeTorusCentral%d_D0_H%d", itube, half), mPipe, + mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, -mAngle0[itube], + 2. * mAngle0[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos0[itube] + mLpartial0[itube] / 2 * TMath::Sin(mAngle0[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle0[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos0[itube] + + mLpartial0[itube] / 2 * + TMath::Sin(mAngle0[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle0[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(pipeTorusCentral, ivolume++, transformation); - TGeoVolume* pipeTube3 = gGeoManager->MakeTube(Form("pipeTube3%d_D0_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial0[itube] / 2.); + TGeoVolume* pipeTube3 = gGeoManager->MakeTube( + Form("pipeTube3%d_D0_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, + mLpartial0[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle0[itube], 0.); - transformation = new TGeoCombiTrans(yPos0[itube], 0., -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos0[itube], 0., + -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[itube]), + rotation); cooling->AddNode(pipeTube3, ivolume++, transformation); - TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus(Form("pipeTorus2%d_D0_H%d", itube, half), mPipe, mRadius0[itube], mRWater, mRWater + mDRPipe, 0., mAngle0[itube]); + TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus( + Form("pipeTorus2%d_D0_H%d", itube, half), mPipe, mRadius0[itube], + mRWater, mRWater + mDRPipe, 0., mAngle0[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius0[itube] + mXPosition0[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube]), + rotation); cooling->AddNode(pipeTorus2, ivolume++, transformation); - TGeoVolume* pipeTube4 = gGeoManager->MakeTube(Form("pipeTube4%d_D0_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater0[itube] / 2.); - translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[itube] / 2.)); + TGeoVolume* pipeTube4 = + gGeoManager->MakeTube(Form("pipeTube4%d_D0_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater0[itube] / 2.); + translation = new TGeoTranslation(mXPosition0[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[0][0] / 2. + + mMoreLength01 - mLWater0[itube] / 2.)); cooling->AddNode(pipeTube4, ivolume++, translation); } // *********************************************************************************************** - Double_t deltaz = mHeatExchangerThickness - Geometry::sKaptonOnCarbonThickness * 4 - Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; + Double_t deltaz = mHeatExchangerThickness - + Geometry::sKaptonOnCarbonThickness * 4 - + Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; rotation = new TGeoRotation("rotation", -90., 90., 0.); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 3, transformation); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 4, transformation); - // **************************************** Carbon Plates **************************************** + // **************************************** Carbon Plates + // **************************************** auto* carbonPlate = new TGeoVolumeAssembly(Form("carbonPlate_D0_H%d", half)); - auto* carbonBase0 = new TGeoBBox(Form("carbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0] / 2. + mMoreLength01), - (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); - auto* t01 = new TGeoTranslation("t01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + + auto& mftBaseParam = MFTBaseParam::Instance(); + Double_t mReducedX = 0.; + if (mftBaseParam.buildAlignment) { + mReducedX = 0.6; + } + + auto* carbonBase0 = new TGeoBBox( + Form("carbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mReducedX), + (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); + auto* t01 = new TGeoTranslation( + "t01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); t01->RegisterYourself(); auto* holeCarbon0 = - new TGeoTubeSeg(Form("holeCarbon0_D0_H%d", half), 0., mRMin[disk], mCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeCarbon0_D0_H%d", half), 0., mRMin[disk], + mCarbonThickness + 0.000001, 0, 180.); auto* t02 = new TGeoTranslation("t02", 0., -mHalfDiskGap, 0.); t02->RegisterYourself(); auto* carbonhole0 = new TGeoSubtraction(carbonBase0, holeCarbon0, t01, t02); auto* ch0 = new TGeoCompositeShape(Form("Carbon0_D0_H%d", half), carbonhole0); - auto* carbonBaseWithHole0 = new TGeoVolume(Form("carbonBaseWithHole_D0_H%d", half), ch0, mCarbon); + auto* carbonBaseWithHole0 = + new TGeoVolume(Form("carbonBaseWithHole_D0_H%d", half), ch0, mCarbon); carbonBaseWithHole0->SetLineColor(kGray + 3); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - carbonPlate->AddNode(carbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + carbonPlate->AddNode(carbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t ty = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { ty += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partCarbon = - gGeoManager->MakeBox(Form("partCarbon_D0_H%d_%d", half, ipart), mCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); + TGeoVolume* partCarbon = gGeoManager->MakeBox( + Form("partCarbon_D0_H%d_%d", half, ipart), mCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); partCarbon->SetLineColor(kGray + 3); auto* t = new TGeoTranslation("t", 0, ty + mHalfDiskGap, mZPlan[disk]); carbonPlate->AddNode(partCarbon, ipart, t); @@ -1044,36 +1409,53 @@ void HeatExchanger::createHalfDisk0(Int_t half) transformation = new TGeoCombiTrans(0., 0., -deltaz / 2., rotation); mHalfDisk->AddNode(carbonPlate, 4, transformation); - // **************************************** Glue Bwtween Carbon Plate and Rohacell Plate **************************************** + // **************************************** Glue Bwtween Carbon Plate and + // Rohacell Plate **************************************** TGeoMedium* mGlueRohacellCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueRohacellCarbon = new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); - auto* glueRohacellCarbonBase0 = new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sGlueRohacellCarbonThickness); - - auto* translation_gluRC01 = new TGeoTranslation("translation_gluRC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueRohacellCarbon = + new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); + auto* glueRohacellCarbonBase0 = + new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., + Geometry::sGlueRohacellCarbonThickness); + + auto* translation_gluRC01 = new TGeoTranslation( + "translation_gluRC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluRC01->RegisterYourself(); - auto* translation_gluRC02 = new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluRC02 = + new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); translation_gluRC02->RegisterYourself(); - auto* holeglueRohacellCarbon0 = - new TGeoTubeSeg(Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); + auto* holeglueRohacellCarbon0 = new TGeoTubeSeg( + Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); - auto* glueRohacellCarbonhole0 = new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, translation_gluRC01, translation_gluRC02); - auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), glueRohacellCarbonhole0); - auto* glueRohacellCarbonBaseWithHole0 = new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, mGlueRohacellCarbon); + auto* glueRohacellCarbonhole0 = + new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, + translation_gluRC01, translation_gluRC02); + auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), + glueRohacellCarbonhole0); + auto* glueRohacellCarbonBaseWithHole0 = + new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, + mGlueRohacellCarbon); glueRohacellCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGRC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGRC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueRohacellCarbon = - gGeoManager->MakeBox(Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sGlueRohacellCarbonThickness); + TGeoVolume* partGlueRohacellCarbon = gGeoManager->MakeBox( + Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), + mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sGlueRohacellCarbonThickness); partGlueRohacellCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGRC + mHalfDiskGap, mZPlan[disk]); glueRohacellCarbon->AddNode(partGlueRohacellCarbon, ipart, t); @@ -1081,41 +1463,62 @@ void HeatExchanger::createHalfDisk0(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness), rotation); + transformation = new TGeoCombiTrans(0., 0., + -(deltaz / 2. - mCarbonThickness - + Geometry::sGlueRohacellCarbonThickness), + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 4, transformation); - // **************************************** Kapton on Carbon Plate **************************************** + // **************************************** Kapton on Carbon Plate + // **************************************** TGeoMedium* mKaptonOnCarbon = gGeoManager->GetMedium("MFT_Kapton$"); - auto* kaptonOnCarbon = new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); - auto* kaptonOnCarbonBase0 = new TGeoBBox(Form("kaptonOnCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0] / 2. + mMoreLength01), - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); - - auto* translation_KC01 = new TGeoTranslation("translation_KC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* kaptonOnCarbon = + new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); + auto* kaptonOnCarbonBase0 = new TGeoBBox( + Form("kaptonOnCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mReducedX), + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); + + auto* translation_KC01 = new TGeoTranslation( + "translation_KC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_KC01->RegisterYourself(); - auto* translation_KC02 = new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); + auto* translation_KC02 = + new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); translation_KC02->RegisterYourself(); auto* holekaptonOnCarbon0 = - new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); - auto* kaptonOnCarbonhole0 = new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, translation_KC01, translation_KC02); - auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), kaptonOnCarbonhole0); - auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume(Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); + auto* kaptonOnCarbonhole0 = + new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, + translation_KC01, translation_KC02); + auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), + kaptonOnCarbonhole0); + auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume( + Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); kaptonOnCarbonBaseWithHole0->SetLineColor(kMagenta); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partkaptonOnCarbonBase = - gGeoManager->MakeBox(Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonOnCarbonThickness); + TGeoVolume* partkaptonOnCarbonBase = gGeoManager->MakeBox( + Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sKaptonOnCarbonThickness); partkaptonOnCarbonBase->SetLineColor(kMagenta); auto* t = new TGeoTranslation("t", 0, tyKC + mHalfDiskGap, mZPlan[disk]); kaptonOnCarbon->AddNode(partkaptonOnCarbonBase, ipart, t); @@ -1123,41 +1526,63 @@ void HeatExchanger::createHalfDisk0(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2, + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2), + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 4, transformation); - // **************************************** Kapton glue on the carbon plate **************************************** + // **************************************** Kapton glue on the carbon plate + // **************************************** TGeoMedium* mGlueKaptonCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueKaptonCarbon = new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); - auto* glueKaptonCarbonBase0 = new TGeoBBox(Form("glueKaptonCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0] / 2. + mMoreLength01), - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); - - auto* translation_gluKC01 = new TGeoTranslation("translation_gluKC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueKaptonCarbon = + new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); + auto* glueKaptonCarbonBase0 = new TGeoBBox( + Form("glueKaptonCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mReducedX), + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); + + auto* translation_gluKC01 = new TGeoTranslation( + "translation_gluKC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluKC01->RegisterYourself(); - auto* translation_gluKC02 = new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluKC02 = + new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); translation_gluKC02->RegisterYourself(); - auto* holeglueKaptonCarbon0 = - new TGeoTubeSeg(Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); + auto* holeglueKaptonCarbon0 = new TGeoTubeSeg( + Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); - auto* glueKaptonCarbonhole0 = new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, translation_gluKC01, translation_gluKC02); - auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), glueKaptonCarbonhole0); - auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume(Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); + auto* glueKaptonCarbonhole0 = + new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, + translation_gluKC01, translation_gluKC02); + auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), + glueKaptonCarbonhole0); + auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume( + Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); glueKaptonCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueKaptonCarbon = - gGeoManager->MakeBox(Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); + TGeoVolume* partGlueKaptonCarbon = gGeoManager->MakeBox( + Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); partGlueKaptonCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGKC + mHalfDiskGap, mZPlan[disk]); glueKaptonCarbon->AddNode(partGlueKaptonCarbon, ipart, t); @@ -1165,19 +1590,30 @@ void HeatExchanger::createHalfDisk0(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 4, transformation); - // **************************************** Rohacell Plate **************************************** - auto* rohacellPlate = new TGeoVolumeAssembly(Form("rohacellPlate_D0_H%d", half)); - auto* rohacellBase0 = new TGeoBBox(Form("rohacellBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., (mSupportYDimensions[disk][0]) / 2., - mRohacellThickness); - auto* holeRohacell0 = new TGeoTubeSeg(Form("holeRohacell0_D0_H%d", half), 0., mRMin[disk], mRohacellThickness + 0.000001, 0, 180.); - - // **************************************** GROOVES ************************************************* - // Creating grooves or not according to sGrooves + // **************************************** Rohacell Plate + // **************************************** + auto* rohacellPlate = + new TGeoVolumeAssembly(Form("rohacellPlate_D0_H%d", half)); + auto* rohacellBase0 = new TGeoBBox( + Form("rohacellBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., + (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); + auto* holeRohacell0 = + new TGeoTubeSeg(Form("holeRohacell0_D0_H%d", half), 0., mRMin[disk], + mRohacellThickness + 0.000001, 0, 180.); + + // **************************************** GROOVES + // ************************************************* Creating grooves or not + // according to sGrooves Double_t diameter = 0.21; // groove diameter Double_t epsilon = 0.06; // outside shift of the goove Int_t iCount = 0; @@ -1189,20 +1625,31 @@ void HeatExchanger::createHalfDisk0(Int_t half) TGeoCompositeShape* rohacellGroove[300]; for (Int_t igroove = 0; igroove < 3; igroove++) { - grooveTube[0][igroove] = new TGeoTube("linear", 0., diameter, mLWater0[igroove] / 2.); - grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius0[igroove], 0., diameter, 0., mAngle0[igroove]); - grooveTube[2][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial0[igroove] / 2.); - grooveTorus[3][igroove] = new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, -mAngle0[igroove], 2. * mAngle0[igroove]); - grooveTube[4][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial0[igroove] / 2.); - grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius0[igroove], 0., diameter, 0., mAngle0[igroove]); - grooveTube[6][igroove] = new TGeoTube("linear", 0., diameter, mLWater0[igroove] / 2.); + grooveTube[0][igroove] = + new TGeoTube("linear", 0., diameter, mLWater0[igroove] / 2.); + grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius0[igroove], 0., + diameter, 0., mAngle0[igroove]); + grooveTube[2][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial0[igroove] / 2.); + grooveTorus[3][igroove] = + new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, + -mAngle0[igroove], 2. * mAngle0[igroove]); + grooveTube[4][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial0[igroove] / 2.); + grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius0[igroove], 0., + diameter, 0., mAngle0[igroove]); + grooveTube[6][igroove] = + new TGeoTube("linear", 0., diameter, mLWater0[igroove] / 2.); } // Rotation matrix TGeoRotation* rotationLinear = new TGeoRotation("rotation", -90., 90., 0.); - TGeoRotation* rotationSideTorusL = new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); - TGeoRotation* rotationSideTorusR = new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); - TGeoRotation* rotationCentralTorus = new TGeoRotation("rotationCentralTorus", 90., 0., 0.); + TGeoRotation* rotationSideTorusL = + new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); + TGeoRotation* rotationSideTorusR = + new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); + TGeoRotation* rotationCentralTorus = + new TGeoRotation("rotationCentralTorus", 90., 0., 0.); TGeoRotation* rotationTiltedLinearR; TGeoRotation* rotationTiltedLinearL; @@ -1210,51 +1657,96 @@ void HeatExchanger::createHalfDisk0(Int_t half) if (Geometry::sGrooves == 1) { for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 3; igroove++) { // 3 grooves - mPosition[igroove] = mXPosition0[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; + mPosition[igroove] = mXPosition0[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater0[igroove] / 2., mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater0[igroove] / 2., + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[0] = new TGeoSubtraction(rohacellBase0, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[0] = new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[0]); + rohacellBaseGroove[0] = + new TGeoSubtraction(rohacellBase0, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[0] = + new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[0]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater0[igroove], mRadius0[igroove] + mXPosition0[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater0[igroove], + mRadius0[igroove] + mXPosition0[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle0[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - xPos0[igroove], yPos0[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle0[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + xPos0[igroove], + yPos0[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., yPos0[igroove] + mLpartial0[igroove] / 2 * TMath::Sin(mAngle0[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle0[igroove] * TMath::DegToRad()) - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + yPos0[igroove] + + mLpartial0[igroove] / 2 * + TMath::Sin(mAngle0[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle0[igroove] * TMath::DegToRad()) - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle0[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - xPos0[igroove]), yPos0[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle0[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + xPos0[igroove]), + yPos0[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater0[igroove]), mRadius0[igroove] + mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater0[igroove]), + mRadius0[igroove] + mPosition[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater0[igroove] / 2.), mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater0[igroove] / 2.), + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, + transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, + transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -1270,88 +1762,150 @@ void HeatExchanger::createHalfDisk0(Int_t half) rohacellBase = new TGeoSubtraction(rohacellBase0, holeRohacell0, t01, t02); } if (Geometry::sGrooves == 1) { - rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell0, t01, t02); + rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], + holeRohacell0, t01, t02); } - auto* rh0 = new TGeoCompositeShape(Form("rohacellTore%d_D0_H%d", 0, half), rohacellBase); - auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D0_H%d", half), rh0, mRohacell); + auto* rh0 = new TGeoCompositeShape(Form("rohacellTore%d_D0_H%d", 0, half), + rohacellBase); + auto* rohacellBaseWithHole = + new TGeoVolume(Form("rohacellBaseWithHole_D0_H%d", half), rh0, mRohacell); TGeoVolume* partRohacell; rohacellBaseWithHole->SetLineColor(kGray); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - rohacellPlate->AddNode(rohacellBaseWithHole, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + rohacellPlate->AddNode(rohacellBaseWithHole, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); ty = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { ty += mSupportYDimensions[disk][ipart] / 2.; auto* t = new TGeoTranslation("t", 0, ty + mHalfDiskGap, mZPlan[disk]); - auto* partRohacell0 = new TGeoBBox(Form("rohacellBase0_D0_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., mRohacellThickness); + auto* partRohacell0 = + new TGeoBBox(Form("rohacellBase0_D0_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., mRohacellThickness); if (Geometry::sGrooves == 1) { - // ************************ Creating grooves for the other parts of the rohacell plate ********************** + // ************************ Creating grooves for the other parts of the + // rohacell plate ********************** Double_t mShift; for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 3; igroove++) { // 3 grooves if (ipart == 1) { - mPosition[ipart] = mXPosition0[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; + mPosition[ipart] = + mXPosition0[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; mShift = -mSupportYDimensions[disk][ipart - 1]; }; if (ipart == 2) { - mPosition[ipart] = mXPosition0[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; + mPosition[ipart] = + mXPosition0[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; }; if (ipart == 3) { - mPosition[ipart] = mXPosition0[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; + mPosition[ipart] = + mXPosition0[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; }; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[0][0] / 2. - mLWater0[igroove] / 2., mPosition[ipart], iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[0][0] / 2. - mLWater0[igroove] / 2., + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellBaseGroove[iCount] = + new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[iCount]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[igroove], mPosition[ipart] + mRadius0[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[0][0] / 2. + mMoreLength01 - + mLWater0[igroove], + mPosition[ipart] + mRadius0[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle0[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[igroove], yPos0[igroove] + mShift - mHalfDiskGap - mSupportYDimensions[disk][ipart] / 2., iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle0[igroove], 90., 0.); + transfo[ip][igroove] = + new TGeoCombiTrans(mSupportXDimensions[0][0] / 2. + + mMoreLength01 - xPos0[igroove], + yPos0[igroove] + mShift - mHalfDiskGap - + mSupportYDimensions[disk][ipart] / 2., + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., mPosition[ipart] + yPos0[igroove] + mLpartial0[igroove] / 2 * TMath::Sin(mAngle0[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle0[igroove] * TMath::DegToRad()) - mXPosition0[igroove], - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + mPosition[ipart] + yPos0[igroove] + + mLpartial0[igroove] / 2 * + TMath::Sin(mAngle0[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle0[igroove] * TMath::DegToRad()) - + mXPosition0[igroove], + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle0[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[0][0] / 2. + mMoreLength01 - xPos0[igroove]), yPos0[igroove] + mPosition[ipart] - mXPosition0[igroove], - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle0[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - + xPos0[igroove]), + yPos0[igroove] + mPosition[ipart] - mXPosition0[igroove], + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[igroove]), mRadius0[igroove] + mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - + mLWater0[igroove]), + mRadius0[igroove] + mPosition[ipart], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[0][0] / 2. + mMoreLength01 - mLWater0[igroove] / 2.), mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[0][0] / 2. + mMoreLength01 - + mLWater0[igroove] / 2.), + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], + nullptr, transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell0Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -1365,31 +1919,43 @@ void HeatExchanger::createHalfDisk0(Int_t half) TGeoBBox* notchRohacell0; TGeoTranslation* tnotch0; if (ipart == (mNPart[disk] - 1)) { - notchRohacell0 = new TGeoBBox(Form("notchRohacell0_D0_H%d", half), 1.1, 0.4, mRohacellThickness + 0.000001); - tnotch0 = new TGeoTranslation("tnotch0", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchRohacell0 = new TGeoBBox(Form("notchRohacell0_D0_H%d", half), 1.1, + 0.4, mRohacellThickness + 0.000001); + tnotch0 = new TGeoTranslation("tnotch0", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch0->RegisterYourself(); } //============================================================= if (Geometry::sGrooves == 0) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell0, nullptr, tnotch0); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D0_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell0, + nullptr, tnotch0); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D0_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D0_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), partRohacell0, mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), + partRohacell0, mRohacell); } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], notchRohacell0, nullptr, tnotch0); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D0_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], + notchRohacell0, nullptr, tnotch0); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D0_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D0_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D0_H%d_%d", half, ipart), + rohacellGroove[iCount - 1], mRohacell); } } //=========================================================================================================== @@ -1398,11 +1964,15 @@ void HeatExchanger::createHalfDisk0(Int_t half) rohacellPlate->AddNode(partRohacell, ipart, t); ty += mSupportYDimensions[disk][ipart] / 2.; - //========== insert to locate the rohacell plate compare to the disk support ============= + //========== insert to locate the rohacell plate compare to the disk support + //============= if (ipart == (mNPart[disk] - 1)) { TGeoTranslation* tinsert0; - TGeoVolume* insert0 = gGeoManager->MakeBox(Form("insert0_H%d_%d", half, ipart), mPeek, 1.0, 0.35 / 2., mRohacellThickness); - Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap - 0.35 / 2.; + TGeoVolume* insert0 = + gGeoManager->MakeBox(Form("insert0_H%d_%d", half, ipart), mPeek, 1.0, + 0.35 / 2., mRohacellThickness); + Double_t ylocation = + mSupportYDimensions[disk][0] + mHalfDiskGap - 0.35 / 2.; for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; } @@ -1447,134 +2017,251 @@ void HeatExchanger::createHalfDisk1(Int_t half) TGeoRotation* rotation = nullptr; TGeoCombiTrans* transformation = nullptr; - // **************************************** Water part **************************************** - // ********************** Four parameters mLwater1, mRadius1, mAngle1, mLpartial1 ************* + // **************************************** Water part + // **************************************** + // ********************** Four parameters mLwater1, mRadius1, mAngle1, + // mLpartial1 ************* Double_t ivolume = 100; // offset chamber 1 Double_t mRadiusCentralTore[4]; Double_t xPos1[4]; Double_t yPos1[4]; for (Int_t itube = 0; itube < 3; itube++) { - TGeoVolume* waterTube1 = gGeoManager->MakeTube(Form("waterTube1%d_D1_H%d", itube, half), mWater, 0., mRWater, mLWater1[itube] / 2.); - translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube] / 2.); + TGeoVolume* waterTube1 = + gGeoManager->MakeTube(Form("waterTube1%d_D1_H%d", itube, half), mWater, + 0., mRWater, mLWater1[itube] / 2.); + translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., + mSupportXDimensions[1][0] / 2. + + mMoreLength01 - mLWater1[itube] / 2.); cooling->AddNode(waterTube1, ivolume++, translation); - TGeoVolume* waterTorus1 = gGeoManager->MakeTorus(Form("waterTorus1%d_D1_H%d", itube, half), mWater, mRadius1[itube], 0., mRWater, 0., mAngle1[itube]); + TGeoVolume* waterTorus1 = gGeoManager->MakeTorus( + Form("waterTorus1%d_D1_H%d", itube, half), mWater, mRadius1[itube], 0., + mRWater, 0., mAngle1[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., + mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube], + rotation); cooling->AddNode(waterTorus1, ivolume++, transformation); - TGeoVolume* waterTube2 = gGeoManager->MakeTube(Form("waterTube2%d_D1_H%d", itube, half), mWater, 0., mRWater, mLpartial1[itube] / 2.); + TGeoVolume* waterTube2 = + gGeoManager->MakeTube(Form("waterTube2%d_D1_H%d", itube, half), mWater, + 0., mRWater, mLpartial1[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle1[itube], 0.); - xPos1[itube] = mLWater1[itube] + mRadius1[itube] * TMath::Sin(mAngle1[itube] * TMath::DegToRad()) + mLpartial1[itube] / 2 * TMath::Cos(mAngle1[itube] * TMath::DegToRad()); - yPos1[itube] = mXPosition1[itube] - mHalfDiskGap + mRadius1[itube] * (1 - TMath::Cos(mAngle1[itube] * TMath::DegToRad())) + mLpartial1[itube] / 2 * TMath::Sin(mAngle1[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos1[itube], 0., mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube], rotation); + xPos1[itube] = + mLWater1[itube] + + mRadius1[itube] * TMath::Sin(mAngle1[itube] * TMath::DegToRad()) + + mLpartial1[itube] / 2 * TMath::Cos(mAngle1[itube] * TMath::DegToRad()); + yPos1[itube] = + mXPosition1[itube] - mHalfDiskGap + + mRadius1[itube] * (1 - TMath::Cos(mAngle1[itube] * TMath::DegToRad())) + + mLpartial1[itube] / 2 * TMath::Sin(mAngle1[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans(yPos1[itube], 0., + mSupportXDimensions[1][0] / 2. + + mMoreLength01 - xPos1[itube], + rotation); cooling->AddNode(waterTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube] - mLpartial1[itube] / 2 * TMath::Cos(mAngle1[itube] * TMath::DegToRad())) / TMath::Sin(mAngle1[itube] * TMath::DegToRad()); - TGeoVolume* waterTorusCentral = gGeoManager->MakeTorus(Form("waterTorusCentral%d_D1_H%d", itube, half), mWater, mRadiusCentralTore[itube], 0., mRWater, - -mAngle1[itube], 2. * mAngle1[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube] - + mLpartial1[itube] / 2 * + TMath::Cos(mAngle1[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle1[itube] * TMath::DegToRad()); + TGeoVolume* waterTorusCentral = + gGeoManager->MakeTorus(Form("waterTorusCentral%d_D1_H%d", itube, half), + mWater, mRadiusCentralTore[itube], 0., mRWater, + -mAngle1[itube], 2. * mAngle1[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos1[itube] + mLpartial1[itube] / 2 * TMath::Sin(mAngle1[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle1[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos1[itube] + + mLpartial1[itube] / 2 * + TMath::Sin(mAngle1[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle1[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(waterTorusCentral, ivolume++, transformation); - TGeoVolume* waterTube3 = gGeoManager->MakeTube(Form("waterTube3%d_D1_H%d", 2, half), mWater, 0., mRWater, mLpartial1[itube] / 2.); + TGeoVolume* waterTube3 = + gGeoManager->MakeTube(Form("waterTube3%d_D1_H%d", 2, half), mWater, 0., + mRWater, mLpartial1[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle1[itube], 0.); - transformation = new TGeoCombiTrans(yPos1[itube], 0., -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos1[itube], 0., + -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube]), + rotation); cooling->AddNode(waterTube3, ivolume++, transformation); - TGeoVolume* waterTorus2 = gGeoManager->MakeTorus(Form("waterTorus2%d_D1_H%d", itube, half), mWater, mRadius1[itube], 0., mRWater, 0., mAngle1[itube]); + TGeoVolume* waterTorus2 = gGeoManager->MakeTorus( + Form("waterTorus2%d_D1_H%d", itube, half), mWater, mRadius1[itube], 0., + mRWater, 0., mAngle1[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube]), + rotation); cooling->AddNode(waterTorus2, ivolume++, transformation); - TGeoVolume* waterTube4 = gGeoManager->MakeTube(Form("waterTube4%d_D1_H%d", itube, half), mWater, 0., mRWater, mLWater1[itube] / 2.); - translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube] / 2.)); + TGeoVolume* waterTube4 = + gGeoManager->MakeTube(Form("waterTube4%d_D1_H%d", itube, half), mWater, + 0., mRWater, mLWater1[itube] / 2.); + translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[1][0] / 2. + + mMoreLength01 - mLWater1[itube] / 2.)); cooling->AddNode(waterTube4, ivolume++, translation); } - // **************************************************** Tube part ************************************************ - // ****************************** Four parameters mLwater1, mRadius1, mAngle1, mLpartial1 ************************ + // **************************************************** Tube part + // ************************************************ + // ****************************** Four parameters mLwater1, mRadius1, mAngle1, + // mLpartial1 ************************ for (Int_t itube = 0; itube < 3; itube++) { - TGeoVolume* pipeTube1 = gGeoManager->MakeTube(Form("pipeTube1%d_D1_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater1[itube] / 2.); - translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube] / 2.); + TGeoVolume* pipeTube1 = + gGeoManager->MakeTube(Form("pipeTube1%d_D1_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater1[itube] / 2.); + translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., + mSupportXDimensions[1][0] / 2. + + mMoreLength01 - mLWater1[itube] / 2.); cooling->AddNode(pipeTube1, ivolume++, translation); - TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus(Form("pipeTorus1%d_D1_H%d", itube, half), mPipe, mRadius1[itube], mRWater, mRWater + mDRPipe, 0., mAngle1[itube]); + TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus( + Form("pipeTorus1%d_D1_H%d", itube, half), mPipe, mRadius1[itube], + mRWater, mRWater + mDRPipe, 0., mAngle1[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., + mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube], + rotation); cooling->AddNode(pipeTorus1, ivolume++, transformation); - TGeoVolume* pipeTube2 = gGeoManager->MakeTube(Form("pipeTube2%d_D1_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial1[itube] / 2.); + TGeoVolume* pipeTube2 = gGeoManager->MakeTube( + Form("pipeTube2%d_D1_H%d", itube, half), mPipe, mRWater, + mRWater + mDRPipe, mLpartial1[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle1[itube], 0.); - xPos1[itube] = mLWater1[itube] + mRadius1[itube] * TMath::Sin(mAngle1[itube] * TMath::DegToRad()) + mLpartial1[itube] / 2 * TMath::Cos(mAngle1[itube] * TMath::DegToRad()); - yPos1[itube] = mXPosition1[itube] - mHalfDiskGap + mRadius1[itube] * (1 - TMath::Cos(mAngle1[itube] * TMath::DegToRad())) + mLpartial1[itube] / 2 * TMath::Sin(mAngle1[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos1[itube], 0., mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube], rotation); + xPos1[itube] = + mLWater1[itube] + + mRadius1[itube] * TMath::Sin(mAngle1[itube] * TMath::DegToRad()) + + mLpartial1[itube] / 2 * TMath::Cos(mAngle1[itube] * TMath::DegToRad()); + yPos1[itube] = + mXPosition1[itube] - mHalfDiskGap + + mRadius1[itube] * (1 - TMath::Cos(mAngle1[itube] * TMath::DegToRad())) + + mLpartial1[itube] / 2 * TMath::Sin(mAngle1[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans(yPos1[itube], 0., + mSupportXDimensions[1][0] / 2. + + mMoreLength01 - xPos1[itube], + rotation); cooling->AddNode(pipeTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube] - mLpartial1[itube] / 2 * TMath::Cos(mAngle1[itube] * TMath::DegToRad())) / TMath::Sin(mAngle1[itube] * TMath::DegToRad()); - TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus(Form("pipeTorusCentral%d_D1_H%d", itube, half), mPipe, mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, - -mAngle1[itube], 2. * mAngle1[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube] - + mLpartial1[itube] / 2 * + TMath::Cos(mAngle1[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle1[itube] * TMath::DegToRad()); + TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus( + Form("pipeTorusCentral%d_D1_H%d", itube, half), mPipe, + mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, -mAngle1[itube], + 2. * mAngle1[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos1[itube] + mLpartial1[itube] / 2 * TMath::Sin(mAngle1[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle1[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos1[itube] + + mLpartial1[itube] / 2 * + TMath::Sin(mAngle1[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle1[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(pipeTorusCentral, ivolume++, transformation); - TGeoVolume* pipeTube3 = gGeoManager->MakeTube(Form("pipeTube3%d_D1_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial1[itube] / 2.); + TGeoVolume* pipeTube3 = gGeoManager->MakeTube( + Form("pipeTube3%d_D1_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, + mLpartial1[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle1[itube], 0.); - transformation = new TGeoCombiTrans(yPos1[itube], 0., -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos1[itube], 0., + -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[itube]), + rotation); cooling->AddNode(pipeTube3, ivolume++, transformation); - TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus(Form("pipeTorus2%d_D1_H%d", itube, half), mPipe, mRadius1[itube], mRWater, mRWater + mDRPipe, 0., mAngle1[itube]); + TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus( + Form("pipeTorus2%d_D1_H%d", itube, half), mPipe, mRadius1[itube], + mRWater, mRWater + mDRPipe, 0., mAngle1[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius1[itube] + mXPosition1[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube]), + rotation); cooling->AddNode(pipeTorus2, ivolume++, transformation); - TGeoVolume* pipeTube4 = gGeoManager->MakeTube(Form("pipeTube4%d_D1_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater1[itube] / 2.); - translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[itube] / 2.)); + TGeoVolume* pipeTube4 = + gGeoManager->MakeTube(Form("pipeTube4%d_D1_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater1[itube] / 2.); + translation = new TGeoTranslation(mXPosition1[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[1][0] / 2. + + mMoreLength01 - mLWater1[itube] / 2.)); cooling->AddNode(pipeTube4, ivolume++, translation); } // *********************************************************************************************** - Double_t deltaz = mHeatExchangerThickness - Geometry::sKaptonOnCarbonThickness * 4 - Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; + Double_t deltaz = mHeatExchangerThickness - + Geometry::sKaptonOnCarbonThickness * 4 - + Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; rotation = new TGeoRotation("rotation", -90., 90., 0.); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 0, transformation); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 1, transformation); - // **************************************** Carbon Plates **************************************** - + // **************************************** Carbon Plates + // **************************************** auto* carbonPlate = new TGeoVolumeAssembly(Form("carbonPlate_D1_H%d", half)); - auto* carbonBase1 = new TGeoBBox(Form("carbonBase1_D1_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength01, - (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); - auto* t11 = new TGeoTranslation("t11", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto& mftBaseParam = MFTBaseParam::Instance(); + Double_t mReducedX = 0.; + if (mftBaseParam.buildAlignment) { + mReducedX = 0.6; + } + + auto* carbonBase1 = new TGeoBBox( + Form("carbonBase1_D1_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength01 - mReducedX, + (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); + auto* t11 = new TGeoTranslation( + "t11", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); t11->RegisterYourself(); auto* holeCarbon1 = - new TGeoTubeSeg(Form("holeCarbon1_D1_H%d", half), 0., mRMin[disk], mCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeCarbon1_D1_H%d", half), 0., mRMin[disk], + mCarbonThickness + 0.000001, 0, 180.); auto* t12 = new TGeoTranslation("t12", 0., -mHalfDiskGap, 0.); t12->RegisterYourself(); auto* carbonhole1 = new TGeoSubtraction(carbonBase1, holeCarbon1, t11, t12); auto* ch1 = new TGeoCompositeShape(Form("Carbon1_D1_H%d", half), carbonhole1); - auto* carbonBaseWithHole1 = new TGeoVolume(Form("carbonBaseWithHole_D1_H%d", half), ch1, mCarbon); + auto* carbonBaseWithHole1 = + new TGeoVolume(Form("carbonBaseWithHole_D1_H%d", half), ch1, mCarbon); carbonBaseWithHole1->SetLineColor(kGray + 3); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - carbonPlate->AddNode(carbonBaseWithHole1, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + carbonPlate->AddNode(carbonBaseWithHole1, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t ty = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { ty += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partCarbon = - gGeoManager->MakeBox(Form("partCarbon_D1_H%d_%d", half, ipart), mCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); + TGeoVolume* partCarbon = gGeoManager->MakeBox( + Form("partCarbon_D1_H%d_%d", half, ipart), mCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); partCarbon->SetLineColor(kGray + 3); auto* t = new TGeoTranslation("t", 0, ty + mHalfDiskGap, mZPlan[disk]); carbonPlate->AddNode(partCarbon, ipart, t); @@ -1587,39 +2274,56 @@ void HeatExchanger::createHalfDisk1(Int_t half) transformation = new TGeoCombiTrans(0., 0., -deltaz / 2., rotation); mHalfDisk->AddNode(carbonPlate, 1, transformation); - // **************************************** Glue Bwtween Carbon Plate and Rohacell Plate **************************************** + // **************************************** Glue Bwtween Carbon Plate and + // Rohacell Plate **************************************** TGeoMedium* mGlueRohacellCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueRohacellCarbon = new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); + auto* glueRohacellCarbon = + new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); - auto* glueRohacellCarbonBase0 = new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sGlueRohacellCarbonThickness); + auto* glueRohacellCarbonBase0 = + new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., + Geometry::sGlueRohacellCarbonThickness); - auto* translation_gluRC01 = new TGeoTranslation("translation_gluRC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* translation_gluRC01 = new TGeoTranslation( + "translation_gluRC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluRC01->RegisterYourself(); - auto* translation_gluRC02 = new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluRC02 = + new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); translation_gluRC02->RegisterYourself(); - auto* holeglueRohacellCarbon0 = - new TGeoTubeSeg(Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); + auto* holeglueRohacellCarbon0 = new TGeoTubeSeg( + Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); - auto* glueRohacellCarbonhole0 = new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, translation_gluRC01, translation_gluRC02); - auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), glueRohacellCarbonhole0); - auto* glueRohacellCarbonBaseWithHole0 = new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, mGlueRohacellCarbon); + auto* glueRohacellCarbonhole0 = + new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, + translation_gluRC01, translation_gluRC02); + auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), + glueRohacellCarbonhole0); + auto* glueRohacellCarbonBaseWithHole0 = + new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, + mGlueRohacellCarbon); glueRohacellCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGRC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGRC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueRohacellCarbon = - gGeoManager->MakeBox(Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sGlueRohacellCarbonThickness); + TGeoVolume* partGlueRohacellCarbon = gGeoManager->MakeBox( + Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), + mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sGlueRohacellCarbonThickness); partGlueRohacellCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGRC + mHalfDiskGap, mZPlan[disk]); glueRohacellCarbon->AddNode(partGlueRohacellCarbon, ipart, t); @@ -1627,41 +2331,62 @@ void HeatExchanger::createHalfDisk1(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness), rotation); + transformation = new TGeoCombiTrans(0., 0., + -(deltaz / 2. - mCarbonThickness - + Geometry::sGlueRohacellCarbonThickness), + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 4, transformation); - // **************************************** Kapton on Carbon Plate **************************************** + // **************************************** Kapton on Carbon Plate + // **************************************** TGeoMedium* mKaptonOnCarbon = gGeoManager->GetMedium("MFT_Kapton$"); - auto* kaptonOnCarbon = new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); - auto* kaptonOnCarbonBase0 = new TGeoBBox(Form("kaptonOnCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength01, - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); - - auto* translation_KC01 = new TGeoTranslation("translation_KC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* kaptonOnCarbon = + new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); + auto* kaptonOnCarbonBase0 = new TGeoBBox( + Form("kaptonOnCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength01 - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); + + auto* translation_KC01 = new TGeoTranslation( + "translation_KC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_KC01->RegisterYourself(); - auto* translation_KC02 = new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); + auto* translation_KC02 = + new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); translation_KC02->RegisterYourself(); auto* holekaptonOnCarbon0 = - new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); - auto* kaptonOnCarbonhole0 = new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, translation_KC01, translation_KC02); - auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), kaptonOnCarbonhole0); - auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume(Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); + auto* kaptonOnCarbonhole0 = + new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, + translation_KC01, translation_KC02); + auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), + kaptonOnCarbonhole0); + auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume( + Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); kaptonOnCarbonBaseWithHole0->SetLineColor(kMagenta); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partkaptonOnCarbonBase = - gGeoManager->MakeBox(Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonOnCarbonThickness); + TGeoVolume* partkaptonOnCarbonBase = gGeoManager->MakeBox( + Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sKaptonOnCarbonThickness); partkaptonOnCarbonBase->SetLineColor(kMagenta); auto* t = new TGeoTranslation("t", 0, tyKC + mHalfDiskGap, mZPlan[disk]); kaptonOnCarbon->AddNode(partkaptonOnCarbonBase, ipart, t); @@ -1669,41 +2394,63 @@ void HeatExchanger::createHalfDisk1(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2, + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2), + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 4, transformation); - // **************************************** Kapton glue on the carbon plate **************************************** + // **************************************** Kapton glue on the carbon plate + // **************************************** TGeoMedium* mGlueKaptonCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueKaptonCarbon = new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); - auto* glueKaptonCarbonBase0 = new TGeoBBox(Form("glueKaptonCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); - - auto* translation_gluKC01 = new TGeoTranslation("translation_gluKC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueKaptonCarbon = + new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); + auto* glueKaptonCarbonBase0 = new TGeoBBox( + Form("glueKaptonCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); + + auto* translation_gluKC01 = new TGeoTranslation( + "translation_gluKC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluKC01->RegisterYourself(); - auto* translation_gluKC02 = new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluKC02 = + new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); translation_gluKC02->RegisterYourself(); - auto* holeglueKaptonCarbon0 = - new TGeoTubeSeg(Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); + auto* holeglueKaptonCarbon0 = new TGeoTubeSeg( + Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); - auto* glueKaptonCarbonhole0 = new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, translation_gluKC01, translation_gluKC02); - auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), glueKaptonCarbonhole0); - auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume(Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); + auto* glueKaptonCarbonhole0 = + new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, + translation_gluKC01, translation_gluKC02); + auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), + glueKaptonCarbonhole0); + auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume( + Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); glueKaptonCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueKaptonCarbon = - gGeoManager->MakeBox(Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); + TGeoVolume* partGlueKaptonCarbon = gGeoManager->MakeBox( + Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); partGlueKaptonCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGKC + mHalfDiskGap, mZPlan[disk]); glueKaptonCarbon->AddNode(partGlueKaptonCarbon, ipart, t); @@ -1711,18 +2458,28 @@ void HeatExchanger::createHalfDisk1(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 4, transformation); - // **************************************** Rohacell Plate **************************************** - auto* rohacellPlate = new TGeoVolumeAssembly(Form("rohacellPlate_D1_H%d", half)); - auto* rohacellBase1 = new TGeoBBox("rohacellBase1", (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); - auto* holeRohacell1 = new TGeoTubeSeg("holeRohacell1", 0., mRMin[disk], mRohacellThickness + 0.000001, 0, 180.); - - // **************************************** GROOVES ************************************************* + // **************************************** Rohacell Plate + // **************************************** + auto* rohacellPlate = + new TGeoVolumeAssembly(Form("rohacellPlate_D1_H%d", half)); + auto* rohacellBase1 = + new TGeoBBox("rohacellBase1", (mSupportXDimensions[disk][0]) / 2., + (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); + auto* holeRohacell1 = new TGeoTubeSeg("holeRohacell1", 0., mRMin[disk], + mRohacellThickness + 0.000001, 0, 180.); + + // **************************************** GROOVES + // ************************************************* Double_t diameter = 0.21; // groove diameter Double_t epsilon = 0.06; // outside shift of the goove Int_t iCount = 0; @@ -1734,20 +2491,31 @@ void HeatExchanger::createHalfDisk1(Int_t half) TGeoCompositeShape* rohacellGroove[300]; for (Int_t igroove = 0; igroove < 3; igroove++) { - grooveTube[0][igroove] = new TGeoTube("linear", 0., diameter, mLWater1[igroove] / 2.); - grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius1[igroove], 0., diameter, 0., mAngle1[igroove]); - grooveTube[2][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial1[igroove] / 2.); - grooveTorus[3][igroove] = new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, -mAngle1[igroove], 2. * mAngle1[igroove]); - grooveTube[4][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial1[igroove] / 2.); - grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius1[igroove], 0., diameter, 0., mAngle1[igroove]); - grooveTube[6][igroove] = new TGeoTube("linear", 0., diameter, mLWater1[igroove] / 2.); + grooveTube[0][igroove] = + new TGeoTube("linear", 0., diameter, mLWater1[igroove] / 2.); + grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius1[igroove], 0., + diameter, 0., mAngle1[igroove]); + grooveTube[2][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial1[igroove] / 2.); + grooveTorus[3][igroove] = + new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, + -mAngle1[igroove], 2. * mAngle1[igroove]); + grooveTube[4][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial1[igroove] / 2.); + grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius1[igroove], 0., + diameter, 0., mAngle1[igroove]); + grooveTube[6][igroove] = + new TGeoTube("linear", 0., diameter, mLWater1[igroove] / 2.); } // Rotation matrix TGeoRotation* rotationLinear = new TGeoRotation("rotation", -90., 90., 0.); - TGeoRotation* rotationSideTorusL = new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); - TGeoRotation* rotationSideTorusR = new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); - TGeoRotation* rotationCentralTorus = new TGeoRotation("rotationCentralTorus", 90., 0., 0.); + TGeoRotation* rotationSideTorusL = + new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); + TGeoRotation* rotationSideTorusR = + new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); + TGeoRotation* rotationCentralTorus = + new TGeoRotation("rotationCentralTorus", 90., 0., 0.); TGeoRotation* rotationTiltedLinearR; TGeoRotation* rotationTiltedLinearL; @@ -1755,52 +2523,95 @@ void HeatExchanger::createHalfDisk1(Int_t half) if (Geometry::sGrooves == 1) { for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 3; igroove++) { // 3 grooves - mPosition[igroove] = mXPosition1[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; + mPosition[igroove] = mXPosition1[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[igroove] / 2., mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[1][0] / 2. + mMoreLength01 - + mLWater1[igroove] / 2., + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[0] = new TGeoSubtraction(rohacellBase1, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[0] = new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[0]); + rohacellBaseGroove[0] = + new TGeoSubtraction(rohacellBase1, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[0] = + new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[0]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[igroove], mRadius1[igroove] + mXPosition1[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[1][0] / 2. + mMoreLength01 - + mLWater1[igroove], + mRadius1[igroove] + mXPosition1[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle1[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[igroove], yPos1[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle1[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[igroove], + yPos1[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., yPos1[igroove] + mLpartial1[igroove] / 2 * TMath::Sin(mAngle1[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle1[igroove] * TMath::DegToRad()) - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + yPos1[igroove] + + mLpartial1[igroove] / 2 * + TMath::Sin(mAngle1[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle1[igroove] * TMath::DegToRad()) - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle1[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[1][0] / 2. + mMoreLength01 - xPos1[igroove]), yPos1[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle1[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - + xPos1[igroove]), + yPos1[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[igroove]), mRadius1[igroove] + mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - + mLWater1[igroove]), + mRadius1[igroove] + mPosition[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[1][0] / 2. + mMoreLength01 - mLWater1[igroove] / 2.), mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[1][0] / 2. + mMoreLength01 - + mLWater1[igroove] / 2.), + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, + transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, + transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -1815,16 +2626,20 @@ void HeatExchanger::createHalfDisk1(Int_t half) rohacellBase = new TGeoSubtraction(rohacellBase1, holeRohacell1, t11, t12); } if (Geometry::sGrooves == 1) { - rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell1, t11, t12); + rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], + holeRohacell1, t11, t12); } - auto* rh1 = new TGeoCompositeShape(Form("rohacellBase1_D1_H%d", half), rohacellBase); - auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D1_H%d", half), rh1, mRohacell); + auto* rh1 = + new TGeoCompositeShape(Form("rohacellBase1_D1_H%d", half), rohacellBase); + auto* rohacellBaseWithHole = + new TGeoVolume(Form("rohacellBaseWithHole_D1_H%d", half), rh1, mRohacell); TGeoVolume* partRohacell; rohacellBaseWithHole->SetLineColor(kGray); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - rohacellPlate->AddNode(rohacellBaseWithHole, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + rohacellPlate->AddNode(rohacellBaseWithHole, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); ty = mSupportYDimensions[disk][0]; @@ -1835,74 +2650,129 @@ void HeatExchanger::createHalfDisk1(Int_t half) //=========================================================================================================== //=========================================================================================================== auto* partRohacell0 = - new TGeoBBox(Form("rohacellBase0_D1_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., + new TGeoBBox(Form("rohacellBase0_D1_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., mSupportYDimensions[disk][ipart] / 2., mRohacellThickness); if (Geometry::sGrooves == 1) { - // ***************** Creating grooves for the other parts of the rohacell plate ********************** + // ***************** Creating grooves for the other parts of the rohacell + // plate ********************** Double_t mShift; for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 3; igroove++) { // 3 grooves if (ipart == 1) { - mPosition[ipart] = mXPosition1[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; + mPosition[ipart] = + mXPosition1[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; mShift = -mSupportYDimensions[disk][ipart - 1]; }; if (ipart == 2) { - mPosition[ipart] = mXPosition1[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; + mPosition[ipart] = + mXPosition1[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; }; if (ipart == 3) { - mPosition[ipart] = mXPosition1[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; + mPosition[ipart] = + mXPosition1[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; }; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater1[igroove] / 2., mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater1[igroove] / 2., + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellBaseGroove[iCount] = + new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[iCount]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater1[igroove], mPosition[ipart] + mRadius1[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater1[igroove], + mPosition[ipart] + mRadius1[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle1[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - xPos1[igroove], yPos1[igroove] + mShift - mHalfDiskGap - mSupportYDimensions[disk][ipart] / 2., iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle1[igroove], 90., 0.); + transfo[ip][igroove] = + new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + + mMoreLength01 - xPos1[igroove], + yPos1[igroove] + mShift - mHalfDiskGap - + mSupportYDimensions[disk][ipart] / 2., + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., mPosition[ipart] + yPos1[igroove] + mLpartial1[igroove] / 2 * TMath::Sin(mAngle1[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle1[igroove] * TMath::DegToRad()) - mXPosition1[igroove], - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + mPosition[ipart] + yPos1[igroove] + + mLpartial1[igroove] / 2 * + TMath::Sin(mAngle1[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle1[igroove] * TMath::DegToRad()) - + mXPosition1[igroove], + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle1[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - xPos1[igroove]), yPos1[igroove] + mPosition[ipart] - mXPosition1[igroove], - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle1[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + xPos1[igroove]), + yPos1[igroove] + mPosition[ipart] - mXPosition1[igroove], + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater1[igroove]), mRadius1[igroove] + mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater1[igroove]), + mRadius1[igroove] + mPosition[ipart], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - mLWater1[igroove] / 2.), mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength01 - + mLWater1[igroove] / 2.), + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], + nullptr, transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell1Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -1915,30 +2785,42 @@ void HeatExchanger::createHalfDisk1(Int_t half) TGeoBBox* notchRohacell1; TGeoTranslation* tnotch1; if (ipart == (mNPart[disk] - 1)) { - notchRohacell1 = new TGeoBBox(Form("notchRohacell1_D1_H%d", half), 1.1, 0.4, mRohacellThickness + 0.000001); - tnotch1 = new TGeoTranslation("tnotch1", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchRohacell1 = new TGeoBBox(Form("notchRohacell1_D1_H%d", half), 1.1, + 0.4, mRohacellThickness + 0.000001); + tnotch1 = new TGeoTranslation("tnotch1", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch1->RegisterYourself(); } //============================================================= if (Geometry::sGrooves == 0) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell1, nullptr, tnotch1); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D1_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell1, + nullptr, tnotch1); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D1_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D1_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), partRohacell0, mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), + partRohacell0, mRohacell); } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], notchRohacell1, nullptr, tnotch1); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D1_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], + notchRohacell1, nullptr, tnotch1); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D1_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D1_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D1_H%d_%d", half, ipart), + rohacellGroove[iCount - 1], mRohacell); } } @@ -1948,11 +2830,15 @@ void HeatExchanger::createHalfDisk1(Int_t half) rohacellPlate->AddNode(partRohacell, ipart, t); ty += mSupportYDimensions[disk][ipart] / 2.; - //========== insert to locate the rohacell plate compare to the disk support ============= + //========== insert to locate the rohacell plate compare to the disk support + //============= if (ipart == (mNPart[disk] - 1)) { TGeoTranslation* tinsert1; - TGeoVolume* insert1 = gGeoManager->MakeBox(Form("insert1_H%d_%d", half, ipart), mPeek, 1.0, 0.35 / 2., mRohacellThickness); - Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap - 0.35 / 2.; + TGeoVolume* insert1 = + gGeoManager->MakeBox(Form("insert1_H%d_%d", half, ipart), mPeek, 1.0, + 0.35 / 2., mRohacellThickness); + Double_t ylocation = + mSupportYDimensions[disk][0] + mHalfDiskGap - 0.35 / 2.; for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; } @@ -1997,124 +2883,240 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoRotation* rotation = nullptr; TGeoCombiTrans* transformation = nullptr; - // **************************************** Water part **************************************** - // ********************** Four parameters mLwater2, mRadius2, mAngle2, mLpartial2 ************* + // **************************************** Water part + // **************************************** + // ********************** Four parameters mLwater2, mRadius2, mAngle2, + // mLpartial2 ************* Double_t ivolume = 200; // offset chamber 2 Double_t mRadiusCentralTore[4]; Double_t xPos2[4]; Double_t yPos2[4]; for (Int_t itube = 0; itube < 3; itube++) { - TGeoVolume* waterTube1 = gGeoManager->MakeTube(Form("waterTube1%d_D2_H%d", itube, half), mWater, 0., mRWater, mLWater2[itube] / 2.); - translation = new TGeoTranslation(mXPosition2[itube] - mHalfDiskGap, 0., mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube] / 2.); + TGeoVolume* waterTube1 = + gGeoManager->MakeTube(Form("waterTube1%d_D2_H%d", itube, half), mWater, + 0., mRWater, mLWater2[itube] / 2.); + translation = new TGeoTranslation(mXPosition2[itube] - mHalfDiskGap, 0., + mSupportXDimensions[2][0] / 2. + + mMoreLength - mLWater2[itube] / 2.); cooling->AddNode(waterTube1, ivolume++, translation); - TGeoVolume* waterTorus1 = gGeoManager->MakeTorus(Form("waterTorus1%d_D2_H%d", itube, half), mWater, mRadius2[itube], 0., mRWater, 0., mAngle2[itube]); + TGeoVolume* waterTorus1 = gGeoManager->MakeTorus( + Form("waterTorus1%d_D2_H%d", itube, half), mWater, mRadius2[itube], 0., + mRWater, 0., mAngle2[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., + mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube], + rotation); cooling->AddNode(waterTorus1, ivolume++, transformation); - TGeoVolume* waterTube2 = gGeoManager->MakeTube(Form("waterTube2%d_D2_H%d", itube, half), mWater, 0., mRWater, mLpartial2[itube] / 2.); + TGeoVolume* waterTube2 = + gGeoManager->MakeTube(Form("waterTube2%d_D2_H%d", itube, half), mWater, + 0., mRWater, mLpartial2[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle2[itube], 0.); - xPos2[itube] = mLWater2[itube] + mRadius2[itube] * TMath::Sin(mAngle2[itube] * TMath::DegToRad()) + mLpartial2[itube] / 2 * TMath::Cos(mAngle2[itube] * TMath::DegToRad()); - yPos2[itube] = mXPosition2[itube] - mHalfDiskGap + mRadius2[itube] * (1 - TMath::Cos(mAngle2[itube] * TMath::DegToRad())) + mLpartial2[itube] / 2 * TMath::Sin(mAngle2[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos2[itube], 0., mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube], rotation); + xPos2[itube] = + mLWater2[itube] + + mRadius2[itube] * TMath::Sin(mAngle2[itube] * TMath::DegToRad()) + + mLpartial2[itube] / 2 * TMath::Cos(mAngle2[itube] * TMath::DegToRad()); + yPos2[itube] = + mXPosition2[itube] - mHalfDiskGap + + mRadius2[itube] * (1 - TMath::Cos(mAngle2[itube] * TMath::DegToRad())) + + mLpartial2[itube] / 2 * TMath::Sin(mAngle2[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans( + yPos2[itube], 0., + mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube], rotation); cooling->AddNode(waterTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube] - mLpartial2[itube] / 2 * TMath::Cos(mAngle2[itube] * TMath::DegToRad())) / TMath::Sin(mAngle2[itube] * TMath::DegToRad()); - TGeoVolume* waterTorusCentral = gGeoManager->MakeTorus(Form("waterTorusCentral%d_D2_H%d", itube, half), mWater, mRadiusCentralTore[itube], 0., mRWater, -mAngle2[itube], 2. * mAngle2[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube] - + mLpartial2[itube] / 2 * + TMath::Cos(mAngle2[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle2[itube] * TMath::DegToRad()); + TGeoVolume* waterTorusCentral = + gGeoManager->MakeTorus(Form("waterTorusCentral%d_D2_H%d", itube, half), + mWater, mRadiusCentralTore[itube], 0., mRWater, + -mAngle2[itube], 2. * mAngle2[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos2[itube] + mLpartial2[itube] / 2 * TMath::Sin(mAngle2[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle2[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos2[itube] + + mLpartial2[itube] / 2 * + TMath::Sin(mAngle2[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle2[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(waterTorusCentral, ivolume++, transformation); - TGeoVolume* waterTube3 = gGeoManager->MakeTube(Form("waterTube3%d_D2_H%d", 2, half), mWater, 0., mRWater, mLpartial2[itube] / 2.); + TGeoVolume* waterTube3 = + gGeoManager->MakeTube(Form("waterTube3%d_D2_H%d", 2, half), mWater, 0., + mRWater, mLpartial2[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle2[itube], 0.); - transformation = new TGeoCombiTrans(yPos2[itube], 0., -(mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos2[itube], 0., + -(mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube]), + rotation); cooling->AddNode(waterTube3, ivolume++, transformation); - TGeoVolume* waterTorus2 = gGeoManager->MakeTorus(Form("waterTorus2%d_D2_H%d", itube, half), mWater, mRadius2[itube], 0., mRWater, 0., mAngle2[itube]); + TGeoVolume* waterTorus2 = gGeoManager->MakeTorus( + Form("waterTorus2%d_D2_H%d", itube, half), mWater, mRadius2[itube], 0., + mRWater, 0., mAngle2[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube]), + rotation); cooling->AddNode(waterTorus2, ivolume++, transformation); - TGeoVolume* waterTube4 = gGeoManager->MakeTube(Form("waterTube4%d_D2_H%d", itube, half), mWater, 0., mRWater, mLWater2[itube] / 2.); - translation = new TGeoTranslation(mXPosition2[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube] / 2.)); + TGeoVolume* waterTube4 = + gGeoManager->MakeTube(Form("waterTube4%d_D2_H%d", itube, half), mWater, + 0., mRWater, mLWater2[itube] / 2.); + translation = new TGeoTranslation( + mXPosition2[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube] / 2.)); cooling->AddNode(waterTube4, ivolume++, translation); } - // **************************************************** Tube part ************************************************ - // ****************************** Four parameters mLwater2, mRadius2, mAngle2, mLpartial2 ************************ + // **************************************************** Tube part + // ************************************************ + // ****************************** Four parameters mLwater2, mRadius2, mAngle2, + // mLpartial2 ************************ for (Int_t itube = 0; itube < 3; itube++) { - TGeoVolume* pipeTube1 = gGeoManager->MakeTube(Form("pipeTube1%d_D2_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater2[itube] / 2.); - translation = new TGeoTranslation(mXPosition2[itube] - mHalfDiskGap, 0., mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube] / 2.); + TGeoVolume* pipeTube1 = + gGeoManager->MakeTube(Form("pipeTube1%d_D2_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater2[itube] / 2.); + translation = new TGeoTranslation(mXPosition2[itube] - mHalfDiskGap, 0., + mSupportXDimensions[2][0] / 2. + + mMoreLength - mLWater2[itube] / 2.); cooling->AddNode(pipeTube1, ivolume++, translation); - TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus(Form("pipeTorus1%d_D2_H%d", itube, half), mPipe, mRadius2[itube], mRWater, mRWater + mDRPipe, 0., mAngle2[itube]); + TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus( + Form("pipeTorus1%d_D2_H%d", itube, half), mPipe, mRadius2[itube], + mRWater, mRWater + mDRPipe, 0., mAngle2[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., + mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube], + rotation); cooling->AddNode(pipeTorus1, ivolume++, transformation); - TGeoVolume* pipeTube2 = gGeoManager->MakeTube(Form("pipeTube2%d_D2_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial2[itube] / 2.); + TGeoVolume* pipeTube2 = gGeoManager->MakeTube( + Form("pipeTube2%d_D2_H%d", itube, half), mPipe, mRWater, + mRWater + mDRPipe, mLpartial2[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle2[itube], 0.); - xPos2[itube] = mLWater2[itube] + mRadius2[itube] * TMath::Sin(mAngle2[itube] * TMath::DegToRad()) + mLpartial2[itube] / 2 * TMath::Cos(mAngle2[itube] * TMath::DegToRad()); - yPos2[itube] = mXPosition2[itube] - mHalfDiskGap + mRadius2[itube] * (1 - TMath::Cos(mAngle2[itube] * TMath::DegToRad())) + mLpartial2[itube] / 2 * TMath::Sin(mAngle2[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos2[itube], 0., mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube], rotation); + xPos2[itube] = + mLWater2[itube] + + mRadius2[itube] * TMath::Sin(mAngle2[itube] * TMath::DegToRad()) + + mLpartial2[itube] / 2 * TMath::Cos(mAngle2[itube] * TMath::DegToRad()); + yPos2[itube] = + mXPosition2[itube] - mHalfDiskGap + + mRadius2[itube] * (1 - TMath::Cos(mAngle2[itube] * TMath::DegToRad())) + + mLpartial2[itube] / 2 * TMath::Sin(mAngle2[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans( + yPos2[itube], 0., + mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube], rotation); cooling->AddNode(pipeTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube] - mLpartial2[itube] / 2 * TMath::Cos(mAngle2[itube] * TMath::DegToRad())) / TMath::Sin(mAngle2[itube] * TMath::DegToRad()); - TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus(Form("pipeTorusCentral%d_D2_H%d", itube, half), mPipe, mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, - -mAngle2[itube], 2. * mAngle2[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube] - + mLpartial2[itube] / 2 * + TMath::Cos(mAngle2[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle2[itube] * TMath::DegToRad()); + TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus( + Form("pipeTorusCentral%d_D2_H%d", itube, half), mPipe, + mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, -mAngle2[itube], + 2. * mAngle2[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos2[itube] + mLpartial2[itube] / 2 * TMath::Sin(mAngle2[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle2[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos2[itube] + + mLpartial2[itube] / 2 * + TMath::Sin(mAngle2[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle2[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(pipeTorusCentral, ivolume++, transformation); - TGeoVolume* pipeTube3 = gGeoManager->MakeTube(Form("pipeTube3%d_D2_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial2[itube] / 2.); + TGeoVolume* pipeTube3 = gGeoManager->MakeTube( + Form("pipeTube3%d_D2_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, + mLpartial2[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle2[itube], 0.); - transformation = new TGeoCombiTrans(yPos2[itube], 0., -(mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos2[itube], 0., + -(mSupportXDimensions[2][0] / 2. + mMoreLength - xPos2[itube]), + rotation); cooling->AddNode(pipeTube3, ivolume++, transformation); - TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus(Form("pipeTorus2%d_D2_H%d", itube, half), mPipe, mRadius2[itube], mRWater, mRWater + mDRPipe, 0., mAngle2[itube]); + TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus( + Form("pipeTorus2%d_D2_H%d", itube, half), mPipe, mRadius2[itube], + mRWater, mRWater + mDRPipe, 0., mAngle2[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius2[itube] + mXPosition2[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube]), + rotation); cooling->AddNode(pipeTorus2, ivolume++, transformation); - TGeoVolume* pipeTube4 = gGeoManager->MakeTube(Form("pipeTube4%d_D2_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater2[itube] / 2.); - translation = new TGeoTranslation(mXPosition2[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube] / 2.)); + TGeoVolume* pipeTube4 = + gGeoManager->MakeTube(Form("pipeTube4%d_D2_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater2[itube] / 2.); + translation = new TGeoTranslation( + mXPosition2[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[itube] / 2.)); cooling->AddNode(pipeTube4, ivolume++, translation); } // *********************************************************************************************** - Double_t deltaz = mHeatExchangerThickness - Geometry::sKaptonOnCarbonThickness * 4 - Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; + Double_t deltaz = mHeatExchangerThickness - + Geometry::sKaptonOnCarbonThickness * 4 - + Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; rotation = new TGeoRotation("rotation", -90., 90., 0.); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 3, transformation); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 4, transformation); - // **************************************** Carbon Plates **************************************** - + // **************************************** Carbon Plates + // **************************************** auto* carbonPlate = new TGeoVolumeAssembly(Form("carbonPlate_D2_H%d", half)); - auto* carbonBase2 = new TGeoBBox(Form("carbonBase2_D2_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength, - (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); - auto* t21 = new TGeoTranslation("t21", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + + auto& mftBaseParam = MFTBaseParam::Instance(); + Double_t mReducedX = 0.; + if (mftBaseParam.buildAlignment) { + mReducedX = 0.6; + } + + auto* carbonBase2 = new TGeoBBox( + Form("carbonBase2_D2_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength - mReducedX, + (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); + auto* t21 = new TGeoTranslation( + "t21", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); t21->RegisterYourself(); auto* holeCarbon2 = - new TGeoTubeSeg(Form("holeCarbon2_D2_H%d", half), 0., mRMin[disk], mCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeCarbon2_D2_H%d", half), 0., mRMin[disk], + mCarbonThickness + 0.000001, 0, 180.); auto* t22 = new TGeoTranslation("t22", 0., -mHalfDiskGap, 0.); t22->RegisterYourself(); auto* carbonhole2 = new TGeoSubtraction(carbonBase2, holeCarbon2, t21, t22); auto* cs2 = new TGeoCompositeShape(Form("Carbon2_D2_H%d", half), carbonhole2); - auto* carbonBaseWithHole2 = new TGeoVolume(Form("carbonBaseWithHole_D2_H%d", half), cs2, mCarbon); + auto* carbonBaseWithHole2 = + new TGeoVolume(Form("carbonBaseWithHole_D2_H%d", half), cs2, mCarbon); carbonBaseWithHole2->SetLineColor(kGray + 3); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - carbonPlate->AddNode(carbonBaseWithHole2, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + carbonPlate->AddNode(carbonBaseWithHole2, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t ty = mSupportYDimensions[disk][0]; @@ -2122,7 +3124,8 @@ void HeatExchanger::createHalfDisk2(Int_t half) ty += mSupportYDimensions[disk][ipart] / 2.; auto* partCarbon0 = - new TGeoBBox(Form("partCarbon0_D2_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., + new TGeoBBox(Form("partCarbon0_D2_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); auto* t = new TGeoTranslation("t", 0, ty + mHalfDiskGap, mZPlan[disk]); @@ -2133,18 +3136,24 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoBBox* notchCarbon2; TGeoTranslation* tnotch2; if (ipart == (mNPart[disk] - 1)) { - notchCarbon2 = new TGeoBBox(Form("notchCarbon2_D2_H%d", half), 2.3, 0.6, mCarbonThickness + 0.000001); - tnotch2 = new TGeoTranslation("tnotch2", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchCarbon2 = new TGeoBBox(Form("notchCarbon2_D2_H%d", half), 2.3, 0.6, + mCarbonThickness + 0.000001); + tnotch2 = new TGeoTranslation("tnotch2", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch2->RegisterYourself(); - partCarbonini = new TGeoSubtraction(partCarbon0, notchCarbon2, nullptr, tnotch2); - auto* carbinit = new TGeoCompositeShape(Form("carbinit%d_D2_H%d", 0, half), partCarbonini); - partCarbonNotch = new TGeoVolume(Form("partCarbonNotch_D2_H%d_%d", half, ipart), carbinit, mCarbon); + partCarbonini = + new TGeoSubtraction(partCarbon0, notchCarbon2, nullptr, tnotch2); + auto* carbinit = new TGeoCompositeShape( + Form("carbinit%d_D2_H%d", 0, half), partCarbonini); + partCarbonNotch = new TGeoVolume( + Form("partCarbonNotch_D2_H%d_%d", half, ipart), carbinit, mCarbon); partCarbonNotch->SetLineColor(kGray + 3); carbonPlate->AddNode(partCarbonNotch, ipart, t); } if (ipart < (mNPart[disk] - 1)) { - auto* partCarbon = new TGeoVolume(Form("partCarbonNotch_D2_H%d_%d", half, ipart), partCarbon0, mCarbon); + auto* partCarbon = new TGeoVolume( + Form("partCarbonNotch_D2_H%d_%d", half, ipart), partCarbon0, mCarbon); partCarbon->SetLineColor(kGray + 3); carbonPlate->AddNode(partCarbon, ipart, t); } @@ -2158,36 +3167,54 @@ void HeatExchanger::createHalfDisk2(Int_t half) transformation = new TGeoCombiTrans(0., 0., -deltaz / 2., rotation); mHalfDisk->AddNode(carbonPlate, 4, transformation); - // **************************************** Glue Bwtween Carbon Plate and Rohacell Plate **************************************** + // **************************************** Glue Bwtween Carbon Plate and + // Rohacell Plate **************************************** TGeoMedium* mGlueRohacellCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueRohacellCarbon = new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); - auto* glueRohacellCarbonBase0 = new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sGlueRohacellCarbonThickness); - - auto* translation_gluRC01 = new TGeoTranslation("translation_gluRC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueRohacellCarbon = + new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); + auto* glueRohacellCarbonBase0 = + new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., + Geometry::sGlueRohacellCarbonThickness); + + auto* translation_gluRC01 = new TGeoTranslation( + "translation_gluRC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluRC01->RegisterYourself(); - auto* translation_gluRC02 = new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluRC02 = + new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); translation_gluRC02->RegisterYourself(); - auto* holeglueRohacellCarbon0 = - new TGeoTubeSeg(Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); + auto* holeglueRohacellCarbon0 = new TGeoTubeSeg( + Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); - auto* glueRohacellCarbonhole0 = new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, translation_gluRC01, translation_gluRC02); - auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), glueRohacellCarbonhole0); + auto* glueRohacellCarbonhole0 = + new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, + translation_gluRC01, translation_gluRC02); + auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), + glueRohacellCarbonhole0); - auto* glueRohacellCarbonBaseWithHole0 = new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, mGlueRohacellCarbon); + auto* glueRohacellCarbonBaseWithHole0 = + new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, + mGlueRohacellCarbon); glueRohacellCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGRC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGRC += mSupportYDimensions[disk][ipart] / 2.; - auto* partGlueRohacellCarbon = new TGeoBBox(Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sGlueRohacellCarbonThickness); + auto* partGlueRohacellCarbon = + new TGeoBBox(Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sGlueRohacellCarbonThickness); //======== notch of the glue, fm === TGeoVolume* partGlueCarbonNotch; @@ -2195,13 +3222,20 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoBBox* notchGlueCarbon2; TGeoTranslation* tnotch2; if (ipart == (mNPart[disk] - 1)) { - notchGlueCarbon2 = new TGeoBBox(Form("notchGlueCarbon2_D2_H%d", half), 2.3, 0.6, Geometry::sGlueRohacellCarbonThickness + 0.000001); - tnotch2 = new TGeoTranslation("tnotch2", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchGlueCarbon2 = + new TGeoBBox(Form("notchGlueCarbon2_D2_H%d", half), 2.3, 0.6, + Geometry::sGlueRohacellCarbonThickness + 0.000001); + tnotch2 = new TGeoTranslation("tnotch2", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch2->RegisterYourself(); - partGlueCarbonini = new TGeoSubtraction(partGlueRohacellCarbon, notchGlueCarbon2, nullptr, tnotch2); - auto* gluecarbinit = new TGeoCompositeShape(Form("gluecarbinit%d_D2_H%d", 0, half), partGlueCarbonini); - partGlueCarbonNotch = new TGeoVolume(Form("partGlueCarbonNotch_D2_H%d_%d", half, ipart), gluecarbinit, mGlueRohacellCarbon); + partGlueCarbonini = new TGeoSubtraction( + partGlueRohacellCarbon, notchGlueCarbon2, nullptr, tnotch2); + auto* gluecarbinit = new TGeoCompositeShape( + Form("gluecarbinit%d_D2_H%d", 0, half), partGlueCarbonini); + partGlueCarbonNotch = + new TGeoVolume(Form("partGlueCarbonNotch_D2_H%d_%d", half, ipart), + gluecarbinit, mGlueRohacellCarbon); partGlueCarbonNotch->SetLineColor(kGray + 3); } @@ -2211,7 +3245,9 @@ void HeatExchanger::createHalfDisk2(Int_t half) glueRohacellCarbon->AddNode(partGlueCarbonNotch, ipart, t); } if (ipart < (mNPart[disk] - 1)) { - auto* partGlueCarbon = new TGeoVolume(Form("partGlueCarbon_D2_H%d_%d", half, ipart), partGlueRohacellCarbon, mGlueRohacellCarbon); + auto* partGlueCarbon = + new TGeoVolume(Form("partGlueCarbon_D2_H%d_%d", half, ipart), + partGlueRohacellCarbon, mGlueRohacellCarbon); partGlueCarbon->SetLineColor(kGreen); glueRohacellCarbon->AddNode(partGlueCarbon, ipart, t); } @@ -2220,42 +3256,64 @@ void HeatExchanger::createHalfDisk2(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness), rotation); + transformation = new TGeoCombiTrans(0., 0., + -(deltaz / 2. - mCarbonThickness - + Geometry::sGlueRohacellCarbonThickness), + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 4, transformation); - // **************************************** Kapton on Carbon Plate **************************************** + // **************************************** Kapton on Carbon Plate + // **************************************** TGeoMedium* mKaptonOnCarbon = gGeoManager->GetMedium("MFT_Kapton$"); - auto* kaptonOnCarbon = new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); - auto* kaptonOnCarbonBase0 = new TGeoBBox(Form("kaptonOnCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength, - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); + auto* kaptonOnCarbon = + new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); + auto* kaptonOnCarbonBase0 = new TGeoBBox( + Form("kaptonOnCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); - auto* translation_KC01 = new TGeoTranslation("translation_KC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* translation_KC01 = new TGeoTranslation( + "translation_KC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_KC01->RegisterYourself(); - auto* translation_KC02 = new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); + auto* translation_KC02 = + new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); translation_KC02->RegisterYourself(); auto* holekaptonOnCarbon0 = - new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); - auto* kaptonOnCarbonhole0 = new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, translation_KC01, translation_KC02); - auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), kaptonOnCarbonhole0); - auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume(Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); + auto* kaptonOnCarbonhole0 = + new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, + translation_KC01, translation_KC02); + auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), + kaptonOnCarbonhole0); + auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume( + Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); kaptonOnCarbonBaseWithHole0->SetLineColor(kMagenta); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyKC += mSupportYDimensions[disk][ipart] / 2.; - auto* partKaptonOnCarbonBase = new TGeoBBox(Form("partKaptonOnCarbon_D0_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonOnCarbonThickness); + auto* partKaptonOnCarbonBase = + new TGeoBBox(Form("partKaptonOnCarbon_D0_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sKaptonOnCarbonThickness); //======== notch of the kapton, fm === TGeoVolume* partKaptonNotch; @@ -2263,13 +3321,20 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoBBox* notchKapton2; TGeoTranslation* tnotch2; if (ipart == (mNPart[disk] - 1)) { - notchKapton2 = new TGeoBBox(Form("notchKapton2_D2_H%d", half), 2.3, 0.6, Geometry::sKaptonOnCarbonThickness + 0.000001); - tnotch2 = new TGeoTranslation("tnotch2", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchKapton2 = + new TGeoBBox(Form("notchKapton2_D2_H%d", half), 2.3, 0.6, + Geometry::sKaptonOnCarbonThickness + 0.000001); + tnotch2 = new TGeoTranslation("tnotch2", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch2->RegisterYourself(); - partKaptonini = new TGeoSubtraction(partKaptonOnCarbonBase, notchKapton2, nullptr, tnotch2); - auto* kaptinit = new TGeoCompositeShape(Form("kaptinit%d_D2_H%d", 0, half), partKaptonini); - partKaptonNotch = new TGeoVolume(Form("partKaptonNotch_D2_H%d_%d", half, ipart), kaptinit, mKaptonOnCarbon); + partKaptonini = new TGeoSubtraction(partKaptonOnCarbonBase, notchKapton2, + nullptr, tnotch2); + auto* kaptinit = new TGeoCompositeShape( + Form("kaptinit%d_D2_H%d", 0, half), partKaptonini); + partKaptonNotch = + new TGeoVolume(Form("partKaptonNotch_D2_H%d_%d", half, ipart), + kaptinit, mKaptonOnCarbon); } auto* t = new TGeoTranslation("t", 0, tyKC + mHalfDiskGap, mZPlan[disk]); @@ -2278,7 +3343,9 @@ void HeatExchanger::createHalfDisk2(Int_t half) kaptonOnCarbon->AddNode(partKaptonNotch, ipart, t); } if (ipart < (mNPart[disk] - 1)) { - auto* partKapton = new TGeoVolume(Form("partKapton_D2_H%d_%d", half, ipart), partKaptonOnCarbonBase, mKaptonOnCarbon); + auto* partKapton = + new TGeoVolume(Form("partKapton_D2_H%d_%d", half, ipart), + partKaptonOnCarbonBase, mKaptonOnCarbon); partKapton->SetLineColor(kMagenta); kaptonOnCarbon->AddNode(partKapton, ipart, t); } @@ -2287,41 +3354,64 @@ void HeatExchanger::createHalfDisk2(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2, + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2), + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 4, transformation); - // **************************************** Kapton glue on the carbon plate **************************************** + // **************************************** Kapton glue on the carbon plate + // **************************************** TGeoMedium* mGlueKaptonCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueKaptonCarbon = new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); - auto* glueKaptonCarbonBase0 = new TGeoBBox(Form("glueKaptonCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); - - auto* translation_gluKC01 = new TGeoTranslation("translation_gluKC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueKaptonCarbon = + new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); + auto* glueKaptonCarbonBase0 = new TGeoBBox( + Form("glueKaptonCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); + + auto* translation_gluKC01 = new TGeoTranslation( + "translation_gluKC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluKC01->RegisterYourself(); - auto* translation_gluKC02 = new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluKC02 = + new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); translation_gluKC02->RegisterYourself(); - auto* holeglueKaptonCarbon0 = - new TGeoTubeSeg(Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); + auto* holeglueKaptonCarbon0 = new TGeoTubeSeg( + Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); - auto* glueKaptonCarbonhole0 = new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, translation_gluKC01, translation_gluKC02); - auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), glueKaptonCarbonhole0); - auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume(Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); + auto* glueKaptonCarbonhole0 = + new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, + translation_gluKC01, translation_gluKC02); + auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), + glueKaptonCarbonhole0); + auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume( + Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); glueKaptonCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGKC += mSupportYDimensions[disk][ipart] / 2.; - auto* partGlueKaptonCarbon = new TGeoBBox(Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); + auto* partGlueKaptonCarbon = new TGeoBBox( + Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); //======== notch of the glue, fm === TGeoVolume* partGlueKaptonNotch; @@ -2329,13 +3419,20 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoBBox* notchGlueKapton2; TGeoTranslation* tnotch2; if (ipart == (mNPart[disk] - 1)) { - notchGlueKapton2 = new TGeoBBox(Form("notchGlueKapton2_D2_H%d", half), 2.3, 0.6, Geometry::sKaptonGlueThickness + 0.000001); - tnotch2 = new TGeoTranslation("tnotch2", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchGlueKapton2 = + new TGeoBBox(Form("notchGlueKapton2_D2_H%d", half), 2.3, 0.6, + Geometry::sKaptonGlueThickness + 0.000001); + tnotch2 = new TGeoTranslation("tnotch2", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch2->RegisterYourself(); - partGlueKaptonini = new TGeoSubtraction(partGlueKaptonCarbon, notchGlueKapton2, nullptr, tnotch2); - auto* gluekaptinit = new TGeoCompositeShape(Form("gluekaptinit%d_D2_H%d", 0, half), partGlueKaptonini); - partGlueKaptonNotch = new TGeoVolume(Form("partGlueKaptonNotch_D2_H%d_%d", half, ipart), gluekaptinit, mGlueKaptonCarbon); + partGlueKaptonini = new TGeoSubtraction( + partGlueKaptonCarbon, notchGlueKapton2, nullptr, tnotch2); + auto* gluekaptinit = new TGeoCompositeShape( + Form("gluekaptinit%d_D2_H%d", 0, half), partGlueKaptonini); + partGlueKaptonNotch = + new TGeoVolume(Form("partGlueKaptonNotch_D2_H%d_%d", half, ipart), + gluekaptinit, mGlueKaptonCarbon); } auto* t = new TGeoTranslation("t", 0, tyGKC + mHalfDiskGap, mZPlan[disk]); @@ -2345,7 +3442,9 @@ void HeatExchanger::createHalfDisk2(Int_t half) glueKaptonCarbon->AddNode(partGlueKaptonNotch, ipart, t); } if (ipart < (mNPart[disk] - 1)) { - auto* partGlueKapton = new TGeoVolume(Form("partGlueKapton_D2_H%d_%d", half, ipart), partGlueKaptonCarbon, mGlueKaptonCarbon); + auto* partGlueKapton = + new TGeoVolume(Form("partGlueKapton_D2_H%d_%d", half, ipart), + partGlueKaptonCarbon, mGlueKaptonCarbon); partGlueKapton->SetLineColor(kGreen); glueKaptonCarbon->AddNode(partGlueKapton, ipart, t); } @@ -2354,19 +3453,29 @@ void HeatExchanger::createHalfDisk2(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 4, transformation); - // **************************************** Rohacell Plate **************************************** - auto* rohacellPlate = new TGeoVolumeAssembly(Form("rohacellPlate_D2_H%d", half)); - auto* rohacellBase2 = new TGeoBBox(Form("rohacellBase2_D2_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); + // **************************************** Rohacell Plate + // **************************************** + auto* rohacellPlate = + new TGeoVolumeAssembly(Form("rohacellPlate_D2_H%d", half)); + auto* rohacellBase2 = new TGeoBBox( + Form("rohacellBase2_D2_H%d", half), (mSupportXDimensions[disk][0]) / 2., + (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); auto* holeRohacell2 = - new TGeoTubeSeg(Form("holeRohacell2_D2_H%d", half), 0., mRMin[disk], mRohacellThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeRohacell2_D2_H%d", half), 0., mRMin[disk], + mRohacellThickness + 0.000001, 0, 180.); - // **************************************** GROOVES ************************************************* + // **************************************** GROOVES + // ************************************************* Double_t diameter = 0.21; // groove diameter Double_t epsilon = 0.06; // outside shift of the goove Int_t iCount = 0; @@ -2378,20 +3487,31 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoCompositeShape* rohacellGroove[300]; for (Int_t igroove = 0; igroove < 3; igroove++) { - grooveTube[0][igroove] = new TGeoTube("linear", 0., diameter, mLWater2[igroove] / 2.); - grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius2[igroove], 0., diameter, 0., mAngle2[igroove]); - grooveTube[2][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial2[igroove] / 2.); - grooveTorus[3][igroove] = new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, -mAngle2[igroove], 2. * mAngle2[igroove]); - grooveTube[4][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial2[igroove] / 2.); - grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius2[igroove], 0., diameter, 0., mAngle2[igroove]); - grooveTube[6][igroove] = new TGeoTube("linear", 0., diameter, mLWater2[igroove] / 2.); + grooveTube[0][igroove] = + new TGeoTube("linear", 0., diameter, mLWater2[igroove] / 2.); + grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius2[igroove], 0., + diameter, 0., mAngle2[igroove]); + grooveTube[2][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial2[igroove] / 2.); + grooveTorus[3][igroove] = + new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, + -mAngle2[igroove], 2. * mAngle2[igroove]); + grooveTube[4][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial2[igroove] / 2.); + grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius2[igroove], 0., + diameter, 0., mAngle2[igroove]); + grooveTube[6][igroove] = + new TGeoTube("linear", 0., diameter, mLWater2[igroove] / 2.); } // Rotation matrix TGeoRotation* rotationLinear = new TGeoRotation("rotation", -90., 90., 0.); - TGeoRotation* rotationSideTorusL = new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); - TGeoRotation* rotationSideTorusR = new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); - TGeoRotation* rotationCentralTorus = new TGeoRotation("rotationCentralTorus", 90., 0., 0.); + TGeoRotation* rotationSideTorusL = + new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); + TGeoRotation* rotationSideTorusR = + new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); + TGeoRotation* rotationCentralTorus = + new TGeoRotation("rotationCentralTorus", 90., 0., 0.); TGeoRotation* rotationTiltedLinearR; TGeoRotation* rotationTiltedLinearL; @@ -2399,52 +3519,96 @@ void HeatExchanger::createHalfDisk2(Int_t half) if (Geometry::sGrooves == 1) { for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 3; igroove++) { // 3 grooves - mPosition[igroove] = mXPosition2[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; + mPosition[igroove] = mXPosition2[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength - mLWater2[igroove] / 2., mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength - + mLWater2[igroove] / 2., + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[0] = new TGeoSubtraction(rohacellBase2, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[0] = new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[0]); + rohacellBaseGroove[0] = + new TGeoSubtraction(rohacellBase2, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[0] = + new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[0]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength - mLWater2[igroove], mRadius2[igroove] + mXPosition2[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength - + mLWater2[igroove], + mRadius2[igroove] + mXPosition2[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle2[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength - xPos2[igroove], yPos2[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle2[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength - + xPos2[igroove], + yPos2[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., yPos2[igroove] + mLpartial2[igroove] / 2 * TMath::Sin(mAngle2[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle2[igroove] * TMath::DegToRad()) - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + yPos2[igroove] + + mLpartial2[igroove] / 2 * + TMath::Sin(mAngle2[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle2[igroove] * TMath::DegToRad()) - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle2[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength - xPos2[igroove]), yPos2[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle2[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength - + xPos2[igroove]), + yPos2[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength - mLWater2[igroove]), mRadius2[igroove] + mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength - + mLWater2[igroove]), + mRadius2[igroove] + mPosition[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength - mLWater2[igroove] / 2.), mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength - + mLWater2[igroove] / 2.), + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, + transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, + transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -2459,16 +3623,20 @@ void HeatExchanger::createHalfDisk2(Int_t half) rohacellBase = new TGeoSubtraction(rohacellBase2, holeRohacell2, t21, t22); } if (Geometry::sGrooves == 1) { - rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell2, t21, t22); + rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], + holeRohacell2, t21, t22); } - auto* rh2 = new TGeoCompositeShape(Form("rohacellTore%d_D2_H%d", 0, half), rohacellBase); - auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D2_H%d", half), rh2, mRohacell); + auto* rh2 = new TGeoCompositeShape(Form("rohacellTore%d_D2_H%d", 0, half), + rohacellBase); + auto* rohacellBaseWithHole = + new TGeoVolume(Form("rohacellBaseWithHole_D2_H%d", half), rh2, mRohacell); TGeoVolume* partRohacell; rohacellBaseWithHole->SetLineColor(kGray); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - rohacellPlate->AddNode(rohacellBaseWithHole, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + rohacellPlate->AddNode(rohacellBaseWithHole, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); ty = mSupportYDimensions[disk][0]; @@ -2479,74 +3647,129 @@ void HeatExchanger::createHalfDisk2(Int_t half) //=========================================================================================================== //=========================================================================================================== auto* partRohacell0 = - new TGeoBBox(Form("rohacellBase0_D2_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., + new TGeoBBox(Form("rohacellBase0_D2_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., mSupportYDimensions[disk][ipart] / 2., mRohacellThickness); if (Geometry::sGrooves == 1) { - // ***************** Creating grooves for the other parts of the rohacell plate ********************** + // ***************** Creating grooves for the other parts of the rohacell + // plate ********************** Double_t mShift; for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 3; igroove++) { // 3 grooves if (ipart == 1) { - mPosition[ipart] = mXPosition2[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; + mPosition[ipart] = + mXPosition2[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; mShift = -mSupportYDimensions[disk][ipart - 1]; }; if (ipart == 2) { - mPosition[ipart] = mXPosition2[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; + mPosition[ipart] = + mXPosition2[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; }; if (ipart == 3) { - mPosition[ipart] = mXPosition2[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; + mPosition[ipart] = + mXPosition2[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; }; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength - mLWater2[igroove] / 2., mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[disk][0] / 2. + mMoreLength - + mLWater2[igroove] / 2., + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellBaseGroove[iCount] = + new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[iCount]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[2][0] / 2. + mMoreLength - mLWater2[igroove], mPosition[ipart] + mRadius2[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[2][0] / 2. + mMoreLength - + mLWater2[igroove], + mPosition[ipart] + mRadius2[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle2[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + mMoreLength - xPos2[igroove], yPos2[igroove] + mShift - mHalfDiskGap - mSupportYDimensions[disk][ipart] / 2., iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle2[igroove], 90., 0.); + transfo[ip][igroove] = + new TGeoCombiTrans(mSupportXDimensions[disk][0] / 2. + + mMoreLength - xPos2[igroove], + yPos2[igroove] + mShift - mHalfDiskGap - + mSupportYDimensions[disk][ipart] / 2., + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., mPosition[ipart] + yPos2[igroove] + mLpartial2[igroove] / 2 * TMath::Sin(mAngle2[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle2[igroove] * TMath::DegToRad()) - mXPosition2[igroove], - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + mPosition[ipart] + yPos2[igroove] + + mLpartial2[igroove] / 2 * + TMath::Sin(mAngle2[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle2[igroove] * TMath::DegToRad()) - + mXPosition2[igroove], + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle2[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength - xPos2[igroove]), yPos2[igroove] + mPosition[ipart] - mXPosition2[igroove], - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle2[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength - + xPos2[igroove]), + yPos2[igroove] + mPosition[ipart] - mXPosition2[igroove], + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength - mLWater2[igroove]), mRadius2[igroove] + mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength - + mLWater2[igroove]), + mRadius2[igroove] + mPosition[ipart], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[disk][0] / 2. + mMoreLength - mLWater2[igroove] / 2.), mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[disk][0] / 2. + mMoreLength - + mLWater2[igroove] / 2.), + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], + nullptr, transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell2Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -2564,37 +3787,55 @@ void HeatExchanger::createHalfDisk2(Int_t half) TGeoBBox* notchRohacell22; TGeoTranslation* tnotch22; if (ipart == (mNPart[disk] - 1)) { - notchRohacell21 = new TGeoBBox(Form("notchRohacell21_D2_H%d", half), 2.3, 0.6, mRohacellThickness + 0.000001); - tnotch21 = new TGeoTranslation("tnotch21", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchRohacell21 = new TGeoBBox(Form("notchRohacell21_D2_H%d", half), 2.3, + 0.6, mRohacellThickness + 0.000001); + tnotch21 = new TGeoTranslation("tnotch21", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch21->RegisterYourself(); - notchRohacell22 = new TGeoBBox(Form("notchRohacell22_D2_H%d", half), 1.1, 0.6, mRohacellThickness + 0.000001); - tnotch22 = new TGeoTranslation("tnotch22", 0., mSupportYDimensions[disk][ipart] / 2. - 0.4, 0.); + notchRohacell22 = new TGeoBBox(Form("notchRohacell22_D2_H%d", half), 1.1, + 0.6, mRohacellThickness + 0.000001); + tnotch22 = new TGeoTranslation( + "tnotch22", 0., mSupportYDimensions[disk][ipart] / 2. - 0.4, 0.); tnotch22->RegisterYourself(); } //============================================================= if (Geometry::sGrooves == 0) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini1 = new TGeoSubtraction(partRohacell0, notchRohacell21, nullptr, tnotch21); - auto* rhinit1 = new TGeoCompositeShape(Form("rhinit1%d_D2_H%d", 0, half), partRohacellini1); - partRohacellini2 = new TGeoSubtraction(rhinit1, notchRohacell22, nullptr, tnotch22); - auto* rhinit2 = new TGeoCompositeShape(Form("rhinit2%d_D2_H%d", 0, half), partRohacellini2); - partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), rhinit2, mRohacell); + partRohacellini1 = new TGeoSubtraction(partRohacell0, notchRohacell21, + nullptr, tnotch21); + auto* rhinit1 = new TGeoCompositeShape( + Form("rhinit1%d_D2_H%d", 0, half), partRohacellini1); + partRohacellini2 = + new TGeoSubtraction(rhinit1, notchRohacell22, nullptr, tnotch22); + auto* rhinit2 = new TGeoCompositeShape( + Form("rhinit2%d_D2_H%d", 0, half), partRohacellini2); + partRohacell = new TGeoVolume( + Form("partRohacelli_D2_H%d_%d", half, ipart), rhinit2, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), partRohacell0, mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), + partRohacell0, mRohacell); } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini1 = new TGeoSubtraction(rohacellGroove[iCount - 1], notchRohacell21, nullptr, tnotch21); - auto* rhinit1 = new TGeoCompositeShape(Form("rhinit1%d_D2_H%d", 0, half), partRohacellini1); - partRohacellini2 = new TGeoSubtraction(rhinit1, notchRohacell22, nullptr, tnotch22); - auto* rhinit2 = new TGeoCompositeShape(Form("rhinit2%d_D2_H%d", 0, half), partRohacellini2); - partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), rhinit2, mRohacell); + partRohacellini1 = new TGeoSubtraction( + rohacellGroove[iCount - 1], notchRohacell21, nullptr, tnotch21); + auto* rhinit1 = new TGeoCompositeShape( + Form("rhinit1%d_D2_H%d", 0, half), partRohacellini1); + partRohacellini2 = + new TGeoSubtraction(rhinit1, notchRohacell22, nullptr, tnotch22); + auto* rhinit2 = new TGeoCompositeShape( + Form("rhinit2%d_D2_H%d", 0, half), partRohacellini2); + partRohacell = new TGeoVolume( + Form("partRohacelli_D2_H%d_%d", half, ipart), rhinit2, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D2_H%d_%d", half, ipart), + rohacellGroove[iCount - 1], mRohacell); } } @@ -2604,10 +3845,13 @@ void HeatExchanger::createHalfDisk2(Int_t half) rohacellPlate->AddNode(partRohacell, ipart, t); ty += mSupportYDimensions[disk][ipart] / 2.; - //========== insert to locate the rohacell plate compare to the disk support ============= + //========== insert to locate the rohacell plate compare to the disk support + //============= if (ipart == (mNPart[disk] - 1)) { TGeoTranslation* tinsert2; - TGeoVolume* insert2 = gGeoManager->MakeBox(Form("insert2_H%d_%d", half, ipart), mPeek, 1.0, 0.40 / 2., mRohacellThickness); + TGeoVolume* insert2 = + gGeoManager->MakeBox(Form("insert2_H%d_%d", half, ipart), mPeek, 1.0, + 0.40 / 2., mRohacellThickness); Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap - 0.80; for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; @@ -2653,132 +3897,249 @@ void HeatExchanger::createHalfDisk3(Int_t half) TGeoRotation* rotation = nullptr; TGeoCombiTrans* transformation = nullptr; - // **************************************** Water part **************************************** - // ********************** Four parameters mLwater3, mRadius3, mAngle3, mLpartial3 ************* + // **************************************** Water part + // **************************************** + // ********************** Four parameters mLwater3, mRadius3, mAngle3, + // mLpartial3 ************* Double_t ivolume = 300; // offset chamber 3 Double_t mRadiusCentralTore[4]; Double_t xPos3[4]; Double_t yPos3[4]; for (Int_t itube = 0; itube < 4; itube++) { - TGeoVolume* waterTube1 = gGeoManager->MakeTube(Form("waterTube1%d_D3_H%d", itube, half), mWater, 0., mRWater, mLWater3[itube] / 2.); - translation = new TGeoTranslation(mXPosition3[itube] - mHalfDiskGap, 0., mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube] / 2.); + TGeoVolume* waterTube1 = + gGeoManager->MakeTube(Form("waterTube1%d_D3_H%d", itube, half), mWater, + 0., mRWater, mLWater3[itube] / 2.); + translation = new TGeoTranslation(mXPosition3[itube] - mHalfDiskGap, 0., + mSupportXDimensions[3][0] / 2. + + mMoreLength - mLWater3[itube] / 2.); cooling->AddNode(waterTube1, ivolume++, translation); - TGeoVolume* waterTorus1 = gGeoManager->MakeTorus(Form("waterTorus1%d_D3_H%d", itube, half), mWater, mRadius3[itube], 0., mRWater, 0., mAngle3[itube]); + TGeoVolume* waterTorus1 = gGeoManager->MakeTorus( + Form("waterTorus1%d_D3_H%d", itube, half), mWater, mRadius3[itube], 0., + mRWater, 0., mAngle3[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., + mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube], + rotation); cooling->AddNode(waterTorus1, ivolume++, transformation); - TGeoVolume* waterTube2 = gGeoManager->MakeTube(Form("waterTube2%d_D3_H%d", itube, half), mWater, 0., mRWater, mLpartial3[itube] / 2.); + TGeoVolume* waterTube2 = + gGeoManager->MakeTube(Form("waterTube2%d_D3_H%d", itube, half), mWater, + 0., mRWater, mLpartial3[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle3[itube], 0.); - xPos3[itube] = mLWater3[itube] + mRadius3[itube] * TMath::Sin(mAngle3[itube] * TMath::DegToRad()) + mLpartial3[itube] / 2 * TMath::Cos(mAngle3[itube] * TMath::DegToRad()); - yPos3[itube] = mXPosition3[itube] - mHalfDiskGap + mRadius3[itube] * (1 - TMath::Cos(mAngle3[itube] * TMath::DegToRad())) + mLpartial3[itube] / 2 * TMath::Sin(mAngle3[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos3[itube], 0., mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube], rotation); + xPos3[itube] = + mLWater3[itube] + + mRadius3[itube] * TMath::Sin(mAngle3[itube] * TMath::DegToRad()) + + mLpartial3[itube] / 2 * TMath::Cos(mAngle3[itube] * TMath::DegToRad()); + yPos3[itube] = + mXPosition3[itube] - mHalfDiskGap + + mRadius3[itube] * (1 - TMath::Cos(mAngle3[itube] * TMath::DegToRad())) + + mLpartial3[itube] / 2 * TMath::Sin(mAngle3[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans( + yPos3[itube], 0., + mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube], rotation); cooling->AddNode(waterTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube] - mLpartial3[itube] / 2 * TMath::Cos(mAngle3[itube] * TMath::DegToRad())) / TMath::Sin(mAngle3[itube] * TMath::DegToRad()); - TGeoVolume* waterTorusCentral = gGeoManager->MakeTorus(Form("waterTorusCentral%d_D3_H%d", itube, half), mWater, mRadiusCentralTore[itube], 0., mRWater, - -mAngle3[itube], 2. * mAngle3[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube] - + mLpartial3[itube] / 2 * + TMath::Cos(mAngle3[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle3[itube] * TMath::DegToRad()); + TGeoVolume* waterTorusCentral = + gGeoManager->MakeTorus(Form("waterTorusCentral%d_D3_H%d", itube, half), + mWater, mRadiusCentralTore[itube], 0., mRWater, + -mAngle3[itube], 2. * mAngle3[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos3[itube] + mLpartial3[itube] / 2 * TMath::Sin(mAngle3[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle3[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos3[itube] + + mLpartial3[itube] / 2 * + TMath::Sin(mAngle3[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle3[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(waterTorusCentral, ivolume++, transformation); - TGeoVolume* waterTube3 = gGeoManager->MakeTube(Form("waterTube3%d_D3_H%d", 2, half), mWater, 0., mRWater, mLpartial3[itube] / 2.); + TGeoVolume* waterTube3 = + gGeoManager->MakeTube(Form("waterTube3%d_D3_H%d", 2, half), mWater, 0., + mRWater, mLpartial3[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle3[itube], 0.); - transformation = new TGeoCombiTrans(yPos3[itube], 0., -(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos3[itube], 0., + -(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube]), + rotation); cooling->AddNode(waterTube3, ivolume++, transformation); - TGeoVolume* waterTorus2 = gGeoManager->MakeTorus(Form("waterTorus2%d_D3_H%d", itube, half), mWater, mRadius3[itube], 0., mRWater, 0., mAngle3[itube]); + TGeoVolume* waterTorus2 = gGeoManager->MakeTorus( + Form("waterTorus2%d_D3_H%d", itube, half), mWater, mRadius3[itube], 0., + mRWater, 0., mAngle3[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube]), + rotation); cooling->AddNode(waterTorus2, ivolume++, transformation); - TGeoVolume* waterTube4 = gGeoManager->MakeTube(Form("waterTube4%d_D3_H%d", itube, half), mWater, 0., mRWater, mLWater3[itube] / 2.); - translation = new TGeoTranslation(mXPosition3[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube] / 2.)); + TGeoVolume* waterTube4 = + gGeoManager->MakeTube(Form("waterTube4%d_D3_H%d", itube, half), mWater, + 0., mRWater, mLWater3[itube] / 2.); + translation = new TGeoTranslation( + mXPosition3[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube] / 2.)); cooling->AddNode(waterTube4, ivolume++, translation); } - // **************************************************** Tube part ************************************************ - // ****************************** Four parameters mLwater3, mRadius3, mAngle3, mLpartial3 ************************ + // **************************************************** Tube part + // ************************************************ + // ****************************** Four parameters mLwater3, mRadius3, mAngle3, + // mLpartial3 ************************ for (Int_t itube = 0; itube < 4; itube++) { - TGeoVolume* pipeTube1 = gGeoManager->MakeTube(Form("pipeTube1%d_D3_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater3[itube] / 2.); - translation = new TGeoTranslation(mXPosition3[itube] - mHalfDiskGap, 0., mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube] / 2.); + TGeoVolume* pipeTube1 = + gGeoManager->MakeTube(Form("pipeTube1%d_D3_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater3[itube] / 2.); + translation = new TGeoTranslation(mXPosition3[itube] - mHalfDiskGap, 0., + mSupportXDimensions[3][0] / 2. + + mMoreLength - mLWater3[itube] / 2.); cooling->AddNode(pipeTube1, ivolume++, translation); - TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus(Form("pipeTorus1%d_D3_H%d", itube, half), mPipe, mRadius3[itube], mRWater, mRWater + mDRPipe, 0., mAngle3[itube]); + TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus( + Form("pipeTorus1%d_D3_H%d", itube, half), mPipe, mRadius3[itube], + mRWater, mRWater + mDRPipe, 0., mAngle3[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., + mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube], + rotation); cooling->AddNode(pipeTorus1, ivolume++, transformation); - TGeoVolume* pipeTube2 = gGeoManager->MakeTube(Form("pipeTube2%d_D3_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial3[itube] / 2.); + TGeoVolume* pipeTube2 = gGeoManager->MakeTube( + Form("pipeTube2%d_D3_H%d", itube, half), mPipe, mRWater, + mRWater + mDRPipe, mLpartial3[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle3[itube], 0.); - xPos3[itube] = mLWater3[itube] + mRadius3[itube] * TMath::Sin(mAngle3[itube] * TMath::DegToRad()) + mLpartial3[itube] / 2 * TMath::Cos(mAngle3[itube] * TMath::DegToRad()); - yPos3[itube] = mXPosition3[itube] - mHalfDiskGap + mRadius3[itube] * (1 - TMath::Cos(mAngle3[itube] * TMath::DegToRad())) + mLpartial3[itube] / 2 * TMath::Sin(mAngle3[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos3[itube], 0., mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube], rotation); + xPos3[itube] = + mLWater3[itube] + + mRadius3[itube] * TMath::Sin(mAngle3[itube] * TMath::DegToRad()) + + mLpartial3[itube] / 2 * TMath::Cos(mAngle3[itube] * TMath::DegToRad()); + yPos3[itube] = + mXPosition3[itube] - mHalfDiskGap + + mRadius3[itube] * (1 - TMath::Cos(mAngle3[itube] * TMath::DegToRad())) + + mLpartial3[itube] / 2 * TMath::Sin(mAngle3[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans( + yPos3[itube], 0., + mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube], rotation); cooling->AddNode(pipeTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube] - mLpartial3[itube] / 2 * TMath::Cos(mAngle3[itube] * TMath::DegToRad())) / TMath::Sin(mAngle3[itube] * TMath::DegToRad()); - TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus(Form("pipeTorusCentral%d_D3_H%d", itube, half), mPipe, mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, - -mAngle3[itube], 2. * mAngle3[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube] - + mLpartial3[itube] / 2 * + TMath::Cos(mAngle3[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle3[itube] * TMath::DegToRad()); + TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus( + Form("pipeTorusCentral%d_D3_H%d", itube, half), mPipe, + mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, -mAngle3[itube], + 2. * mAngle3[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos3[itube] + mLpartial3[itube] / 2 * TMath::Sin(mAngle3[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle3[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos3[itube] + + mLpartial3[itube] / 2 * + TMath::Sin(mAngle3[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle3[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(pipeTorusCentral, ivolume++, transformation); - TGeoVolume* pipeTube3 = gGeoManager->MakeTube(Form("pipeTube3%d_D3_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial3[itube] / 2.); + TGeoVolume* pipeTube3 = gGeoManager->MakeTube( + Form("pipeTube3%d_D3_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, + mLpartial3[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle3[itube], 0.); - transformation = new TGeoCombiTrans(yPos3[itube], 0., -(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos3[itube], 0., + -(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[itube]), + rotation); cooling->AddNode(pipeTube3, ivolume++, transformation); - TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus(Form("pipeTorus2%d_D3_H%d", itube, half), mPipe, mRadius3[itube], mRWater, mRWater + mDRPipe, 0., mAngle3[itube]); + TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus( + Form("pipeTorus2%d_D3_H%d", itube, half), mPipe, mRadius3[itube], + mRWater, mRWater + mDRPipe, 0., mAngle3[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius3[itube] + mXPosition3[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube]), + rotation); cooling->AddNode(pipeTorus2, ivolume++, transformation); - TGeoVolume* pipeTube4 = gGeoManager->MakeTube(Form("pipeTube4%d_D3_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater3[itube] / 2.); - translation = new TGeoTranslation(mXPosition3[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube] / 2.)); + TGeoVolume* pipeTube4 = + gGeoManager->MakeTube(Form("pipeTube4%d_D3_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater3[itube] / 2.); + translation = new TGeoTranslation( + mXPosition3[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[itube] / 2.)); cooling->AddNode(pipeTube4, ivolume++, translation); } // *********************************************************************************************** - Double_t deltaz = mHeatExchangerThickness - Geometry::sKaptonOnCarbonThickness * 4 - Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; + Double_t deltaz = mHeatExchangerThickness - + Geometry::sKaptonOnCarbonThickness * 4 - + Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; rotation = new TGeoRotation("rotation", -90., 90., 0.); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 3, transformation); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 4, transformation); - // **************************************** Carbon Plates **************************************** + // **************************************** Carbon Plates + // **************************************** auto* carbonPlate = new TGeoVolumeAssembly(Form("carbonPlate_D3_H%d", half)); - auto* carbonBase3 = new TGeoBBox(Form("carbonBase3_D3_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength, - (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); - auto* t31 = new TGeoTranslation("t31", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + + auto& mftBaseParam = MFTBaseParam::Instance(); + Double_t mReducedX = 0.; + if (mftBaseParam.buildAlignment) { + mReducedX = 0.6; + } + + auto* carbonBase3 = new TGeoBBox( + Form("carbonBase3_D3_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength - mReducedX, + (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); + auto* t31 = new TGeoTranslation( + "t31", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); t31->RegisterYourself(); auto* holeCarbon3 = - new TGeoTubeSeg(Form("holeCarbon3_D3_H%d", half), 0., mRMin[disk], mCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeCarbon3_D3_H%d", half), 0., mRMin[disk], + mCarbonThickness + 0.000001, 0, 180.); auto* t32 = new TGeoTranslation("t32", 0., -mHalfDiskGap, 0.); t32->RegisterYourself(); auto* carbonhole3 = new TGeoSubtraction(carbonBase3, holeCarbon3, t31, t32); auto* cs3 = new TGeoCompositeShape(Form("Carbon3_D3_H%d", half), carbonhole3); - auto* carbonBaseWithHole3 = new TGeoVolume(Form("carbonBaseWithHole_D3_H%d", half), cs3, mCarbon); + auto* carbonBaseWithHole3 = + new TGeoVolume(Form("carbonBaseWithHole_D3_H%d", half), cs3, mCarbon); carbonBaseWithHole3->SetLineColor(kGray + 3); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - carbonPlate->AddNode(carbonBaseWithHole3, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + carbonPlate->AddNode(carbonBaseWithHole3, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t ty = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { ty += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partCarbon = - gGeoManager->MakeBox(Form("partCarbon_D3_H%d_%d", half, ipart), mCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); + TGeoVolume* partCarbon = gGeoManager->MakeBox( + Form("partCarbon_D3_H%d_%d", half, ipart), mCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); partCarbon->SetLineColor(kGray + 3); auto* t = new TGeoTranslation("t", 0, ty + mHalfDiskGap, mZPlan[disk]); carbonPlate->AddNode(partCarbon, ipart, t); @@ -2791,36 +4152,53 @@ void HeatExchanger::createHalfDisk3(Int_t half) transformation = new TGeoCombiTrans(0., 0., -deltaz / 2., rotation); mHalfDisk->AddNode(carbonPlate, 4, transformation); - // **************************************** Glue Bwtween Carbon Plate and Rohacell Plate **************************************** + // **************************************** Glue Bwtween Carbon Plate and + // Rohacell Plate **************************************** TGeoMedium* mGlueRohacellCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueRohacellCarbon = new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); - auto* glueRohacellCarbonBase0 = new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sGlueRohacellCarbonThickness); - - auto* translation_gluRC01 = new TGeoTranslation("translation_gluRC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueRohacellCarbon = + new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); + auto* glueRohacellCarbonBase0 = + new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., + Geometry::sGlueRohacellCarbonThickness); + + auto* translation_gluRC01 = new TGeoTranslation( + "translation_gluRC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluRC01->RegisterYourself(); - auto* translation_gluRC02 = new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluRC02 = + new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); translation_gluRC02->RegisterYourself(); - auto* holeglueRohacellCarbon0 = - new TGeoTubeSeg(Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); + auto* holeglueRohacellCarbon0 = new TGeoTubeSeg( + Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); - auto* glueRohacellCarbonhole0 = new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, translation_gluRC01, translation_gluRC02); - auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), glueRohacellCarbonhole0); - auto* glueRohacellCarbonBaseWithHole0 = new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, mGlueRohacellCarbon); + auto* glueRohacellCarbonhole0 = + new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, + translation_gluRC01, translation_gluRC02); + auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), + glueRohacellCarbonhole0); + auto* glueRohacellCarbonBaseWithHole0 = + new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, + mGlueRohacellCarbon); glueRohacellCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGRC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGRC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueRohacellCarbon = - gGeoManager->MakeBox(Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sGlueRohacellCarbonThickness); + TGeoVolume* partGlueRohacellCarbon = gGeoManager->MakeBox( + Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), + mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sGlueRohacellCarbonThickness); partGlueRohacellCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGRC + mHalfDiskGap, mZPlan[disk]); glueRohacellCarbon->AddNode(partGlueRohacellCarbon, ipart, t); @@ -2828,41 +4206,62 @@ void HeatExchanger::createHalfDisk3(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness), rotation); + transformation = new TGeoCombiTrans(0., 0., + -(deltaz / 2. - mCarbonThickness - + Geometry::sGlueRohacellCarbonThickness), + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 4, transformation); - // **************************************** Kapton on Carbon Plate **************************************** + // **************************************** Kapton on Carbon Plate + // **************************************** TGeoMedium* mKaptonOnCarbon = gGeoManager->GetMedium("MFT_Kapton$"); - auto* kaptonOnCarbon = new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); - auto* kaptonOnCarbonBase0 = new TGeoBBox(Form("kaptonOnCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength, - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); - - auto* translation_KC01 = new TGeoTranslation("translation_KC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* kaptonOnCarbon = + new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); + auto* kaptonOnCarbonBase0 = new TGeoBBox( + Form("kaptonOnCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); + + auto* translation_KC01 = new TGeoTranslation( + "translation_KC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_KC01->RegisterYourself(); - auto* translation_KC02 = new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); + auto* translation_KC02 = + new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); translation_KC02->RegisterYourself(); auto* holekaptonOnCarbon0 = - new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); - auto* kaptonOnCarbonhole0 = new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, translation_KC01, translation_KC02); - auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), kaptonOnCarbonhole0); - auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume(Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); + auto* kaptonOnCarbonhole0 = + new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, + translation_KC01, translation_KC02); + auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), + kaptonOnCarbonhole0); + auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume( + Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); kaptonOnCarbonBaseWithHole0->SetLineColor(kMagenta); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partkaptonOnCarbonBase = - gGeoManager->MakeBox(Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonOnCarbonThickness); + TGeoVolume* partkaptonOnCarbonBase = gGeoManager->MakeBox( + Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sKaptonOnCarbonThickness); partkaptonOnCarbonBase->SetLineColor(kMagenta); auto* t = new TGeoTranslation("t", 0, tyKC + mHalfDiskGap, mZPlan[disk]); kaptonOnCarbon->AddNode(partkaptonOnCarbonBase, ipart, t); @@ -2870,41 +4269,63 @@ void HeatExchanger::createHalfDisk3(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2, + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2), + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 4, transformation); - // **************************************** Kapton glue on the carbon plate **************************************** + // **************************************** Kapton glue on the carbon plate + // **************************************** TGeoMedium* mGlueKaptonCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueKaptonCarbon = new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); - auto* glueKaptonCarbonBase0 = new TGeoBBox(Form("glueKaptonCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); - - auto* translation_gluKC01 = new TGeoTranslation("translation_gluKC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueKaptonCarbon = + new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); + auto* glueKaptonCarbonBase0 = new TGeoBBox( + Form("glueKaptonCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); + + auto* translation_gluKC01 = new TGeoTranslation( + "translation_gluKC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluKC01->RegisterYourself(); - auto* translation_gluKC02 = new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluKC02 = + new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); translation_gluKC02->RegisterYourself(); - auto* holeglueKaptonCarbon0 = - new TGeoTubeSeg(Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); + auto* holeglueKaptonCarbon0 = new TGeoTubeSeg( + Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); - auto* glueKaptonCarbonhole0 = new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, translation_gluKC01, translation_gluKC02); - auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), glueKaptonCarbonhole0); - auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume(Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); + auto* glueKaptonCarbonhole0 = + new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, + translation_gluKC01, translation_gluKC02); + auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), + glueKaptonCarbonhole0); + auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume( + Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); glueKaptonCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueKaptonCarbon = - gGeoManager->MakeBox(Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); + TGeoVolume* partGlueKaptonCarbon = gGeoManager->MakeBox( + Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); partGlueKaptonCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGKC + mHalfDiskGap, mZPlan[disk]); glueKaptonCarbon->AddNode(partGlueKaptonCarbon, ipart, t); @@ -2912,20 +4333,31 @@ void HeatExchanger::createHalfDisk3(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 4, transformation); - // **************************************** Rohacell Plate **************************************** - auto* rohacellPlate = new TGeoVolumeAssembly(Form("rohacellPlate_D3_H%d", half)); - auto* rohacellBase3 = new TGeoBBox(Form("rohacellBase3_D3_H%d", half), (mSupportXDimensions[disk][0]) / 2., (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); + // **************************************** Rohacell Plate + // **************************************** + auto* rohacellPlate = + new TGeoVolumeAssembly(Form("rohacellPlate_D3_H%d", half)); + auto* rohacellBase3 = new TGeoBBox( + Form("rohacellBase3_D3_H%d", half), (mSupportXDimensions[disk][0]) / 2., + (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); auto* holeRohacell3 = - new TGeoTubeSeg(Form("holeRohacell3_D3_H%d", half), 0., mRMin[disk], mRohacellThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeRohacell3_D3_H%d", half), 0., mRMin[disk], + mRohacellThickness + 0.000001, 0, 180.); - // **************************************** GROOVES ************************************************* - // Creating grooves or not according to sGrooves + // **************************************** GROOVES + // ************************************************* Creating grooves or not + // according to sGrooves Double_t diameter = 0.21; // groove diameter Double_t epsilon = 0.06; // outside shift of the goove Int_t iCount = 0; @@ -2937,20 +4369,31 @@ void HeatExchanger::createHalfDisk3(Int_t half) TGeoCompositeShape* rohacellGroove[300]; for (Int_t igroove = 0; igroove < 4; igroove++) { - grooveTube[0][igroove] = new TGeoTube("linear", 0., diameter, mLWater3[igroove] / 2.); - grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius3[igroove], 0., diameter, 0., mAngle3[igroove]); - grooveTube[2][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial3[igroove] / 2.); - grooveTorus[3][igroove] = new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, -mAngle3[igroove], 2. * mAngle3[igroove]); - grooveTube[4][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial3[igroove] / 2.); - grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius3[igroove], 0., diameter, 0., mAngle3[igroove]); - grooveTube[6][igroove] = new TGeoTube("linear", 0., diameter, mLWater3[igroove] / 2.); + grooveTube[0][igroove] = + new TGeoTube("linear", 0., diameter, mLWater3[igroove] / 2.); + grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius3[igroove], 0., + diameter, 0., mAngle3[igroove]); + grooveTube[2][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial3[igroove] / 2.); + grooveTorus[3][igroove] = + new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, + -mAngle3[igroove], 2. * mAngle3[igroove]); + grooveTube[4][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial3[igroove] / 2.); + grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius3[igroove], 0., + diameter, 0., mAngle3[igroove]); + grooveTube[6][igroove] = + new TGeoTube("linear", 0., diameter, mLWater3[igroove] / 2.); } // Rotation matrix TGeoRotation* rotationLinear = new TGeoRotation("rotation", -90., 90., 0.); - TGeoRotation* rotationSideTorusL = new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); - TGeoRotation* rotationSideTorusR = new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); - TGeoRotation* rotationCentralTorus = new TGeoRotation("rotationCentralTorus", 90., 0., 0.); + TGeoRotation* rotationSideTorusL = + new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); + TGeoRotation* rotationSideTorusR = + new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); + TGeoRotation* rotationCentralTorus = + new TGeoRotation("rotationCentralTorus", 90., 0., 0.); TGeoRotation* rotationTiltedLinearR; TGeoRotation* rotationTiltedLinearL; @@ -2958,51 +4401,95 @@ void HeatExchanger::createHalfDisk3(Int_t half) if (Geometry::sGrooves == 1) { for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 4; igroove++) { // 4 grooves - mPosition[igroove] = mXPosition3[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; + mPosition[igroove] = mXPosition3[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove] / 2., mPosition[igroove], iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove] / 2., + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[0] = new TGeoSubtraction(rohacellBase3, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[0] = new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[0]); + rohacellBaseGroove[0] = + new TGeoSubtraction(rohacellBase3, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[0] = + new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[0]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove], mRadius3[igroove] + mXPosition3[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove], + mRadius3[igroove] + mXPosition3[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle3[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[igroove], yPos3[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle3[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[igroove], + yPos3[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., yPos3[igroove] + mLpartial3[igroove] / 2 * TMath::Sin(mAngle3[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle3[igroove] * TMath::DegToRad()) - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + yPos3[igroove] + + mLpartial3[igroove] / 2 * + TMath::Sin(mAngle3[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle3[igroove] * TMath::DegToRad()) - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle3[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[igroove]), yPos3[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle3[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[3][0] / 2. + mMoreLength - + xPos3[igroove]), + yPos3[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove]), mRadius3[igroove] + mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove]), + mRadius3[igroove] + mPosition[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove] / 2.), mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove] / 2.), + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, + transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, + transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -3016,15 +4503,19 @@ void HeatExchanger::createHalfDisk3(Int_t half) rohacellBase = new TGeoSubtraction(rohacellBase3, holeRohacell3, t31, t32); } if (Geometry::sGrooves == 1) { - rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell3, t31, t32); + rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], + holeRohacell3, t31, t32); } - auto* rh3 = new TGeoCompositeShape(Form("rohacellTore%d_D0_H%d", 0, half), rohacellBase); - auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D3_H%d", half), rh3, mRohacell); + auto* rh3 = new TGeoCompositeShape(Form("rohacellTore%d_D0_H%d", 0, half), + rohacellBase); + auto* rohacellBaseWithHole = + new TGeoVolume(Form("rohacellBaseWithHole_D3_H%d", half), rh3, mRohacell); rohacellBaseWithHole->SetLineColor(kGray); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - rohacellPlate->AddNode(rohacellBaseWithHole, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + rohacellPlate->AddNode(rohacellBaseWithHole, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); ty = mSupportYDimensions[disk][0]; @@ -3036,74 +4527,128 @@ void HeatExchanger::createHalfDisk3(Int_t half) //=========================================================================================================== //=========================================================================================================== auto* partRohacell0 = - new TGeoBBox(Form("rohacellBase0_D3_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., + new TGeoBBox(Form("rohacellBase0_D3_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., mSupportYDimensions[disk][ipart] / 2., mRohacellThickness); Double_t mShift; if (Geometry::sGrooves == 1) { - // **************** Creating grooves for the other parts of the rohacell plate ********************** + // **************** Creating grooves for the other parts of the rohacell + // plate ********************** for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 4; igroove++) { // 4 grooves if (ipart == 1) { - mPosition[ipart] = mXPosition3[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; + mPosition[ipart] = + mXPosition3[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; mShift = -mSupportYDimensions[disk][ipart - 1]; }; if (ipart == 2) { - mPosition[ipart] = mXPosition3[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; + mPosition[ipart] = + mXPosition3[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; }; if (ipart == 3) { - mPosition[ipart] = mXPosition3[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; + mPosition[ipart] = + mXPosition3[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; }; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove] / 2., mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove] / 2., + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellBaseGroove[iCount] = + new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[iCount]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove], mPosition[ipart] + mRadius3[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove], + mPosition[ipart] + mRadius3[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle3[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[igroove], yPos3[igroove] + mShift - mHalfDiskGap - mSupportYDimensions[disk][ipart] / 2., iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle3[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[igroove], + yPos3[igroove] + mShift - mHalfDiskGap - + mSupportYDimensions[disk][ipart] / 2., + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., mPosition[ipart] + yPos3[igroove] + mLpartial3[igroove] / 2 * TMath::Sin(mAngle3[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle3[igroove] * TMath::DegToRad()) - mXPosition3[igroove], - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + mPosition[ipart] + yPos3[igroove] + + mLpartial3[igroove] / 2 * + TMath::Sin(mAngle3[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle3[igroove] * TMath::DegToRad()) - + mXPosition3[igroove], + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle3[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[3][0] / 2. + mMoreLength - xPos3[igroove]), yPos3[igroove] + mPosition[ipart] - mXPosition3[igroove], - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle3[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[3][0] / 2. + mMoreLength - + xPos3[igroove]), + yPos3[igroove] + mPosition[ipart] - mXPosition3[igroove], + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove]), mRadius3[igroove] + mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove]), + mRadius3[igroove] + mPosition[ipart], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[3][0] / 2. + mMoreLength - mLWater3[igroove] / 2.), mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[3][0] / 2. + mMoreLength - + mLWater3[igroove] / 2.), + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], + nullptr, transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell3Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -3120,30 +4665,42 @@ void HeatExchanger::createHalfDisk3(Int_t half) xnotch = 2.05; // half width ynotch = 0.4; // full height if (ipart == (mNPart[disk] - 1)) { - notchRohacell0 = new TGeoBBox(Form("notchRohacell0_D3_H%d", half), xnotch, ynotch, mRohacellThickness + 0.000001); - tnotch0 = new TGeoTranslation("tnotch0", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchRohacell0 = new TGeoBBox(Form("notchRohacell0_D3_H%d", half), xnotch, + ynotch, mRohacellThickness + 0.000001); + tnotch0 = new TGeoTranslation("tnotch0", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch0->RegisterYourself(); } //============================================================= if (Geometry::sGrooves == 0) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell0, nullptr, tnotch0); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D3_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell0, + nullptr, tnotch0); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D3_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D3_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), partRohacell0, mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), + partRohacell0, mRohacell); } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], notchRohacell0, nullptr, tnotch0); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D3_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], + notchRohacell0, nullptr, tnotch0); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D3_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D3_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D3_H%d_%d", half, ipart), + rohacellGroove[iCount - 1], mRohacell); } } @@ -3152,11 +4709,15 @@ void HeatExchanger::createHalfDisk3(Int_t half) partRohacell->SetLineColor(kGray); rohacellPlate->AddNode(partRohacell, ipart, t); - //========== insert to locate the rohacell plate compare to the disk support ============= + //========== insert to locate the rohacell plate compare to the disk support + //============= if (ipart == (mNPart[disk] - 1)) { TGeoTranslation* tinsert3; - TGeoVolume* insert3 = gGeoManager->MakeBox(Form("insert3_H%d_%d", half, ipart), mPeek, 4.0 / 2., 0.44 / 2., mRohacellThickness); - Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap + 0.44 / 2. - ynotch; + TGeoVolume* insert3 = + gGeoManager->MakeBox(Form("insert3_H%d_%d", half, ipart), mPeek, + 4.0 / 2., 0.44 / 2., mRohacellThickness); + Double_t ylocation = + mSupportYDimensions[disk][0] + mHalfDiskGap + 0.44 / 2. - ynotch; for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; } @@ -3202,134 +4763,249 @@ void HeatExchanger::createHalfDisk4(Int_t half) TGeoRotation* rotation = nullptr; TGeoCombiTrans* transformation = nullptr; - // **************************************** Water part ****************************************** - // ********************* Four parameters mLwater4, mRadius4, mAngle4, mLpartial4 **************** + // **************************************** Water part + // ****************************************** + // ********************* Four parameters mLwater4, mRadius4, mAngle4, + // mLpartial4 **************** Double_t ivolume = 400; // offset chamber 4 Double_t mRadiusCentralTore[4]; Double_t xPos4[4]; Double_t yPos4[4]; for (Int_t itube = 0; itube < 4; itube++) { - TGeoVolume* waterTube1 = gGeoManager->MakeTube(Form("waterTube1%d_D4_H%d", itube, half), mWater, 0., mRWater, mLWater4[itube] / 2.); - translation = new TGeoTranslation(mXPosition4[itube] - mHalfDiskGap, 0., mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube] / 2.); + TGeoVolume* waterTube1 = + gGeoManager->MakeTube(Form("waterTube1%d_D4_H%d", itube, half), mWater, + 0., mRWater, mLWater4[itube] / 2.); + translation = new TGeoTranslation(mXPosition4[itube] - mHalfDiskGap, 0., + mSupportXDimensions[4][0] / 2. + + mMoreLength - mLWater4[itube] / 2.); cooling->AddNode(waterTube1, ivolume++, translation); - TGeoVolume* waterTorus1 = gGeoManager->MakeTorus(Form("waterTorus1%d_D4_H%d", itube, half), mWater, mRadius4[itube], 0., mRWater, 0., mAngle4[itube]); + TGeoVolume* waterTorus1 = gGeoManager->MakeTorus( + Form("waterTorus1%d_D4_H%d", itube, half), mWater, mRadius4[itube], 0., + mRWater, 0., mAngle4[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., + mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube], + rotation); // cooling->AddNode(waterTorus1, ivolume++, transformation); - TGeoVolume* waterTube2 = gGeoManager->MakeTube(Form("waterTube2%d_D4_H%d", itube, half), mWater, 0., mRWater, mLpartial4[itube] / 2.); + TGeoVolume* waterTube2 = + gGeoManager->MakeTube(Form("waterTube2%d_D4_H%d", itube, half), mWater, + 0., mRWater, mLpartial4[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle4[itube], 0.); - xPos4[itube] = mLWater4[itube] + mRadius4[itube] * TMath::Sin(mAngle4[itube] * TMath::DegToRad()) + mLpartial4[itube] / 2 * TMath::Cos(mAngle4[itube] * TMath::DegToRad()); - yPos4[itube] = mXPosition4[itube] - mHalfDiskGap + mRadius4[itube] * (1 - TMath::Cos(mAngle4[itube] * TMath::DegToRad())) + mLpartial4[itube] / 2 * TMath::Sin(mAngle4[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos4[itube], 0., mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube], rotation); + xPos4[itube] = + mLWater4[itube] + + mRadius4[itube] * TMath::Sin(mAngle4[itube] * TMath::DegToRad()) + + mLpartial4[itube] / 2 * TMath::Cos(mAngle4[itube] * TMath::DegToRad()); + yPos4[itube] = + mXPosition4[itube] - mHalfDiskGap + + mRadius4[itube] * (1 - TMath::Cos(mAngle4[itube] * TMath::DegToRad())) + + mLpartial4[itube] / 2 * TMath::Sin(mAngle4[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans( + yPos4[itube], 0., + mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube], rotation); cooling->AddNode(waterTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube] - mLpartial4[itube] / 2 * TMath::Cos(mAngle4[itube] * TMath::DegToRad())) / TMath::Sin(mAngle4[itube] * TMath::DegToRad()); - TGeoVolume* waterTorusCentral = gGeoManager->MakeTorus(Form("waterTorusCentral%d_D4_H%d", itube, half), mWater, mRadiusCentralTore[itube], 0., - mRWater, -mAngle4[itube], 2. * mAngle4[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube] - + mLpartial4[itube] / 2 * + TMath::Cos(mAngle4[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle4[itube] * TMath::DegToRad()); + TGeoVolume* waterTorusCentral = + gGeoManager->MakeTorus(Form("waterTorusCentral%d_D4_H%d", itube, half), + mWater, mRadiusCentralTore[itube], 0., mRWater, + -mAngle4[itube], 2. * mAngle4[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos4[itube] + mLpartial4[itube] / 2 * TMath::Sin(mAngle4[itube] * TMath::DegToRad()) - - mRadiusCentralTore[itube] * TMath::Cos(mAngle4[itube] * TMath::DegToRad()), - 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos4[itube] + + mLpartial4[itube] / 2 * + TMath::Sin(mAngle4[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle4[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(waterTorusCentral, ivolume++, transformation); - TGeoVolume* waterTube3 = gGeoManager->MakeTube(Form("waterTube3%d_D4_H%d", 2, half), mWater, 0., mRWater, mLpartial4[itube] / 2.); + TGeoVolume* waterTube3 = + gGeoManager->MakeTube(Form("waterTube3%d_D4_H%d", 2, half), mWater, 0., + mRWater, mLpartial4[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle4[itube], 0.); - transformation = new TGeoCombiTrans(yPos4[itube], 0., -(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos4[itube], 0., + -(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube]), + rotation); cooling->AddNode(waterTube3, ivolume++, transformation); - TGeoVolume* waterTorus2 = gGeoManager->MakeTorus(Form("waterTorus2%d_D4_H%d", itube, half), mWater, mRadius4[itube], 0., mRWater, 0., mAngle4[itube]); + TGeoVolume* waterTorus2 = gGeoManager->MakeTorus( + Form("waterTorus2%d_D4_H%d", itube, half), mWater, mRadius4[itube], 0., + mRWater, 0., mAngle4[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube]), + rotation); cooling->AddNode(waterTorus2, ivolume++, transformation); - TGeoVolume* waterTube4 = gGeoManager->MakeTube(Form("waterTube4%d_D4_H%d", itube, half), mWater, 0., mRWater, mLWater4[itube] / 2.); - translation = new TGeoTranslation(mXPosition4[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube] / 2.)); + TGeoVolume* waterTube4 = + gGeoManager->MakeTube(Form("waterTube4%d_D4_H%d", itube, half), mWater, + 0., mRWater, mLWater4[itube] / 2.); + translation = new TGeoTranslation( + mXPosition4[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube] / 2.)); cooling->AddNode(waterTube4, ivolume++, translation); } - // **************************************** Tube part ******************************************* - // ********************* Four parameters mLwater4, mRadius4, mAngle4, mLpartial4 **************** + // **************************************** Tube part + // ******************************************* + // ********************* Four parameters mLwater4, mRadius4, mAngle4, + // mLpartial4 **************** for (Int_t itube = 0; itube < 4; itube++) { - TGeoVolume* pipeTube1 = gGeoManager->MakeTube(Form("pipeTube1%d_D4_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater4[itube] / 2.); - translation = new TGeoTranslation(mXPosition4[itube] - mHalfDiskGap, 0., mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube] / 2.); + TGeoVolume* pipeTube1 = + gGeoManager->MakeTube(Form("pipeTube1%d_D4_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater4[itube] / 2.); + translation = new TGeoTranslation(mXPosition4[itube] - mHalfDiskGap, 0., + mSupportXDimensions[4][0] / 2. + + mMoreLength - mLWater4[itube] / 2.); cooling->AddNode(pipeTube1, ivolume++, translation); - TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus(Form("pipeTorus1%d_D4_H%d", itube, half), mPipe, mRadius4[itube], mRWater, mRWater + mDRPipe, 0., mAngle4[itube]); + TGeoVolume* pipeTorus1 = gGeoManager->MakeTorus( + Form("pipeTorus1%d_D4_H%d", itube, half), mPipe, mRadius4[itube], + mRWater, mRWater + mDRPipe, 0., mAngle4[itube]); rotation = new TGeoRotation("rotation", 180., -90., 0.); - transformation = new TGeoCombiTrans(mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube], rotation); + transformation = new TGeoCombiTrans( + mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., + mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube], + rotation); cooling->AddNode(pipeTorus1, ivolume++, transformation); - TGeoVolume* pipeTube2 = gGeoManager->MakeTube(Form("pipeTube2%d_D4_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial4[itube] / 2.); + TGeoVolume* pipeTube2 = gGeoManager->MakeTube( + Form("pipeTube2%d_D4_H%d", itube, half), mPipe, mRWater, + mRWater + mDRPipe, mLpartial4[itube] / 2.); rotation = new TGeoRotation("rotation", 90., 180 - mAngle4[itube], 0.); - xPos4[itube] = mLWater4[itube] + mRadius4[itube] * TMath::Sin(mAngle4[itube] * TMath::DegToRad()) + mLpartial4[itube] / 2 * TMath::Cos(mAngle4[itube] * TMath::DegToRad()); - yPos4[itube] = mXPosition4[itube] - mHalfDiskGap + mRadius4[itube] * (1 - TMath::Cos(mAngle4[itube] * TMath::DegToRad())) + mLpartial4[itube] / 2 * TMath::Sin(mAngle4[itube] * TMath::DegToRad()); - transformation = new TGeoCombiTrans(yPos4[itube], 0., mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube], rotation); + xPos4[itube] = + mLWater4[itube] + + mRadius4[itube] * TMath::Sin(mAngle4[itube] * TMath::DegToRad()) + + mLpartial4[itube] / 2 * TMath::Cos(mAngle4[itube] * TMath::DegToRad()); + yPos4[itube] = + mXPosition4[itube] - mHalfDiskGap + + mRadius4[itube] * (1 - TMath::Cos(mAngle4[itube] * TMath::DegToRad())) + + mLpartial4[itube] / 2 * TMath::Sin(mAngle4[itube] * TMath::DegToRad()); + transformation = new TGeoCombiTrans( + yPos4[itube], 0., + mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube], rotation); cooling->AddNode(pipeTube2, ivolume++, transformation); - mRadiusCentralTore[itube] = (mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube] - mLpartial4[itube] / 2 * TMath::Cos(mAngle4[itube] * TMath::DegToRad())) / TMath::Sin(mAngle4[itube] * TMath::DegToRad()); - TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus(Form("pipeTorusCentral%d_D4_H%d", itube, half), mPipe, mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, - -mAngle4[itube], 2. * mAngle4[itube]); + mRadiusCentralTore[itube] = + (mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube] - + mLpartial4[itube] / 2 * + TMath::Cos(mAngle4[itube] * TMath::DegToRad())) / + TMath::Sin(mAngle4[itube] * TMath::DegToRad()); + TGeoVolume* pipeTorusCentral = gGeoManager->MakeTorus( + Form("pipeTorusCentral%d_D4_H%d", itube, half), mPipe, + mRadiusCentralTore[itube], mRWater, mRWater + mDRPipe, -mAngle4[itube], + 2. * mAngle4[itube]); rotation = new TGeoRotation("rotation", 0., 90., 0.); - transformation = new TGeoCombiTrans(yPos4[itube] + mLpartial4[itube] / 2 * TMath::Sin(mAngle4[itube] * TMath::DegToRad()) - mRadiusCentralTore[itube] * TMath::Cos(mAngle4[itube] * TMath::DegToRad()), 0., 0., rotation); + transformation = new TGeoCombiTrans( + yPos4[itube] + + mLpartial4[itube] / 2 * + TMath::Sin(mAngle4[itube] * TMath::DegToRad()) - + mRadiusCentralTore[itube] * + TMath::Cos(mAngle4[itube] * TMath::DegToRad()), + 0., 0., rotation); cooling->AddNode(pipeTorusCentral, ivolume++, transformation); - TGeoVolume* pipeTube3 = gGeoManager->MakeTube(Form("pipeTube3%d_D4_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, mLpartial4[itube] / 2.); + TGeoVolume* pipeTube3 = gGeoManager->MakeTube( + Form("pipeTube3%d_D4_H%d", 2, half), mPipe, mRWater, mRWater + mDRPipe, + mLpartial4[itube] / 2.); rotation = new TGeoRotation("rotation", -90., 0 - mAngle4[itube], 0.); - transformation = new TGeoCombiTrans(yPos4[itube], 0., -(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube]), rotation); + transformation = new TGeoCombiTrans( + yPos4[itube], 0., + -(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[itube]), + rotation); cooling->AddNode(pipeTube3, ivolume++, transformation); - TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus(Form("pipeTorus2%d_D4_H%d", itube, half), mPipe, mRadius4[itube], mRWater, mRWater + mDRPipe, 0., mAngle4[itube]); + TGeoVolume* pipeTorus2 = gGeoManager->MakeTorus( + Form("pipeTorus2%d_D4_H%d", itube, half), mPipe, mRadius4[itube], + mRWater, mRWater + mDRPipe, 0., mAngle4[itube]); rotation = new TGeoRotation("rotation", 180., 90., 0.); - transformation = new TGeoCombiTrans(mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube]), rotation); + transformation = new TGeoCombiTrans( + mRadius4[itube] + mXPosition4[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube]), + rotation); cooling->AddNode(pipeTorus2, ivolume++, transformation); - TGeoVolume* pipeTube4 = gGeoManager->MakeTube(Form("pipeTube4%d_D4_H%d", itube, half), mPipe, mRWater, mRWater + mDRPipe, mLWater4[itube] / 2.); - translation = new TGeoTranslation(mXPosition4[itube] - mHalfDiskGap, 0., -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube] / 2.)); + TGeoVolume* pipeTube4 = + gGeoManager->MakeTube(Form("pipeTube4%d_D4_H%d", itube, half), mPipe, + mRWater, mRWater + mDRPipe, mLWater4[itube] / 2.); + translation = new TGeoTranslation( + mXPosition4[itube] - mHalfDiskGap, 0., + -(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[itube] / 2.)); cooling->AddNode(pipeTube4, ivolume++, translation); } // *********************************************************************************************** - Double_t deltaz = mHeatExchangerThickness - Geometry::sKaptonOnCarbonThickness * 4 - Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; + Double_t deltaz = mHeatExchangerThickness - + Geometry::sKaptonOnCarbonThickness * 4 - + Geometry::sKaptonGlueThickness * 4 - 2 * mCarbonThickness; rotation = new TGeoRotation("rotation", -90., 90., 0.); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] + deltaz / 2. - mCarbonThickness - mRWater - mDRPipe - + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 3, transformation); - transformation = - new TGeoCombiTrans(0., 0., mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + 2 * Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + mZPlan[disk] - deltaz / 2. + mCarbonThickness + mRWater + mDRPipe + + 2 * Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(cooling, 4, transformation); - // **************************************** Carbon Plates **************************************** + // **************************************** Carbon Plates + // **************************************** auto* carbonPlate = new TGeoVolumeAssembly(Form("carbonPlate_D4_H%d", half)); - auto* carbonBase4 = new TGeoBBox(Form("carbonBase4_D4_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength, - (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); - auto* t41 = new TGeoTranslation("t41", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + + auto& mftBaseParam = MFTBaseParam::Instance(); + Double_t mReducedX = 0.; + if (mftBaseParam.buildAlignment) { + mReducedX = 0.6; + } + + auto* carbonBase4 = new TGeoBBox( + Form("carbonBase4_D4_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength - mReducedX, + (mSupportYDimensions[disk][0]) / 2., mCarbonThickness); + auto* t41 = new TGeoTranslation( + "t41", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); t41->RegisterYourself(); auto* holeCarbon4 = - new TGeoTubeSeg(Form("holeCarbon4_D4_H%d", half), 0., mRMin[disk], mCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeCarbon4_D4_H%d", half), 0., mRMin[disk], + mCarbonThickness + 0.000001, 0, 180.); auto* t42 = new TGeoTranslation("t42", 0., -mHalfDiskGap, 0.); t42->RegisterYourself(); auto* carbonhole4 = new TGeoSubtraction(carbonBase4, holeCarbon4, t41, t42); auto* cs4 = new TGeoCompositeShape(Form("Carbon4_D4_H%d", half), carbonhole4); - auto* carbonBaseWithHole4 = new TGeoVolume(Form("carbonBaseWithHole_D4_H%d", half), cs4, mCarbon); + auto* carbonBaseWithHole4 = + new TGeoVolume(Form("carbonBaseWithHole_D4_H%d", half), cs4, mCarbon); carbonBaseWithHole4->SetLineColor(kGray + 3); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - carbonPlate->AddNode(carbonBaseWithHole4, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + carbonPlate->AddNode(carbonBaseWithHole4, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t ty = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { ty += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partCarbon = - gGeoManager->MakeBox(Form("partCarbon_D4_H%d_%d", half, ipart), mCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); + TGeoVolume* partCarbon = gGeoManager->MakeBox( + Form("partCarbon_D4_H%d_%d", half, ipart), mCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., mCarbonThickness); partCarbon->SetLineColor(kGray + 3); auto* t = new TGeoTranslation("t", 0, ty + mHalfDiskGap, mZPlan[disk]); carbonPlate->AddNode(partCarbon, ipart, t); @@ -3342,36 +5018,53 @@ void HeatExchanger::createHalfDisk4(Int_t half) transformation = new TGeoCombiTrans(0., 0., -deltaz / 2., rotation); mHalfDisk->AddNode(carbonPlate, 4, transformation); - // **************************************** Glue Bwtween Carbon Plate and Rohacell Plate **************************************** + // **************************************** Glue Bwtween Carbon Plate and + // Rohacell Plate **************************************** TGeoMedium* mGlueRohacellCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueRohacellCarbon = new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); - auto* glueRohacellCarbonBase0 = new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., Geometry::sGlueRohacellCarbonThickness); - - auto* translation_gluRC01 = new TGeoTranslation("translation_gluRC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueRohacellCarbon = + new TGeoVolumeAssembly(Form("glueRohacellCarbon_D0_H%d", half)); + auto* glueRohacellCarbonBase0 = + new TGeoBBox(Form("glueRohacellCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., + Geometry::sGlueRohacellCarbonThickness); + + auto* translation_gluRC01 = new TGeoTranslation( + "translation_gluRC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluRC01->RegisterYourself(); - auto* translation_gluRC02 = new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluRC02 = + new TGeoTranslation("translation_gluRC02", 0., -mHalfDiskGap, 0.); translation_gluRC02->RegisterYourself(); - auto* holeglueRohacellCarbon0 = - new TGeoTubeSeg(Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); + auto* holeglueRohacellCarbon0 = new TGeoTubeSeg( + Form("holeglueRohacellCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sGlueRohacellCarbonThickness + 0.000001, 0, 180.); - auto* glueRohacellCarbonhole0 = new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, translation_gluRC01, translation_gluRC02); - auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), glueRohacellCarbonhole0); - auto* glueRohacellCarbonBaseWithHole0 = new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, mGlueRohacellCarbon); + auto* glueRohacellCarbonhole0 = + new TGeoSubtraction(glueRohacellCarbonBase0, holeglueRohacellCarbon0, + translation_gluRC01, translation_gluRC02); + auto* gRC0 = new TGeoCompositeShape(Form("glueRohacellCarbon0_D0_H%d", half), + glueRohacellCarbonhole0); + auto* glueRohacellCarbonBaseWithHole0 = + new TGeoVolume(Form("glueRohacellCarbonWithHole_D0_H%d", half), gRC0, + mGlueRohacellCarbon); glueRohacellCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueRohacellCarbon->AddNode(glueRohacellCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGRC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGRC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueRohacellCarbon = - gGeoManager->MakeBox(Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sGlueRohacellCarbonThickness); + TGeoVolume* partGlueRohacellCarbon = gGeoManager->MakeBox( + Form("partGlueRohacellCarbon_D0_H%d_%d", half, ipart), + mGlueRohacellCarbon, mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sGlueRohacellCarbonThickness); partGlueRohacellCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGRC + mHalfDiskGap, mZPlan[disk]); glueRohacellCarbon->AddNode(partGlueRohacellCarbon, ipart, t); @@ -3379,41 +5072,62 @@ void HeatExchanger::createHalfDisk4(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness, + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. - mCarbonThickness - Geometry::sGlueRohacellCarbonThickness), rotation); + transformation = new TGeoCombiTrans(0., 0., + -(deltaz / 2. - mCarbonThickness - + Geometry::sGlueRohacellCarbonThickness), + rotation); mHalfDisk->AddNode(glueRohacellCarbon, 4, transformation); - // **************************************** Kapton on Carbon Plate **************************************** + // **************************************** Kapton on Carbon Plate + // **************************************** TGeoMedium* mKaptonOnCarbon = gGeoManager->GetMedium("MFT_Kapton$"); - auto* kaptonOnCarbon = new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); - auto* kaptonOnCarbonBase0 = new TGeoBBox(Form("kaptonOnCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2. + mMoreLength, - (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); - - auto* translation_KC01 = new TGeoTranslation("translation_KC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* kaptonOnCarbon = + new TGeoVolumeAssembly(Form("kaptonOnCarbon_D0_H%d", half)); + auto* kaptonOnCarbonBase0 = new TGeoBBox( + Form("kaptonOnCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. + mMoreLength - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonOnCarbonThickness); + + auto* translation_KC01 = new TGeoTranslation( + "translation_KC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_KC01->RegisterYourself(); - auto* translation_KC02 = new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); + auto* translation_KC02 = + new TGeoTranslation("translation_KC02", 0., -mHalfDiskGap, 0.); translation_KC02->RegisterYourself(); auto* holekaptonOnCarbon0 = - new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holekaptonOnCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonOnCarbonThickness + 0.000001, 0, 180.); - auto* kaptonOnCarbonhole0 = new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, translation_KC01, translation_KC02); - auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), kaptonOnCarbonhole0); - auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume(Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); + auto* kaptonOnCarbonhole0 = + new TGeoSubtraction(kaptonOnCarbonBase0, holekaptonOnCarbon0, + translation_KC01, translation_KC02); + auto* KC0 = new TGeoCompositeShape(Form("kaptonOnCarbon_D0_H%d", half), + kaptonOnCarbonhole0); + auto* kaptonOnCarbonBaseWithHole0 = new TGeoVolume( + Form("kaptonOnCarbonWithHole_D0_H%d", half), KC0, mKaptonOnCarbon); kaptonOnCarbonBaseWithHole0->SetLineColor(kMagenta); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + kaptonOnCarbon->AddNode(kaptonOnCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partkaptonOnCarbonBase = - gGeoManager->MakeBox(Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonOnCarbonThickness); + TGeoVolume* partkaptonOnCarbonBase = gGeoManager->MakeBox( + Form("partkaptonOnCarbon_D0_H%d_%d", half, ipart), mKaptonOnCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., + Geometry::sKaptonOnCarbonThickness); partkaptonOnCarbonBase->SetLineColor(kMagenta); auto* t = new TGeoTranslation("t", 0, tyKC + mHalfDiskGap, mZPlan[disk]); kaptonOnCarbon->AddNode(partkaptonOnCarbonBase, ipart, t); @@ -3421,40 +5135,63 @@ void HeatExchanger::createHalfDisk4(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2, rotation); + transformation = new TGeoCombiTrans( + 0., 0., + deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2, + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + Geometry::sKaptonGlueThickness * 2), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2 + Geometry::sKaptonOnCarbonThickness + mCarbonThickness + + Geometry::sKaptonGlueThickness * 2), + rotation); mHalfDisk->AddNode(kaptonOnCarbon, 4, transformation); - // **************************************** Kapton glue on the carbon plate **************************************** + // **************************************** Kapton glue on the carbon plate + // **************************************** TGeoMedium* mGlueKaptonCarbon = gGeoManager->GetMedium("MFT_Epoxy$"); - auto* glueKaptonCarbon = new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); - auto* glueKaptonCarbonBase0 = new TGeoBBox(Form("glueKaptonCarbonBase0_D0_H%d", half), (mSupportXDimensions[disk][0]) / 2., (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); - - auto* translation_gluKC01 = new TGeoTranslation("translation_gluKC01", 0., (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); + auto* glueKaptonCarbon = + new TGeoVolumeAssembly(Form("glueKaptonCarbon_D0_H%d", half)); + auto* glueKaptonCarbonBase0 = new TGeoBBox( + Form("glueKaptonCarbonBase0_D0_H%d", half), + (mSupportXDimensions[disk][0]) / 2. - mReducedX, + (mSupportYDimensions[disk][0]) / 2., Geometry::sKaptonGlueThickness); + + auto* translation_gluKC01 = new TGeoTranslation( + "translation_gluKC01", 0., + (mSupportYDimensions[disk][0]) / 2. + mHalfDiskGap, 0.); translation_gluKC01->RegisterYourself(); - auto* translation_gluKC02 = new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); + auto* translation_gluKC02 = + new TGeoTranslation("translation_gluKC02", 0., -mHalfDiskGap, 0.); translation_gluKC02->RegisterYourself(); - auto* holeglueKaptonCarbon0 = - new TGeoTubeSeg(Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); + auto* holeglueKaptonCarbon0 = new TGeoTubeSeg( + Form("holeglueKaptonCarbon0_D0_H%d", half), 0., mRMin[disk], + Geometry::sKaptonGlueThickness + 0.000001, 0, 180.); - auto* glueKaptonCarbonhole0 = new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, translation_gluKC01, translation_gluKC02); - auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), glueKaptonCarbonhole0); - auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume(Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); + auto* glueKaptonCarbonhole0 = + new TGeoSubtraction(glueKaptonCarbonBase0, holeglueKaptonCarbon0, + translation_gluKC01, translation_gluKC02); + auto* gKC0 = new TGeoCompositeShape(Form("glueKaptonCarbon0_D0_H%d", half), + glueKaptonCarbonhole0); + auto* glueKaptonCarbonBaseWithHole0 = new TGeoVolume( + Form("glueKaptonCarbonWithHole_D0_H%d", half), gKC0, mGlueKaptonCarbon); glueKaptonCarbonBaseWithHole0->SetLineColor(kGreen); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + glueKaptonCarbon->AddNode(glueKaptonCarbonBaseWithHole0, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); Double_t tyGKC = mSupportYDimensions[disk][0]; for (Int_t ipart = 1; ipart < mNPart[disk]; ipart++) { tyGKC += mSupportYDimensions[disk][ipart] / 2.; - TGeoVolume* partGlueKaptonCarbon = - gGeoManager->MakeBox(Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, mSupportXDimensions[disk][ipart] / 2., - mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); + TGeoVolume* partGlueKaptonCarbon = gGeoManager->MakeBox( + Form("partGlueKaptonCarbon_D0_H%d_%d", half, ipart), mGlueKaptonCarbon, + mSupportXDimensions[disk][ipart] / 2., + mSupportYDimensions[disk][ipart] / 2., Geometry::sKaptonGlueThickness); partGlueKaptonCarbon->SetLineColor(kGreen); auto* t = new TGeoTranslation("t", 0, tyGKC + mHalfDiskGap, mZPlan[disk]); glueKaptonCarbon->AddNode(partGlueKaptonCarbon, ipart, t); @@ -3462,19 +5199,29 @@ void HeatExchanger::createHalfDisk4(Int_t half) } rotation = new TGeoRotation("rotation", 180., 0., 0.); - transformation = new TGeoCombiTrans(0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, rotation); + transformation = new TGeoCombiTrans( + 0., 0., deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness, + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 3, transformation); - transformation = new TGeoCombiTrans(0., 0., -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), rotation); + transformation = new TGeoCombiTrans( + 0., 0., + -(deltaz / 2. + mCarbonThickness + Geometry::sKaptonGlueThickness), + rotation); mHalfDisk->AddNode(glueKaptonCarbon, 4, transformation); - // **************************************** Rohacell Plate **************************************** - auto* rohacellPlate = new TGeoVolumeAssembly(Form("rohacellPlate_D4_H%d", half)); - auto* rohacellBase4 = new TGeoBBox(Form("rohacellBase4_D4_H%d", half), (mSupportXDimensions[disk][0]) / 2., - (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); + // **************************************** Rohacell Plate + // **************************************** + auto* rohacellPlate = + new TGeoVolumeAssembly(Form("rohacellPlate_D4_H%d", half)); + auto* rohacellBase4 = new TGeoBBox( + Form("rohacellBase4_D4_H%d", half), (mSupportXDimensions[disk][0]) / 2., + (mSupportYDimensions[disk][0]) / 2., mRohacellThickness); auto* holeRohacell4 = - new TGeoTubeSeg(Form("holeRohacell4_D4_H%d", half), 0., mRMin[disk], mRohacellThickness + 0.000001, 0, 180.); + new TGeoTubeSeg(Form("holeRohacell4_D4_H%d", half), 0., mRMin[disk], + mRohacellThickness + 0.000001, 0, 180.); - // *************************************** Grooves ************************************************* + // *************************************** Grooves + // ************************************************* Double_t diameter = 0.21; // groove diameter Double_t epsilon = 0.06; // outside shift of the goove Int_t iCount = 0; @@ -3487,20 +5234,31 @@ void HeatExchanger::createHalfDisk4(Int_t half) TGeoRotation* rotationTorus5[8]; for (Int_t igroove = 0; igroove < 4; igroove++) { - grooveTube[0][igroove] = new TGeoTube("linear", 0., diameter, mLWater4[igroove] / 2.); - grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius4[igroove], 0., diameter, 0., mAngle4[igroove]); - grooveTube[2][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial4[igroove] / 2.); - grooveTorus[3][igroove] = new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, -mAngle4[igroove], 2. * mAngle4[igroove]); - grooveTube[4][igroove] = new TGeoTube("tiltedLinear", 0., diameter, mLpartial4[igroove] / 2.); - grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius4[igroove], 0., diameter, 0., mAngle4[igroove]); - grooveTube[6][igroove] = new TGeoTube("linear", 0., diameter, mLWater4[igroove] / 2.); + grooveTube[0][igroove] = + new TGeoTube("linear", 0., diameter, mLWater4[igroove] / 2.); + grooveTorus[1][igroove] = new TGeoTorus("SideTorus", mRadius4[igroove], 0., + diameter, 0., mAngle4[igroove]); + grooveTube[2][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial4[igroove] / 2.); + grooveTorus[3][igroove] = + new TGeoTorus("centralTorus", mRadiusCentralTore[igroove], 0., diameter, + -mAngle4[igroove], 2. * mAngle4[igroove]); + grooveTube[4][igroove] = + new TGeoTube("tiltedLinear", 0., diameter, mLpartial4[igroove] / 2.); + grooveTorus[5][igroove] = new TGeoTorus("SideTorus", mRadius4[igroove], 0., + diameter, 0., mAngle4[igroove]); + grooveTube[6][igroove] = + new TGeoTube("linear", 0., diameter, mLWater4[igroove] / 2.); } // Rotation matrix TGeoRotation* rotationLinear = new TGeoRotation("rotation", 90., 90., 0.); - TGeoRotation* rotationSideTorusL = new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); - TGeoRotation* rotationSideTorusR = new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); - TGeoRotation* rotationCentralTorus = new TGeoRotation("rotationCentralTorus", 90., 0., 0.); + TGeoRotation* rotationSideTorusL = + new TGeoRotation("rotationSideTorusLeft", -90., 0., 0.); + TGeoRotation* rotationSideTorusR = + new TGeoRotation("rotationSideTorusRight", 90., 180., 180.); + TGeoRotation* rotationCentralTorus = + new TGeoRotation("rotationCentralTorus", 90., 0., 0.); TGeoRotation* rotationTiltedLinearR; TGeoRotation* rotationTiltedLinearL; @@ -3508,54 +5266,95 @@ void HeatExchanger::createHalfDisk4(Int_t half) if (Geometry::sGrooves == 1) { for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 4; igroove++) { // 4 grooves - mPosition[igroove] = mXPosition4[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; + mPosition[igroove] = mXPosition4[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove] / 2., mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove] / 2., + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[0] = new TGeoSubtraction(rohacellBase4, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[0] = new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[0]); + rohacellBaseGroove[0] = + new TGeoSubtraction(rohacellBase4, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[0] = + new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[0]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove], mRadius4[igroove] + mXPosition4[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove], + mRadius4[igroove] + mXPosition4[igroove] - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle4[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[igroove], yPos4[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle4[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[igroove], + yPos4[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., yPos4[igroove] + mLpartial4[igroove] / 2 * TMath::Sin(mAngle4[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle4[igroove] * TMath::DegToRad()) - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + yPos4[igroove] + + mLpartial4[igroove] / 2 * + TMath::Sin(mAngle4[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle4[igroove] * TMath::DegToRad()) - + mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle4[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[igroove]), yPos4[igroove] - mSupportYDimensions[disk][0] / 2. - mHalfDiskGap, - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle4[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[4][0] / 2. + mMoreLength - + xPos4[igroove]), + yPos4[igroove] - mSupportYDimensions[disk][0] / 2. - + mHalfDiskGap, + iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove]), mRadius4[igroove] + mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove]), + mRadius4[igroove] + mPosition[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove] / 2.), mPosition[igroove], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove] / 2.), + mPosition[igroove], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, + transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, + transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -3570,16 +5369,20 @@ void HeatExchanger::createHalfDisk4(Int_t half) rohacellBase = new TGeoSubtraction(rohacellBase4, holeRohacell4, t41, t42); } if (Geometry::sGrooves == 1) { - rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], holeRohacell4, t41, t42); + rohacellBase = new TGeoSubtraction(rohacellGroove[iCount - 1], + holeRohacell4, t41, t42); } - auto* rh4 = new TGeoCompositeShape(Form("rohacellTore%d_D4_H%d", 0, half), rohacellBase); - auto* rohacellBaseWithHole = new TGeoVolume(Form("rohacellBaseWithHole_D4_H%d", half), rh4, mRohacell); + auto* rh4 = new TGeoCompositeShape(Form("rohacellTore%d_D4_H%d", 0, half), + rohacellBase); + auto* rohacellBaseWithHole = + new TGeoVolume(Form("rohacellBaseWithHole_D4_H%d", half), rh4, mRohacell); TGeoVolume* partRohacell; rohacellBaseWithHole->SetLineColor(kGray); rotation = new TGeoRotation("rotation", 0., 0., 0.); transformation = new TGeoCombiTrans(0., 0., 0., rotation); - rohacellPlate->AddNode(rohacellBaseWithHole, 0, new TGeoTranslation(0., 0., mZPlan[disk])); + rohacellPlate->AddNode(rohacellBaseWithHole, 0, + new TGeoTranslation(0., 0., mZPlan[disk])); ty = mSupportYDimensions[disk][0]; @@ -3590,74 +5393,128 @@ void HeatExchanger::createHalfDisk4(Int_t half) //=========================================================================================================== //=========================================================================================================== auto* partRohacell0 = - new TGeoBBox(Form("rohacellBase0_D4_H%d_%d", half, ipart), mSupportXDimensions[disk][ipart] / 2., + new TGeoBBox(Form("rohacellBase0_D4_H%d_%d", half, ipart), + mSupportXDimensions[disk][ipart] / 2., mSupportYDimensions[disk][ipart] / 2., mRohacellThickness); Double_t mShift; if (Geometry::sGrooves == 1) { - // **************** Creating grooves for the other parts of the rohacell plate ********************** + // **************** Creating grooves for the other parts of the rohacell + // plate ********************** for (Int_t iface = 1; iface > -2; iface -= 2) { // front and rear for (Int_t igroove = 0; igroove < 4; igroove++) { // 4 grooves if (ipart == 1) { - mPosition[ipart] = mXPosition4[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; + mPosition[ipart] = + mXPosition4[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1]; mShift = -mSupportYDimensions[disk][ipart - 1]; }; if (ipart == 2) { - mPosition[ipart] = mXPosition4[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2]; + mPosition[ipart] = + mXPosition4[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2]; }; if (ipart == 3) { - mPosition[ipart] = mXPosition4[igroove] - mSupportYDimensions[disk][ipart] / 2. - mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; - mShift = -mSupportYDimensions[disk][ipart - 1] - mSupportYDimensions[disk][ipart - 2] - mSupportYDimensions[disk][ipart - 3]; + mPosition[ipart] = + mXPosition4[igroove] - mSupportYDimensions[disk][ipart] / 2. - + mHalfDiskGap - mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; + mShift = -mSupportYDimensions[disk][ipart - 1] - + mSupportYDimensions[disk][ipart - 2] - + mSupportYDimensions[disk][ipart - 3]; }; for (Int_t ip = 0; ip < 7; ip++) { // each groove is made of 7 parts switch (ip) { case 0: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove] / 2., mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove] / 2., + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); if (igroove == 0 && iface == 1) { - rohacellBaseGroove[iCount] = new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", ip, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellBaseGroove[iCount] = + new TGeoSubtraction(partRohacell0, grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", + ip, igroove, iface, half), + rohacellBaseGroove[iCount]); }; break; case 1: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove], mPosition[ipart] + mRadius4[igroove], - iface * (mRohacellThickness + epsilon), rotationSideTorusR); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove], + mPosition[ipart] + mRadius4[igroove], + iface * (mRohacellThickness + epsilon), rotationSideTorusR); break; case 2: // Linear tilted - rotationTiltedLinearR = new TGeoRotation("rotationTiltedLinearRight", 90. - mAngle4[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[igroove], yPos4[igroove] + mShift - mHalfDiskGap - mSupportYDimensions[disk][ipart] / 2., iface * (mRohacellThickness + epsilon), rotationTiltedLinearR); + rotationTiltedLinearR = new TGeoRotation( + "rotationTiltedLinearRight", 90. - mAngle4[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[igroove], + yPos4[igroove] + mShift - mHalfDiskGap - + mSupportYDimensions[disk][ipart] / 2., + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearR); break; case 3: // Central Torus - transfo[ip][igroove] = new TGeoCombiTrans(0., mPosition[ipart] + yPos4[igroove] + mLpartial4[igroove] / 2 * TMath::Sin(mAngle4[igroove] * TMath::DegToRad()) - mRadiusCentralTore[igroove] * TMath::Cos(mAngle4[igroove] * TMath::DegToRad()) - mXPosition4[igroove], - iface * (mRohacellThickness + epsilon), rotationCentralTorus); + transfo[ip][igroove] = new TGeoCombiTrans( + 0., + mPosition[ipart] + yPos4[igroove] + + mLpartial4[igroove] / 2 * + TMath::Sin(mAngle4[igroove] * TMath::DegToRad()) - + mRadiusCentralTore[igroove] * + TMath::Cos(mAngle4[igroove] * TMath::DegToRad()) - + mXPosition4[igroove], + iface * (mRohacellThickness + epsilon), rotationCentralTorus); break; case 4: // Linear tilted - rotationTiltedLinearL = new TGeoRotation("rotationTiltedLinearLeft", 90. + mAngle4[igroove], 90., 0.); - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[4][0] / 2. + mMoreLength - xPos4[igroove]), yPos4[igroove] + mPosition[ipart] - mXPosition4[igroove], - iface * (mRohacellThickness + epsilon), rotationTiltedLinearL); + rotationTiltedLinearL = new TGeoRotation( + "rotationTiltedLinearLeft", 90. + mAngle4[igroove], 90., 0.); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[4][0] / 2. + mMoreLength - + xPos4[igroove]), + yPos4[igroove] + mPosition[ipart] - mXPosition4[igroove], + iface * (mRohacellThickness + epsilon), + rotationTiltedLinearL); break; case 5: // side torus - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove]), mRadius4[igroove] + mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationSideTorusL); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove]), + mRadius4[igroove] + mPosition[ipart], + iface * (mRohacellThickness + epsilon), rotationSideTorusL); break; case 6: // Linear - transfo[ip][igroove] = new TGeoCombiTrans(-(mSupportXDimensions[4][0] / 2. + mMoreLength - mLWater4[igroove] / 2.), mPosition[ipart], - iface * (mRohacellThickness + epsilon), rotationLinear); + transfo[ip][igroove] = new TGeoCombiTrans( + -(mSupportXDimensions[4][0] / 2. + mMoreLength - + mLWater4[igroove] / 2.), + mPosition[ipart], iface * (mRohacellThickness + epsilon), + rotationLinear); break; } if (!(ip == 0 && igroove == 0 && iface == 1)) { if (ip & 1) { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTorus[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTorus[ip][igroove], + nullptr, transfo[ip][igroove]); } else { - rohacellBaseGroove[iCount] = - new TGeoSubtraction(rohacellGroove[iCount - 1], grooveTube[ip][igroove], nullptr, transfo[ip][igroove]); + rohacellBaseGroove[iCount] = new TGeoSubtraction( + rohacellGroove[iCount - 1], grooveTube[ip][igroove], + nullptr, transfo[ip][igroove]); } - rohacellGroove[iCount] = new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", iCount, igroove, iface, half), rohacellBaseGroove[iCount]); + rohacellGroove[iCount] = + new TGeoCompositeShape(Form("rohacell4Groove%d_G%d_F%d_H%d", + iCount, igroove, iface, half), + rohacellBaseGroove[iCount]); } iCount++; } @@ -3674,30 +5531,42 @@ void HeatExchanger::createHalfDisk4(Int_t half) xnotch = 2.1; // half width ynotch = 0.4; // full height if (ipart == (mNPart[disk] - 1)) { - notchRohacell0 = new TGeoBBox(Form("notchRohacell0_D4_H%d", half), xnotch, ynotch, mRohacellThickness + 0.000001); - tnotch0 = new TGeoTranslation("tnotch0", 0., mSupportYDimensions[disk][ipart] / 2., 0.); + notchRohacell0 = new TGeoBBox(Form("notchRohacell0_D4_H%d", half), xnotch, + ynotch, mRohacellThickness + 0.000001); + tnotch0 = new TGeoTranslation("tnotch0", 0., + mSupportYDimensions[disk][ipart] / 2., 0.); tnotch0->RegisterYourself(); } //============================================================= if (Geometry::sGrooves == 0) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell0, nullptr, tnotch0); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D4_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(partRohacell0, notchRohacell0, + nullptr, tnotch0); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D4_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D4_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), partRohacell0, mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), + partRohacell0, mRohacell); } } if (Geometry::sGrooves == 1) { if (ipart == (mNPart[disk] - 1)) { - partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], notchRohacell0, nullptr, tnotch0); - auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D4_H%d", 0, half), partRohacellini); - partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), rhinit, mRohacell); + partRohacellini = new TGeoSubtraction(rohacellGroove[iCount - 1], + notchRohacell0, nullptr, tnotch0); + auto* rhinit = new TGeoCompositeShape(Form("rhinit%d_D4_H%d", 0, half), + partRohacellini); + partRohacell = new TGeoVolume( + Form("partRohacelli_D4_H%d_%d", half, ipart), rhinit, mRohacell); } if (ipart < (mNPart[disk] - 1)) { - partRohacell = new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), rohacellGroove[iCount - 1], mRohacell); + partRohacell = + new TGeoVolume(Form("partRohacelli_D4_H%d_%d", half, ipart), + rohacellGroove[iCount - 1], mRohacell); } } //=========================================================================================================== @@ -3705,11 +5574,15 @@ void HeatExchanger::createHalfDisk4(Int_t half) partRohacell->SetLineColor(kGray); rohacellPlate->AddNode(partRohacell, ipart, t); - //========== insert to locate the rohacell plate compare to the disk support ============= + //========== insert to locate the rohacell plate compare to the disk support + //============= if (ipart == (mNPart[disk] - 1)) { TGeoTranslation* tinsert4; - TGeoVolume* insert4 = gGeoManager->MakeBox(Form("insert4_H%d_%d", half, ipart), mPeek, 4.0 / 2., 0.44 / 2., mRohacellThickness); - Double_t ylocation = mSupportYDimensions[disk][0] + mHalfDiskGap + 0.44 / 2. - ynotch; + TGeoVolume* insert4 = + gGeoManager->MakeBox(Form("insert4_H%d_%d", half, ipart), mPeek, + 4.0 / 2., 0.44 / 2., mRohacellThickness); + Double_t ylocation = + mSupportYDimensions[disk][0] + mHalfDiskGap + 0.44 / 2. - ynotch; for (Int_t ip = 1; ip < mNPart[disk]; ip++) { ylocation = ylocation + mSupportYDimensions[disk][ip]; } @@ -3747,9 +5620,12 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) Float_t radius1; //----------------------------------------------------------------- if (disk == 0 || disk == 1 || disk == 2) { - auto* mCoolingPipe1 = new TGeoVolumeAssembly(Form("cooling_pipe1_H%d", half)); - auto* mCoolingPipeRear1 = new TGeoVolumeAssembly(Form("cooling_pipeRear1_H%d", half)); - auto* mCoolingPipeRear2 = new TGeoVolumeAssembly(Form("cooling_pipeRear2_H%d", half)); + auto* mCoolingPipe1 = + new TGeoVolumeAssembly(Form("cooling_pipe1_H%d", half)); + auto* mCoolingPipeRear1 = + new TGeoVolumeAssembly(Form("cooling_pipeRear1_H%d", half)); + auto* mCoolingPipeRear2 = + new TGeoVolumeAssembly(Form("cooling_pipeRear2_H%d", half)); if (disk == 0) { length1 = 1.5; } @@ -3759,14 +5635,18 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) if (disk == 2) { length1 = 0.5; } - Tube1 = gGeoManager->MakeTube(Form("Tube1_H%d_D%d", half, disk), mPipe, rin, rout, length1 / 2); - TubeW1 = gGeoManager->MakeTube(Form("TubeW1_H%d_D%d", half, disk), mWater, 0., rin, length1 / 2); + Tube1 = gGeoManager->MakeTube(Form("Tube1_H%d_D%d", half, disk), mPipe, rin, + rout, length1 / 2); + TubeW1 = gGeoManager->MakeTube(Form("TubeW1_H%d_D%d", half, disk), mWater, + 0., rin, length1 / 2); TGeoTranslation* tTube1 = new TGeoTranslation(0.0, 0.0, 0.0); tTube1->RegisterYourself(); radius1 = 0.4; - Torus1 = gGeoManager->MakeTorus(Form("Torus1_H%d_D%d", half, disk), mPipe, radius1, rin, rout, 0., 90.); - TorusW1 = gGeoManager->MakeTorus(Form("TorusW1_H%d_D%d", half, disk), mWater, radius1, 0., rin, 0., 90.); + Torus1 = gGeoManager->MakeTorus(Form("Torus1_H%d_D%d", half, disk), mPipe, + radius1, rin, rout, 0., 90.); + TorusW1 = gGeoManager->MakeTorus(Form("TorusW1_H%d_D%d", half, disk), + mWater, radius1, 0., rin, 0., 90.); rTorus1 = new TGeoRotation("rotationTorus1", 0.0, 90.0, 0.0); rTorus1->RegisterYourself(); transfoTorus1 = new TGeoCombiTrans(-radius1, 0., length1 / 2, rTorus1); @@ -3781,22 +5661,30 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) if (disk == 2) { length2 = 0.55; } - TGeoVolume* Tube2 = gGeoManager->MakeTube(Form("Tube2_H%d_D%d", half, disk), mPipe, rin, rout, length2 / 2); - TGeoVolume* TubeW2 = gGeoManager->MakeTube(Form("TubeW2_H%d_D%d", half, disk), mWater, 0., rin, length2 / 2); + TGeoVolume* Tube2 = gGeoManager->MakeTube(Form("Tube2_H%d_D%d", half, disk), + mPipe, rin, rout, length2 / 2); + TGeoVolume* TubeW2 = gGeoManager->MakeTube( + Form("TubeW2_H%d_D%d", half, disk), mWater, 0., rin, length2 / 2); TGeoRotation* rTube2 = new TGeoRotation("rotationTube2", 90.0, 90.0, 0.0); rTube2->RegisterYourself(); - TGeoCombiTrans* transfoTube2 = new TGeoCombiTrans(-length2 / 2 - radius1, 0., length1 / 2 + radius1, rTube2); + TGeoCombiTrans* transfoTube2 = new TGeoCombiTrans( + -length2 / 2 - radius1, 0., length1 / 2 + radius1, rTube2); transfoTube2->RegisterYourself(); Float_t radius2 = 4.; if (disk == 2) { radius2 = 3.5; } - TGeoVolume* Torus2 = gGeoManager->MakeTorus(Form("Torus2_H%d_D%d", half, disk), mPipe, radius2, rin, rout, 0., -90.); - TGeoVolume* TorusW2 = gGeoManager->MakeTorus(Form("TorusW2_H%d_D%d", half, disk), mWater, radius2, 0., rin, 0., -90.); + TGeoVolume* Torus2 = + gGeoManager->MakeTorus(Form("Torus2_H%d_D%d", half, disk), mPipe, + radius2, rin, rout, 0., -90.); + TGeoVolume* TorusW2 = + gGeoManager->MakeTorus(Form("TorusW2_H%d_D%d", half, disk), mWater, + radius2, 0., rin, 0., -90.); TGeoRotation* rTorus2 = new TGeoRotation("rotationTorus2", 180.0, 0.0, 0.0); rTorus2->RegisterYourself(); - TGeoCombiTrans* transfoTorus2 = new TGeoCombiTrans(-length2 - radius1, -radius2, length1 / 2 + radius1, rTorus2); + TGeoCombiTrans* transfoTorus2 = new TGeoCombiTrans( + -length2 - radius1, -radius2, length1 / 2 + radius1, rTorus2); transfoTorus2->RegisterYourself(); Float_t length3; @@ -3809,28 +5697,45 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) if (disk == 2) { length3 = 4.2; } - TGeoVolume* Tube3 = gGeoManager->MakeTube(Form("Tube3_H%d_D%d", half, disk), mPipe, rin, rout, length3 / 2); - TGeoVolume* TubeW3 = gGeoManager->MakeTube(Form("TubeW3_H%d_D%d", half, disk), mWater, 0., rin, length3 / 2); + TGeoVolume* Tube3 = gGeoManager->MakeTube(Form("Tube3_H%d_D%d", half, disk), + mPipe, rin, rout, length3 / 2); + TGeoVolume* TubeW3 = gGeoManager->MakeTube( + Form("TubeW3_H%d_D%d", half, disk), mWater, 0., rin, length3 / 2); TGeoRotation* rTube3 = new TGeoRotation("rotationTube3", 0.0, -90.0, 0.0); rTube3->RegisterYourself(); - TGeoCombiTrans* transfoTube3 = new TGeoCombiTrans(-length2 - radius2 - radius1, -radius2 - length3 / 2, length1 / 2 + radius1, rTube3); + TGeoCombiTrans* transfoTube3 = + new TGeoCombiTrans(-length2 - radius2 - radius1, -radius2 - length3 / 2, + length1 / 2 + radius1, rTube3); transfoTube3->RegisterYourself(); - Float_t length4 = 16.0; // one single pipe instead of 3 pipes coming from the 3 first disks + Float_t length4 = 16.0; // one single pipe instead of 3 pipes coming from + // the 3 first disks Float_t rin4 = 0.216; Float_t rout4 = 0.346; - TGeoVolume* Tube4 = gGeoManager->MakeTube(Form("Tube4_H%d_D%d", half, disk), mPipe, rin4, rout4, length4 / 2); - TGeoVolume* TubeW4 = gGeoManager->MakeTube(Form("TubeW4_H%d_D%d", half, disk), mWater, 0., rin4, length4 / 2); + TGeoVolume* Tube4 = gGeoManager->MakeTube(Form("Tube4_H%d_D%d", half, disk), + mPipe, rin4, rout4, length4 / 2); + TGeoVolume* TubeW4 = gGeoManager->MakeTube( + Form("TubeW4_H%d_D%d", half, disk), mWater, 0., rin4, length4 / 2); Float_t theta4 = 10.5; // horizontal plane angle Float_t phi4 = 35; // vertical plane angle - TGeoRotation* rTube4 = new TGeoRotation("rotationTube4", 90.0 + theta4, 90.0 + phi4, 0.0); + TGeoRotation* rTube4 = + new TGeoRotation("rotationTube4", 90.0 + theta4, 90.0 + phi4, 0.0); rTube4->RegisterYourself(); // next line, the x and z axis are reversed in the location... Float_t dx = 2.0; - Float_t xTube4 = length1 / 2. + radius1 + TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Sin(phi4 * TMath::DegToRad()) * length4 / 2 * 0.8; - Float_t yTube4 = -radius2 - length3 - TMath::Sin(theta4 * TMath::DegToRad()) * length4 / 2 * 0.8; - Float_t zTube4 = -radius1 - length2 - radius2 - TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Cos(phi4 * TMath::DegToRad()) * length4 / 2 * 0.8 - 0.2; - TGeoCombiTrans* transfoTube4 = new TGeoCombiTrans(zTube4, yTube4 - 0.2, xTube4 - 0.1, rTube4); + Float_t xTube4 = length1 / 2. + radius1 + + TMath::Cos(theta4 * TMath::DegToRad()) * + TMath::Sin(phi4 * TMath::DegToRad()) * length4 / 2 * + 0.8; + Float_t yTube4 = -radius2 - length3 - + TMath::Sin(theta4 * TMath::DegToRad()) * length4 / 2 * 0.8; + Float_t zTube4 = -radius1 - length2 - radius2 - + TMath::Cos(theta4 * TMath::DegToRad()) * + TMath::Cos(phi4 * TMath::DegToRad()) * length4 / 2 * + 0.8 - + 0.2; + TGeoCombiTrans* transfoTube4 = + new TGeoCombiTrans(zTube4, yTube4 - 0.2, xTube4 - 0.1, rTube4); transfoTube4->RegisterYourself(); Float_t length5 = 13.0; // one single pipe instead of 5 pipes @@ -3848,16 +5753,34 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) nhi[2] = TMath::Cos(theta); Float_t rin5 = 0.278; Float_t rout5 = 0.447; - TGeoVolume* Tube5 = gGeoManager->MakeCtub(Form("Tube5_H%d_D%d", half, disk), mPipe, rin5, rout5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); - TGeoVolume* TubeW5 = gGeoManager->MakeCtub(Form("TubeW5_H%d_D%d", half, disk), mWater, 0., rin5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + TGeoVolume* Tube5 = gGeoManager->MakeCtub( + Form("Tube5_H%d_D%d", half, disk), mPipe, rin5, rout5, length5 / 2, 0., + 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + TGeoVolume* TubeW5 = gGeoManager->MakeCtub( + Form("TubeW5_H%d_D%d", half, disk), mWater, 0., rin5, length5 / 2, 0., + 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); Float_t theta5 = 11.5; // angle from the "horizontal" plane x,z Float_t phi5 = 16.7; // "azimutal" angle - TGeoRotation* rTube5 = new TGeoRotation("rotationTube5", 90.0 + theta5, 90.0 + phi5, 0.0); + TGeoRotation* rTube5 = + new TGeoRotation("rotationTube5", 90.0 + theta5, 90.0 + phi5, 0.0); rTube5->RegisterYourself(); - Float_t xTube5 = xTube4 + TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Sin(phi4 * TMath::DegToRad()) * length4 / 2 + TMath::Cos(theta5 * TMath::DegToRad()) * TMath::Sin(phi5 * TMath::DegToRad()) * length5 / 2 * 1.03; - Float_t yTube5 = yTube4 - TMath::Sin(theta4 * TMath::DegToRad()) * length4 / 2 - TMath::Sin(theta5 * TMath::DegToRad()) * length5 / 2 * 1.03 + 0.2; - Float_t zTube5 = zTube4 - TMath::Cos(theta4 * TMath::DegToRad()) * TMath::Cos(phi4 * TMath::DegToRad()) * length4 / 2 - TMath::Cos(theta5 * TMath::DegToRad()) * TMath::Cos(phi5 * TMath::DegToRad()) * length5 / 2 * 1.03; - TGeoCombiTrans* transfoTube5 = new TGeoCombiTrans(zTube5, yTube5, xTube5, rTube5); + Float_t xTube5 = xTube4 + + TMath::Cos(theta4 * TMath::DegToRad()) * + TMath::Sin(phi4 * TMath::DegToRad()) * length4 / 2 + + TMath::Cos(theta5 * TMath::DegToRad()) * + TMath::Sin(phi5 * TMath::DegToRad()) * length5 / 2 * + 1.03; + Float_t yTube5 = + yTube4 - TMath::Sin(theta4 * TMath::DegToRad()) * length4 / 2 - + TMath::Sin(theta5 * TMath::DegToRad()) * length5 / 2 * 1.03 + 0.2; + Float_t zTube5 = zTube4 - + TMath::Cos(theta4 * TMath::DegToRad()) * + TMath::Cos(phi4 * TMath::DegToRad()) * length4 / 2 - + TMath::Cos(theta5 * TMath::DegToRad()) * + TMath::Cos(phi5 * TMath::DegToRad()) * length5 / 2 * + 1.03; + TGeoCombiTrans* transfoTube5 = + new TGeoCombiTrans(zTube5, yTube5, xTube5, rTube5); transfoTube5->RegisterYourself(); Tube1->SetLineColor(kGray); @@ -3890,32 +5813,54 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) } //----------------------------------------------------------------- - auto* mCoolingPipe2 = new TGeoVolumeAssembly(Form("cooling_pipe2_H%d_D%d", half, disk)); - TGeoVolume* Tube1p = gGeoManager->MakeTube(Form("Tube1p_H%d_D%d", half, disk), mPipe, rin, rout, length1 / 2); - TGeoVolume* TubeW1p = gGeoManager->MakeTube(Form("TubeW1p_H%d_D%d", half, disk), mWater, 0., rin, length1 / 2); - - TGeoVolume* Torus1p = gGeoManager->MakeTorus(Form("Torus1p_H%d_D%d", half, disk), mPipe, radius1, rin, rout, 0., 90.); - TGeoVolume* TorusW1p = gGeoManager->MakeTorus(Form("TorusW1p_H%d_D%d", half, disk), mWater, radius1, 0., rin, 0., 90.); - - TGeoVolume* Tube2p = gGeoManager->MakeTube(Form("Tube2p_H%d_D%d", half, disk), mPipe, rin, rout, length2 / 2); - TGeoVolume* TubeW2p = gGeoManager->MakeTube(Form("TubeW2p_H%d_D%d", half, disk), mWater, 0., rin, length2 / 2); - - TGeoVolume* Torus2p = gGeoManager->MakeTorus(Form("Torus2p_H%d_D%d", half, disk), mPipe, radius2, rin, rout, 0., 90.); - TGeoVolume* TorusW2p = gGeoManager->MakeTorus(Form("TorusW2p_H%d_D%d", half, disk), mWater, radius2, 0., rin, 0., 90.); - - TGeoVolume* Tube3p = gGeoManager->MakeTube(Form("Tube3p_H%d_D%d", half, disk), mPipe, rin, rout, length3 / 2); - TGeoVolume* TubeW3p = gGeoManager->MakeTube(Form("TubeW3p_H%d_D%d", half, disk), mWater, 0., rin, length3 / 2); - - TGeoRotation* rTorus2p = new TGeoRotation("rotationTorus2p", 180.0, 0.0, 0.0); + auto* mCoolingPipe2 = + new TGeoVolumeAssembly(Form("cooling_pipe2_H%d_D%d", half, disk)); + TGeoVolume* Tube1p = gGeoManager->MakeTube( + Form("Tube1p_H%d_D%d", half, disk), mPipe, rin, rout, length1 / 2); + TGeoVolume* TubeW1p = gGeoManager->MakeTube( + Form("TubeW1p_H%d_D%d", half, disk), mWater, 0., rin, length1 / 2); + + TGeoVolume* Torus1p = + gGeoManager->MakeTorus(Form("Torus1p_H%d_D%d", half, disk), mPipe, + radius1, rin, rout, 0., 90.); + TGeoVolume* TorusW1p = + gGeoManager->MakeTorus(Form("TorusW1p_H%d_D%d", half, disk), mWater, + radius1, 0., rin, 0., 90.); + + TGeoVolume* Tube2p = gGeoManager->MakeTube( + Form("Tube2p_H%d_D%d", half, disk), mPipe, rin, rout, length2 / 2); + TGeoVolume* TubeW2p = gGeoManager->MakeTube( + Form("TubeW2p_H%d_D%d", half, disk), mWater, 0., rin, length2 / 2); + + TGeoVolume* Torus2p = + gGeoManager->MakeTorus(Form("Torus2p_H%d_D%d", half, disk), mPipe, + radius2, rin, rout, 0., 90.); + TGeoVolume* TorusW2p = + gGeoManager->MakeTorus(Form("TorusW2p_H%d_D%d", half, disk), mWater, + radius2, 0., rin, 0., 90.); + + TGeoVolume* Tube3p = gGeoManager->MakeTube( + Form("Tube3p_H%d_D%d", half, disk), mPipe, rin, rout, length3 / 2); + TGeoVolume* TubeW3p = gGeoManager->MakeTube( + Form("TubeW3p_H%d_D%d", half, disk), mWater, 0., rin, length3 / 2); + + TGeoRotation* rTorus2p = + new TGeoRotation("rotationTorus2p", 180.0, 0.0, 0.0); rTorus2p->RegisterYourself(); - TGeoCombiTrans* transfoTorus2p = new TGeoCombiTrans(-length2 - radius1, radius2, length1 / 2 + radius1, rTorus2p); + TGeoCombiTrans* transfoTorus2p = new TGeoCombiTrans( + -length2 - radius1, radius2, length1 / 2 + radius1, rTorus2p); transfoTorus2p->RegisterYourself(); - TGeoCombiTrans* transfoTube3p = new TGeoCombiTrans(-length2 - radius2 - radius1, radius2 + length3 / 2, length1 / 2 + radius1, rTube3); + TGeoCombiTrans* transfoTube3p = + new TGeoCombiTrans(-length2 - radius2 - radius1, radius2 + length3 / 2, + length1 / 2 + radius1, rTube3); transfoTube3p->RegisterYourself(); - TGeoRotation* rTube4p = new TGeoRotation(Form("rotationTube4p_H%d_D%d", half, disk), 90.0 - theta4, phi4 - 90.0, 0.0); + TGeoRotation* rTube4p = + new TGeoRotation(Form("rotationTube4p_H%d_D%d", half, disk), + 90.0 - theta4, phi4 - 90.0, 0.0); rTube4p->RegisterYourself(); - TGeoCombiTrans* transfoTube4p = new TGeoCombiTrans(zTube4, -yTube4 + 0.2, xTube4 - 0.1, rTube4p); + TGeoCombiTrans* transfoTube4p = + new TGeoCombiTrans(zTube4, -yTube4 + 0.2, xTube4 - 0.1, rTube4p); transfoTube4p->RegisterYourself(); Tube1p->SetLineColor(kGray); @@ -3938,8 +5883,10 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) if (disk == 0) { - TGeoVolume* Tube4p = gGeoManager->MakeTube(Form("Tube4p_H%d_D%d", half, disk), mPipe, rin4, rout4, length4 / 2); - TGeoVolume* TubeW4p = gGeoManager->MakeTube(Form("TubeW4p_H%d_D%d", half, disk), mWater, 0., rin4, length4 / 2); + TGeoVolume* Tube4p = gGeoManager->MakeTube( + Form("Tube4p_H%d_D%d", half, disk), mPipe, rin4, rout4, length4 / 2); + TGeoVolume* TubeW4p = gGeoManager->MakeTube( + Form("TubeW4p_H%d_D%d", half, disk), mWater, 0., rin4, length4 / 2); Tube4p->SetLineColor(kGray); TubeW4p->SetLineColor(kBlue); @@ -3955,9 +5902,14 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) nhi[0] = TMath::Sin(theta) * TMath::Cos(phi); nhi[1] = TMath::Sin(theta) * TMath::Sin(phi); nhi[2] = TMath::Cos(theta); - TGeoVolume* Tube5p = gGeoManager->MakeCtub(Form("Tube5p_H%d_D%d", half, disk), mPipe, rin5, rout5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); - TGeoVolume* TubeW5p = gGeoManager->MakeCtub(Form("TubeW5p_H%d_D%d", half, disk), mWater, 0., rin5, length5 / 2, 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); - TGeoRotation* rTube5p = new TGeoRotation("rotationTube5p", -90.0 - theta5, -(90.0 + phi5), 0.0); + TGeoVolume* Tube5p = gGeoManager->MakeCtub( + Form("Tube5p_H%d_D%d", half, disk), mPipe, rin5, rout5, length5 / 2, + 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + TGeoVolume* TubeW5p = gGeoManager->MakeCtub( + Form("TubeW5p_H%d_D%d", half, disk), mWater, 0., rin5, length5 / 2, + 0., 360., nlow[0], nlow[1], nlow[2], nhi[0], nhi[1], nhi[2]); + TGeoRotation* rTube5p = new TGeoRotation("rotationTube5p", -90.0 - theta5, + -(90.0 + phi5), 0.0); rTube5p->RegisterYourself(); TGeoCombiTrans* transfoTube5p; transfoTube5p = new TGeoCombiTrans(zTube5, -yTube5, xTube5, rTube5p); @@ -3975,15 +5927,19 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) TGeoRotation* rotation1 = new TGeoRotation("rotation1", 90., 90., 90.); rotation1->RegisterYourself(); // 0.75 = Y location from manifold line 836 - transfoCoolingPipe1 = new TGeoCombiTrans(13.8 + length1 / 2, 0.75, 0.0, rotation1); + transfoCoolingPipe1 = + new TGeoCombiTrans(13.8 + length1 / 2, 0.75, 0.0, rotation1); transfoCoolingPipe1->RegisterYourself(); - transfoCoolingPipeRear1 = new TGeoCombiTrans(13.8 + length1 / 2, 0.75, 0.0, rotation1); + transfoCoolingPipeRear1 = + new TGeoCombiTrans(13.8 + length1 / 2, 0.75, 0.0, rotation1); transfoCoolingPipeRear1->RegisterYourself(); TGeoRotation* rotation2 = new TGeoRotation("rotation2", 90., 90., 90.); rotation2->RegisterYourself(); - transfoCoolingPipe2 = new TGeoCombiTrans(13.8 + length1 / 2, -0.75, 0.0, rotation2); + transfoCoolingPipe2 = + new TGeoCombiTrans(13.8 + length1 / 2, -0.75, 0.0, rotation2); transfoCoolingPipe2->RegisterYourself(); - transfoCoolingPipeRear2 = new TGeoCombiTrans(13.8 + length1 / 2, -0.75, 0.0, rotation2); + transfoCoolingPipeRear2 = + new TGeoCombiTrans(13.8 + length1 / 2, -0.75, 0.0, rotation2); transfoCoolingPipeRear2->RegisterYourself(); mHalfDisk->AddNode(mCoolingPipe1, 1, transfoCoolingPipe1); mHalfDisk->AddNode(mCoolingPipeRear1, 1, transfoCoolingPipeRear1); @@ -3995,46 +5951,70 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) //================================================================= if (disk == 3) { // One diagonal side - auto* mCoolingPipe3 = new TGeoVolumeAssembly(Form("cooling_pipe3_H%d_D%d", half, disk)); + auto* mCoolingPipe3 = + new TGeoVolumeAssembly(Form("cooling_pipe3_H%d_D%d", half, disk)); Float_t length1_3 = 4.0; - TGeoVolume* Tube1_3 = gGeoManager->MakeTube(Form("Tube1_3_H%d_D%d", half, disk), mPipe, rin, rout, length1_3 / 2); - TGeoVolume* TubeW1_3 = gGeoManager->MakeTube(Form("TubeW1_3_H%d_D%d", half, disk), mWater, 0., rin, length1_3 / 2); + TGeoVolume* Tube1_3 = gGeoManager->MakeTube( + Form("Tube1_3_H%d_D%d", half, disk), mPipe, rin, rout, length1_3 / 2); + TGeoVolume* TubeW1_3 = gGeoManager->MakeTube( + Form("TubeW1_3_H%d_D%d", half, disk), mWater, 0., rin, length1_3 / 2); TGeoTranslation* tTube1_3 = new TGeoTranslation(0.0, 0.0, 0.0); tTube1_3->RegisterYourself(); Float_t radius1_3 = 0.4; - TGeoVolume* Torus1_3 = gGeoManager->MakeTorus(Form("Torus1_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); - TGeoVolume* TorusW1_3 = gGeoManager->MakeTorus(Form("TorusW1_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); - TGeoRotation* rTorus1_3 = new TGeoRotation("rotationTorus1_3", 90.0, 90.0, 0.0); + TGeoVolume* Torus1_3 = + gGeoManager->MakeTorus(Form("Torus1_3_H%d_D%d", half, disk), mPipe, + radius1_3, rin, rout, 0., 90.); + TGeoVolume* TorusW1_3 = + gGeoManager->MakeTorus(Form("TorusW1_3_H%d_D%d", half, disk), mWater, + radius1_3, 0., rin, 0., 90.); + TGeoRotation* rTorus1_3 = + new TGeoRotation("rotationTorus1_3", 90.0, 90.0, 0.0); rTorus1_3->RegisterYourself(); - TGeoCombiTrans* transfoTorus1_3 = new TGeoCombiTrans(0.0, -radius1_3, length1_3 / 2, rTorus1_3); + TGeoCombiTrans* transfoTorus1_3 = + new TGeoCombiTrans(0.0, -radius1_3, length1_3 / 2, rTorus1_3); transfoTorus1_3->RegisterYourself(); Float_t length2_3; if (disk == 3) { length2_3 = 10.4; } - TGeoVolume* Tube2_3 = gGeoManager->MakeTube(Form("Tube2_3_H%d_D%d", half, disk), mPipe, rin, rout, length2_3 / 2); - TGeoVolume* TubeW2_3 = gGeoManager->MakeTube(Form("TubeW2_3_H%d_D%d", half, disk), mWater, 0., rin, length2_3 / 2); - TGeoRotation* rTube2_3 = new TGeoRotation("rotationTube2_3", 180.0, 90.0, 90.0); + TGeoVolume* Tube2_3 = gGeoManager->MakeTube( + Form("Tube2_3_H%d_D%d", half, disk), mPipe, rin, rout, length2_3 / 2); + TGeoVolume* TubeW2_3 = gGeoManager->MakeTube( + Form("TubeW2_3_H%d_D%d", half, disk), mWater, 0., rin, length2_3 / 2); + TGeoRotation* rTube2_3 = + new TGeoRotation("rotationTube2_3", 180.0, 90.0, 90.0); rTube2_3->RegisterYourself(); - TGeoCombiTrans* transfoTube2_3 = new TGeoCombiTrans(0., -length2_3 / 2 - radius1_3, length1_3 / 2 + radius1_3, rTube2_3); + TGeoCombiTrans* transfoTube2_3 = new TGeoCombiTrans( + 0., -length2_3 / 2 - radius1_3, length1_3 / 2 + radius1_3, rTube2_3); transfoTube2_3->RegisterYourself(); - TGeoVolume* Torus2_3 = gGeoManager->MakeTorus(Form("Torus2_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); - TGeoVolume* TorusW2_3 = gGeoManager->MakeTorus(Form("TorusW2_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); - TGeoRotation* rTorus2_3 = new TGeoRotation("rotationTorus2_3", 90.0, 90.0, 180.0); + TGeoVolume* Torus2_3 = + gGeoManager->MakeTorus(Form("Torus2_3_H%d_D%d", half, disk), mPipe, + radius1_3, rin, rout, 0., 90.); + TGeoVolume* TorusW2_3 = + gGeoManager->MakeTorus(Form("TorusW2_3_H%d_D%d", half, disk), mWater, + radius1_3, 0., rin, 0., 90.); + TGeoRotation* rTorus2_3 = + new TGeoRotation("rotationTorus2_3", 90.0, 90.0, 180.0); rTorus2_3->RegisterYourself(); - TGeoCombiTrans* transfoTorus2_3 = new TGeoCombiTrans(0.0, -length2_3 - radius1_3, length1_3 / 2 + radius1_3 + radius1_3, rTorus2_3); + TGeoCombiTrans* transfoTorus2_3 = + new TGeoCombiTrans(0.0, -length2_3 - radius1_3, + length1_3 / 2 + radius1_3 + radius1_3, rTorus2_3); transfoTorus2_3->RegisterYourself(); Float_t length3_3; if (disk == 3) { length3_3 = 1.5; } - TGeoVolume* Tube3_3 = gGeoManager->MakeTube(Form("Tube3_3_H%d_D%d", half, disk), mPipe, rin, rout, length3_3 / 2); - TGeoVolume* TubeW3_3 = gGeoManager->MakeTube(Form("TubeW3_3_H%d_D%d", half, disk), mWater, 0., rin, length3_3 / 2); + TGeoVolume* Tube3_3 = gGeoManager->MakeTube( + Form("Tube3_3_H%d_D%d", half, disk), mPipe, rin, rout, length3_3 / 2); + TGeoVolume* TubeW3_3 = gGeoManager->MakeTube( + Form("TubeW3_3_H%d_D%d", half, disk), mWater, 0., rin, length3_3 / 2); TGeoRotation* rTube3_3 = new TGeoRotation("rotationTube3_3", 0.0, 0.0, 0.0); rTube3_3->RegisterYourself(); - TGeoCombiTrans* transfoTube3_3 = new TGeoCombiTrans(0., -length2_3 - radius1_3 - radius1_3, length1_3 / 2 + radius1_3 + radius1_3 + length3_3 / 2, rTube3_3); + TGeoCombiTrans* transfoTube3_3 = new TGeoCombiTrans( + 0., -length2_3 - radius1_3 - radius1_3, + length1_3 / 2 + radius1_3 + radius1_3 + length3_3 / 2, rTube3_3); transfoTube3_3->RegisterYourself(); Tube1_3->SetLineColor(kGray); @@ -4058,21 +6038,37 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) TGeoCombiTrans* transfoCoolingPipe3_3 = nullptr; TGeoRotation* rotation3_3 = new TGeoRotation("rotation3_3", 90., 90., 76.); rotation3_3->RegisterYourself(); - transfoCoolingPipe3_3 = new TGeoCombiTrans(17. + length1_3 / 2, 0.75, 0.0, rotation3_3); + transfoCoolingPipe3_3 = + new TGeoCombiTrans(17. + length1_3 / 2, 0.75, 0.0, rotation3_3); // ------------------Other diagonal side - auto* mCoolingPipe4 = new TGeoVolumeAssembly(Form("cooling_pipe4_H%d_D%d", half, disk)); - - TGeoVolume* Tube1p_3 = gGeoManager->MakeTube(Form("Tube1p_3_H%d_D%d", half, disk), mPipe, rin, rout, length1_3 / 2); - TGeoVolume* Torus1p_3 = gGeoManager->MakeTorus(Form("Torus1p_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); - TGeoVolume* Tube2p_3 = gGeoManager->MakeTube(Form("Tube2p_3_H%d_D%d", half, disk), mPipe, rin, rout, length2_3 / 2); - TGeoVolume* Torus2p_3 = gGeoManager->MakeTorus(Form("Torus2p_3_H%d_D%d", half, disk), mPipe, radius1_3, rin, rout, 0., 90.); - TGeoVolume* Tube3p_3 = gGeoManager->MakeTube(Form("Tube3p_3_H%d_D%d", half, disk), mPipe, rin, rout, length3_3 / 2); - TGeoVolume* TubeW1p_3 = gGeoManager->MakeTube(Form("TubeW1p_3_H%d_D%d", half, disk), mWater, 0, rin, length1_3 / 2); - TGeoVolume* TorusW1p_3 = gGeoManager->MakeTorus(Form("TorusW1p_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); - TGeoVolume* TubeW2p_3 = gGeoManager->MakeTube(Form("TubeW2p_3_H%d_D%d", half, disk), mWater, 0., rin, length2_3 / 2); - TGeoVolume* TorusW2p_3 = gGeoManager->MakeTorus(Form("TorusW2p_3_H%d_D%d", half, disk), mWater, radius1_3, 0., rin, 0., 90.); - TGeoVolume* TubeW3p_3 = gGeoManager->MakeTube(Form("TubeW3p_3_H%d_D%d", half, disk), mWater, 0., rin, length3_3 / 2); + auto* mCoolingPipe4 = + new TGeoVolumeAssembly(Form("cooling_pipe4_H%d_D%d", half, disk)); + + TGeoVolume* Tube1p_3 = gGeoManager->MakeTube( + Form("Tube1p_3_H%d_D%d", half, disk), mPipe, rin, rout, length1_3 / 2); + TGeoVolume* Torus1p_3 = + gGeoManager->MakeTorus(Form("Torus1p_3_H%d_D%d", half, disk), mPipe, + radius1_3, rin, rout, 0., 90.); + TGeoVolume* Tube2p_3 = gGeoManager->MakeTube( + Form("Tube2p_3_H%d_D%d", half, disk), mPipe, rin, rout, length2_3 / 2); + TGeoVolume* Torus2p_3 = + gGeoManager->MakeTorus(Form("Torus2p_3_H%d_D%d", half, disk), mPipe, + radius1_3, rin, rout, 0., 90.); + TGeoVolume* Tube3p_3 = gGeoManager->MakeTube( + Form("Tube3p_3_H%d_D%d", half, disk), mPipe, rin, rout, length3_3 / 2); + TGeoVolume* TubeW1p_3 = gGeoManager->MakeTube( + Form("TubeW1p_3_H%d_D%d", half, disk), mWater, 0, rin, length1_3 / 2); + TGeoVolume* TorusW1p_3 = + gGeoManager->MakeTorus(Form("TorusW1p_3_H%d_D%d", half, disk), mWater, + radius1_3, 0., rin, 0., 90.); + TGeoVolume* TubeW2p_3 = gGeoManager->MakeTube( + Form("TubeW2p_3_H%d_D%d", half, disk), mWater, 0., rin, length2_3 / 2); + TGeoVolume* TorusW2p_3 = + gGeoManager->MakeTorus(Form("TorusW2p_3_H%d_D%d", half, disk), mWater, + radius1_3, 0., rin, 0., 90.); + TGeoVolume* TubeW3p_3 = gGeoManager->MakeTube( + Form("TubeW3p_3_H%d_D%d", half, disk), mWater, 0., rin, length3_3 / 2); Tube1p_3->SetLineColor(kGray); Torus1p_3->SetLineColor(kGray); @@ -4095,7 +6091,8 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) TGeoCombiTrans* transfoCoolingPipe4_3 = nullptr; TGeoRotation* rotation4_3 = new TGeoRotation("rotation4_3", 90., 90., -76.); rotation4_3->RegisterYourself(); - transfoCoolingPipe4_3 = new TGeoCombiTrans(17. + length1_3 / 2, -0.75, 0.0, rotation4_3); + transfoCoolingPipe4_3 = + new TGeoCombiTrans(17. + length1_3 / 2, -0.75, 0.0, rotation4_3); transfoCoolingPipe4_3->RegisterYourself(); mHalfDisk->AddNode(mCoolingPipe3, 1, transfoCoolingPipe3_3); @@ -4105,48 +6102,72 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) //================================================================= if (disk == 4) { // One diagonal side - auto* mCoolingPipe3 = new TGeoVolumeAssembly(Form("cooling_pipe3_H%d_D%d", half, disk)); + auto* mCoolingPipe3 = + new TGeoVolumeAssembly(Form("cooling_pipe3_H%d_D%d", half, disk)); Float_t length1_4 = 3.0; - TGeoVolume* Tube1_4 = gGeoManager->MakeTube(Form("Tube1_4_H%d_D%d", half, disk), mPipe, rin, rout, length1_4 / 2); - TGeoVolume* TubeW1_4 = gGeoManager->MakeTube(Form("TubeW1_4_H%d_D%d", half, disk), mWater, 0., rin, length1_4 / 2); + TGeoVolume* Tube1_4 = gGeoManager->MakeTube( + Form("Tube1_4_H%d_D%d", half, disk), mPipe, rin, rout, length1_4 / 2); + TGeoVolume* TubeW1_4 = gGeoManager->MakeTube( + Form("TubeW1_4_H%d_D%d", half, disk), mWater, 0., rin, length1_4 / 2); TGeoTranslation* tTube1_4 = new TGeoTranslation(0.0, 0.0, 0.0); tTube1_4->RegisterYourself(); Float_t radius1_4 = 0.4; - TGeoVolume* Torus1_4 = gGeoManager->MakeTorus(Form("Torus1_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); - TGeoVolume* TorusW1_4 = gGeoManager->MakeTorus(Form("TorusW1_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); - TGeoRotation* rTorus1_4 = new TGeoRotation("rotationTorus1_4", 90.0, 90.0, 0.0); + TGeoVolume* Torus1_4 = + gGeoManager->MakeTorus(Form("Torus1_4_H%d_D%d", half, disk), mPipe, + radius1_4, rin, rout, 0., 90.); + TGeoVolume* TorusW1_4 = + gGeoManager->MakeTorus(Form("TorusW1_4_H%d_D%d", half, disk), mWater, + radius1_4, 0., rin, 0., 90.); + TGeoRotation* rTorus1_4 = + new TGeoRotation("rotationTorus1_4", 90.0, 90.0, 0.0); rTorus1_4->RegisterYourself(); - TGeoCombiTrans* transfoTorus1_4 = new TGeoCombiTrans(0.0, -radius1_4, length1_4 / 2, rTorus1_4); + TGeoCombiTrans* transfoTorus1_4 = + new TGeoCombiTrans(0.0, -radius1_4, length1_4 / 2, rTorus1_4); transfoTorus1_4->RegisterYourself(); Float_t length2_4; if (disk == 4) { length2_4 = 10.8; } - TGeoVolume* Tube2_4 = gGeoManager->MakeTube("Tube2_4", mPipe, rin, rout, length2_4 / 2); - TGeoVolume* TubeW2_4 = gGeoManager->MakeTube("TubeW2_4", mWater, 0., rin, length2_4 / 2); - TGeoRotation* rTube2_4 = new TGeoRotation("rotationTube2_4", 180.0, 90.0, 90.0); + TGeoVolume* Tube2_4 = + gGeoManager->MakeTube("Tube2_4", mPipe, rin, rout, length2_4 / 2); + TGeoVolume* TubeW2_4 = + gGeoManager->MakeTube("TubeW2_4", mWater, 0., rin, length2_4 / 2); + TGeoRotation* rTube2_4 = + new TGeoRotation("rotationTube2_4", 180.0, 90.0, 90.0); rTube2_4->RegisterYourself(); - TGeoCombiTrans* transfoTube2_4 = new TGeoCombiTrans(0., -length2_4 / 2 - radius1_4, length1_4 / 2 + radius1_4, rTube2_4); + TGeoCombiTrans* transfoTube2_4 = new TGeoCombiTrans( + 0., -length2_4 / 2 - radius1_4, length1_4 / 2 + radius1_4, rTube2_4); transfoTube2_4->RegisterYourself(); - TGeoVolume* Torus2_4 = gGeoManager->MakeTorus(Form("Torus2_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); - TGeoVolume* TorusW2_4 = gGeoManager->MakeTorus(Form("TorusW2_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); - TGeoRotation* rTorus2_4 = new TGeoRotation("rotationTorus2_4", 90.0, 90.0, 180.0); + TGeoVolume* Torus2_4 = + gGeoManager->MakeTorus(Form("Torus2_4_H%d_D%d", half, disk), mPipe, + radius1_4, rin, rout, 0., 90.); + TGeoVolume* TorusW2_4 = + gGeoManager->MakeTorus(Form("TorusW2_4_H%d_D%d", half, disk), mWater, + radius1_4, 0., rin, 0., 90.); + TGeoRotation* rTorus2_4 = + new TGeoRotation("rotationTorus2_4", 90.0, 90.0, 180.0); rTorus2_4->RegisterYourself(); - TGeoCombiTrans* transfoTorus2_4 = new TGeoCombiTrans(0.0, -length2_4 - radius1_4, length1_4 / 2 + radius1_4 + radius1_4, rTorus2_4); + TGeoCombiTrans* transfoTorus2_4 = + new TGeoCombiTrans(0.0, -length2_4 - radius1_4, + length1_4 / 2 + radius1_4 + radius1_4, rTorus2_4); transfoTorus2_4->RegisterYourself(); Float_t length3_4; if (disk == 4) { length3_4 = 3.8; } - TGeoVolume* Tube3_4 = gGeoManager->MakeTube(Form("Tube3_4_H%d_D%d", half, disk), mPipe, rin, rout, length3_4 / 2); - TGeoVolume* TubeW3_4 = gGeoManager->MakeTube(Form("TubeW3_4_H%d_D%d", half, disk), mWater, 0., rin, length3_4 / 2); + TGeoVolume* Tube3_4 = gGeoManager->MakeTube( + Form("Tube3_4_H%d_D%d", half, disk), mPipe, rin, rout, length3_4 / 2); + TGeoVolume* TubeW3_4 = gGeoManager->MakeTube( + Form("TubeW3_4_H%d_D%d", half, disk), mWater, 0., rin, length3_4 / 2); TGeoRotation* rTube3_4 = new TGeoRotation("rotationTube3_4", 0.0, 0.0, 0.0); rTube3_4->RegisterYourself(); - TGeoCombiTrans* transfoTube3_4 = new TGeoCombiTrans(0., -length2_4 - radius1_4 - radius1_4, length1_4 / 2 + radius1_4 + radius1_4 + length3_4 / 2, rTube3_4); + TGeoCombiTrans* transfoTube3_4 = new TGeoCombiTrans( + 0., -length2_4 - radius1_4 - radius1_4, + length1_4 / 2 + radius1_4 + radius1_4 + length3_4 / 2, rTube3_4); transfoTube3_4->RegisterYourself(); Tube1_4->SetLineColor(kGray); @@ -4170,20 +6191,36 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) TGeoCombiTrans* transfoCoolingPipe3_4 = nullptr; TGeoRotation* rotation3_4 = new TGeoRotation("rotation3_4", 90., 90., 100.); rotation3_4->RegisterYourself(); - transfoCoolingPipe3_4 = new TGeoCombiTrans(17. + length1_4 / 2, 0.75, 0.0, rotation3_4); + transfoCoolingPipe3_4 = + new TGeoCombiTrans(17. + length1_4 / 2, 0.75, 0.0, rotation3_4); transfoCoolingPipe3_4->RegisterYourself(); // ------------------Other diagonal side - auto* mCoolingPipe4 = new TGeoVolumeAssembly(Form("cooling_pipe4_H%d_D%d", half, disk)); - TGeoVolume* Tube1p_4 = gGeoManager->MakeTube(Form("Tube1p_4_H%d_D%d", half, disk), mPipe, rin, rout, length1_4 / 2); - TGeoVolume* Torus1p_4 = gGeoManager->MakeTorus(Form("Torus1p_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); - TGeoVolume* Tube2p_4 = gGeoManager->MakeTube(Form("Tube2p_4_H%d_D%d", half, disk), mPipe, rin, rout, length2_4 / 2); - TGeoVolume* Torus2p_4 = gGeoManager->MakeTorus(Form("Torus2p_4_H%d_D%d", half, disk), mPipe, radius1_4, rin, rout, 0., 90.); - TGeoVolume* Tube3p_4 = gGeoManager->MakeTube(Form("Tube3p_4_H%d_D%d", half, disk), mPipe, rin, rout, length3_4 / 2); - TGeoVolume* TubeW1p_4 = gGeoManager->MakeTube(Form("TubeW1p_4_H%d_D%d", half, disk), mWater, 0., rin, length1_4 / 2); - TGeoVolume* TorusW1p_4 = gGeoManager->MakeTorus(Form("TorusW1p_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); - TGeoVolume* TubeW2p_4 = gGeoManager->MakeTube(Form("TubeW2p_4_H%d_D%d", half, disk), mWater, 0., rin, length2_4 / 2); - TGeoVolume* TorusW2p_4 = gGeoManager->MakeTorus(Form("TorusW2p_4_H%d_D%d", half, disk), mWater, radius1_4, 0., rin, 0., 90.); - TGeoVolume* TubeW3p_4 = gGeoManager->MakeTube(Form("TubeW3p_4_H%d_D%d", half, disk), mWater, 0., rin, length3_4 / 2); + auto* mCoolingPipe4 = + new TGeoVolumeAssembly(Form("cooling_pipe4_H%d_D%d", half, disk)); + TGeoVolume* Tube1p_4 = gGeoManager->MakeTube( + Form("Tube1p_4_H%d_D%d", half, disk), mPipe, rin, rout, length1_4 / 2); + TGeoVolume* Torus1p_4 = + gGeoManager->MakeTorus(Form("Torus1p_4_H%d_D%d", half, disk), mPipe, + radius1_4, rin, rout, 0., 90.); + TGeoVolume* Tube2p_4 = gGeoManager->MakeTube( + Form("Tube2p_4_H%d_D%d", half, disk), mPipe, rin, rout, length2_4 / 2); + TGeoVolume* Torus2p_4 = + gGeoManager->MakeTorus(Form("Torus2p_4_H%d_D%d", half, disk), mPipe, + radius1_4, rin, rout, 0., 90.); + TGeoVolume* Tube3p_4 = gGeoManager->MakeTube( + Form("Tube3p_4_H%d_D%d", half, disk), mPipe, rin, rout, length3_4 / 2); + TGeoVolume* TubeW1p_4 = gGeoManager->MakeTube( + Form("TubeW1p_4_H%d_D%d", half, disk), mWater, 0., rin, length1_4 / 2); + TGeoVolume* TorusW1p_4 = + gGeoManager->MakeTorus(Form("TorusW1p_4_H%d_D%d", half, disk), mWater, + radius1_4, 0., rin, 0., 90.); + TGeoVolume* TubeW2p_4 = gGeoManager->MakeTube( + Form("TubeW2p_4_H%d_D%d", half, disk), mWater, 0., rin, length2_4 / 2); + TGeoVolume* TorusW2p_4 = + gGeoManager->MakeTorus(Form("TorusW2p_4_H%d_D%d", half, disk), mWater, + radius1_4, 0., rin, 0., 90.); + TGeoVolume* TubeW3p_4 = gGeoManager->MakeTube( + Form("TubeW3p_4_H%d_D%d", half, disk), mWater, 0., rin, length3_4 / 2); Tube1p_4->SetLineColor(kGray); Torus1p_4->SetLineColor(kGray); @@ -4204,9 +6241,11 @@ void HeatExchanger::createCoolingPipes(Int_t half, Int_t disk) mCoolingPipe4->AddNode(TubeW3p_4, 1, transfoTube3_4); TGeoCombiTrans* transfoCoolingPipe4_4 = nullptr; - TGeoRotation* rotation4_4 = new TGeoRotation("rotation4_4", 90., 90., -100.); + TGeoRotation* rotation4_4 = + new TGeoRotation("rotation4_4", 90., 90., -100.); rotation4_4->RegisterYourself(); - transfoCoolingPipe4_4 = new TGeoCombiTrans(17. + length1_4 / 2, -0.75, 0.0, rotation4_4); + transfoCoolingPipe4_4 = + new TGeoCombiTrans(17. + length1_4 / 2, -0.75, 0.0, rotation4_4); transfoCoolingPipe4_4->RegisterYourself(); mHalfDisk->AddNode(mCoolingPipe3, 1, transfoCoolingPipe3_4); @@ -4223,19 +6262,30 @@ void HeatExchanger::initParameters() mHalfDiskTransformation = new TGeoCombiTrans**[constants::DisksNumber]; for (Int_t idisk = 0; idisk < constants::DisksNumber; idisk++) { mHalfDiskRotation[idisk] = new TGeoRotation*[constants::HalvesNumber]; - mHalfDiskTransformation[idisk] = new TGeoCombiTrans*[constants::HalvesNumber]; + mHalfDiskTransformation[idisk] = + new TGeoCombiTrans*[constants::HalvesNumber]; for (Int_t ihalf = 0; ihalf < constants::HalvesNumber; ihalf++) { - mHalfDiskRotation[idisk][ihalf] = new TGeoRotation(Form("rotation%d%d", idisk, ihalf), 0., 0., 0.); + mHalfDiskRotation[idisk][ihalf] = + new TGeoRotation(Form("rotation%d%d", idisk, ihalf), 0., 0., 0.); mHalfDiskTransformation[idisk][ihalf] = - new TGeoCombiTrans(Form("transformation%d%d", idisk, ihalf), 0., 0., 0., mHalfDiskRotation[idisk][ihalf]); + new TGeoCombiTrans(Form("transformation%d%d", idisk, ihalf), 0., 0., + 0., mHalfDiskRotation[idisk][ihalf]); } } if (Geometry::sGrooves == 0) { - mRohacellThickness = mHeatExchangerThickness / 2. - 2. * mCarbonThickness - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness - 2 * Geometry::sKaptonGlueThickness - 2 * (mRWater + mDRPipe); // smaller rohacell thickness, no grooves + mRohacellThickness = + mHeatExchangerThickness / 2. - 2. * mCarbonThickness - + 2 * Geometry::sGlueRohacellCarbonThickness - + 2 * Geometry::sKaptonOnCarbonThickness - + 2 * Geometry::sKaptonGlueThickness - + 2 * (mRWater + mDRPipe); // smaller rohacell thickness, no grooves } if (Geometry::sGrooves == 1) { - mRohacellThickness = mHeatExchangerThickness / 2. - 2. * mCarbonThickness - 2 * Geometry::sGlueRohacellCarbonThickness - 2 * Geometry::sKaptonOnCarbonThickness - 2 * Geometry::sKaptonGlueThickness; // with grooves + mRohacellThickness = mHeatExchangerThickness / 2. - 2. * mCarbonThickness - + 2 * Geometry::sGlueRohacellCarbonThickness - + 2 * Geometry::sKaptonOnCarbonThickness - + 2 * Geometry::sKaptonGlueThickness; // with grooves } mHalfDiskGap = 0.1; @@ -4266,8 +6316,10 @@ void HeatExchanger::initParameters() mSupportYDimensions[i] = new double[mNPart[i]]; } - mMoreLength01 = 0.6; // additional length of carbon plates compare to the rohacell plate, disk 0 and 1 - mMoreLength = 0.6; // additional length of carbon plates compare to the rohacell plate + mMoreLength01 = 0.6; // additional length of carbon plates compare to the + // rohacell plate, disk 0 and 1 + mMoreLength = + 0.6; // additional length of carbon plates compare to the rohacell plate // disk width // disks 0, 1 diff --git a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h index c18094674ba74..ba5b8d9e1c26b 100644 --- a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h +++ b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h @@ -38,6 +38,7 @@ class DCSConfigReader void init(bool); void loadConfig(gsl::span configBuf); // load MFT config void clear(); + void clearDeadmap(); const std::vector& getConfigInfo() const { return mDCSConfig; } const o2::itsmft::NoiseMap& getNoiseMap() const { return mNoiseMap; } diff --git a/Detectors/ITSMFT/MFT/condition/src/DCSConfigReader.cxx b/Detectors/ITSMFT/MFT/condition/src/DCSConfigReader.cxx index 58a91edb140ee..97f353cb0f140 100644 --- a/Detectors/ITSMFT/MFT/condition/src/DCSConfigReader.cxx +++ b/Detectors/ITSMFT/MFT/condition/src/DCSConfigReader.cxx @@ -39,10 +39,19 @@ void DCSConfigReader::clear() mDCSConfig.clear(); } +void DCSConfigReader::clearDeadmap() +{ + for (int iChip = 0; iChip < 936; ++iChip) { + mNoiseMap.resetChip(iChip); + } +} + //_______________________________________________________________ void DCSConfigReader::parseConfig() { + clearDeadmap(); + char delimiter_newline = '\n'; char delimiter = ','; diff --git a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h index 29fbf53bb63e6..98842e0a5470a 100644 --- a/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h +++ b/Detectors/ITSMFT/MFT/tracking/include/MFTTracking/MFTTrackingParam.h @@ -49,9 +49,9 @@ struct MFTTrackingParam : public o2::conf::ConfigurableParamHelper ErrActions = { @@ -133,7 +137,9 @@ struct ChipStat { ErrActPropagate | ErrActDump, // DColumns non increasing ErrActPropagate | ErrActDump, // Chip data interleaved on the cable ErrActPropagate | ErrActDump, // Truncated buffer while something was expected - ErrActPropagate | ErrActDump // trailer seen after header w/o FE of FD set + ErrActPropagate | ErrActDump, // trailer seen after header w/o FE of FD set + ErrActPropagate | ErrActDump, // ALPIDE MEB was flushed by the busy handling + ErrActPropagate | ErrActDump // ALPIDE received a second trigger while the strobe was still open }; uint16_t feeID = -1; size_t nHits = 0; diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h index 93205b90506d0..32c5a9b28a70c 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/GBTLink.h @@ -39,7 +39,7 @@ } \ if ((errRes)&uint8_t(Abort)) { \ discardData(); \ - return AbortedOnError; \ + return (status = AbortedOnError); \ } namespace o2 @@ -187,6 +187,8 @@ struct GBTLink { uint8_t checkErrorsDiagnosticWord(const GBTDiagnostic* gbtD) const { return NoError; } uint8_t checkErrorsCalibrationWord(const GBTCalibration* gbtCal) const { return NoError; } uint8_t checkErrorsCableID(const GBTData* gbtD, uint8_t cableSW) const { return NoError; } + uint8_t checkErrorsIRNotExtracted() const { return NoError; } + #else uint8_t checkErrorsAlignmentPadding(); uint8_t checkErrorsRDH(const RDH& rdh); @@ -203,6 +205,8 @@ struct GBTLink { uint8_t checkErrorsDiagnosticWord(const GBTDiagnostic* gbtD); uint8_t checkErrorsCalibrationWord(const GBTCalibration* gbtCal); uint8_t checkErrorsCableID(const GBTData* gbtD, uint8_t cableSW); + uint8_t checkErrorsIRNotExtracted(); + #endif uint8_t checkErrorsGBTDataID(const GBTData* dbtD); @@ -339,7 +343,10 @@ GBTLink::CollectedDataStatus GBTLink::collectROFCableData(const Mapping& chmap) } continue; } + // trigger is supposed to be seen + GBTLINK_DECODE_ERRORCHECK(errRes, checkErrorsIRNotExtracted()); } + auto gbtD = reinterpret_cast(&currRawPiece->data[dataOffset]); expectPacketDone = true; diff --git a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx index d47d951edb526..af4c8de5caf39 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx @@ -510,6 +510,23 @@ uint8_t GBTLink::checkErrorsCableID(const GBTData* gbtD, uint8_t cableSW) return err; } +///_________________________________________________________________ +/// Check that the IR was extracted from the trigger +uint8_t GBTLink::checkErrorsIRNotExtracted() +{ + uint8_t err = uint8_t(NoError); + if (ir.isDummy()) { + statistics.errorCounts[GBTLinkDecodingStat::ErrMissingGBTTrigger]++; + gbtErrStatUpadated = true; + if (needToPrintError(statistics.errorCounts[GBTLinkDecodingStat::ErrMissingGBTTrigger])) { + LOG(info) << describe() << " IR_RDH " << irHBF << " IR_ROF " << ir << ". " << statistics.ErrNames[GBTLinkDecodingStat::ErrMissingGBTTrigger]; + } + errorBits |= 0x1 << int(GBTLinkDecodingStat::ErrMissingGBTTrigger); + err |= uint8_t(Abort); + } + return err; +} + ///_________________________________________________________________ /// Account link recovery RDH flag void GBTLink::accountLinkRecovery(o2::InteractionRecord ir) diff --git a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx index c3fe5b4971032..980b673e802ef 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx @@ -375,10 +375,10 @@ ChipPixelData* RawPixelDecoder::getNextChipData(std::vector= chipData.getChipID()) { if (!mROFRampUpStage) { - const int MaxErrLog = 5; + const int MaxErrLog = 2; static int errLocCount = 0; if (errLocCount < MaxErrLog) { - LOGP(error, "Wrong order/duplication: encountered chip {} after processing chip {}, skippin. Inform PDP (message {} of max {} allowed)", + LOGP(warn, "Wrong order/duplication: encountered chip {} after processing chip {}, skipping.", chipData.getChipID(), mLastReadChipID, ++errLocCount, MaxErrLog); } } diff --git a/Detectors/MUON/MCH/Conditions/src/bad-channels-ccdb.cxx b/Detectors/MUON/MCH/Conditions/src/bad-channels-ccdb.cxx index e0ac76154b1ce..7a63792d53ce8 100644 --- a/Detectors/MUON/MCH/Conditions/src/bad-channels-ccdb.cxx +++ b/Detectors/MUON/MCH/Conditions/src/bad-channels-ccdb.cxx @@ -80,7 +80,8 @@ void uploadBadChannels(const std::string ccdbUrl, << dest << "\n"; if (makeDefault) { - md["default"] = true; + md["default"] = "true"; + md["Created"] = "1"; } api.storeAsTFileAny(&bv, dest, md, startTimestamp, endTimestamp); } diff --git a/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx b/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx index 31e9a1772425d..ea173bd1fc7cc 100644 --- a/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx +++ b/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx @@ -193,7 +193,6 @@ framework::DataProcessorSpec bool useStatusMap = DigitFilterParam::Instance().statusMask != 0; if (useStatusMap) { - // input += ";statusmap:MCH/STATUSMAP/0?lifetime=sporadic"; input += ";statusmap:MCH/STATUSMAP/0"; } diff --git a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Response.h b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Response.h index 03b30778f7c11..c9c46a846f2ea 100644 --- a/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Response.h +++ b/Detectors/MUON/MCH/Simulation/include/MCHSimulation/Response.h @@ -34,6 +34,7 @@ class Response ~Response() = default; float getChargeSpread() const { return mChargeSpread; } + float getPitch() const { return mPitch; } float getSigmaIntegration() const { return mSigmaIntegration; } bool isAboveThreshold(float charge) const { return charge > mChargeThreshold; } @@ -64,6 +65,21 @@ class Response /// compute the number of samples corresponding to the charge in ADC units uint32_t nSamples(float charge) const; + /// Ratio of particle mean eloss with respect MIP's Khalil Boudjemline, sep 2003, PhD.Thesis and Particle Data Book + float eLossRatio(float logbetagamma) const; + /// ToDo: check Aliroot formula vs PDG, if really log_10 and not ln or bug in Aliroot + + /// Angle effect in tracking chambers at theta =10 degres as a function of ElossRatio (Khalil BOUDJEMLINE sep 2003 Ph.D Thesis) (in micrometers) + float angleEffect10(float elossratio) const; + + /// Angle effect: Normalisation form theta=10 degres to theta between 0 and 10 (Khalil BOUDJEMLINE sep 2003 Ph.D Thesis) + /// Angle with respect to the wires assuming that chambers are perpendicular to the z axis. + float angleEffectNorma(float elossratio) const; + + /// Magnetic field effect: Normalisation form theta=16 degres (eq. 10 degrees B=0) to theta between -20 and 20 (Lamia Benhabib jun 2006 ) + /// Angle with respect to the wires assuming that chambers are perpendicular to the z axis. + float magAngleEffectNorma(float angle, float bfield) const; + private: MathiesonOriginal mMathieson{}; ///< Mathieson function float mPitch = 0.f; ///< anode-cathode pitch (cm) @@ -72,6 +88,8 @@ class Response float mSigmaIntegration = 0.f; ///< number of sigmas used for charge distribution float mChargeCorr = 0.f; ///< amplitude of charge correlation between cathodes float mChargeThreshold = 0.f; ///< minimum fraction of charge considered + bool mAngleEffect = true; ///< switch for angle effect influencing charge deposition + bool mMagnetEffect = true; ///< switch for magnetic field influencing charge deposition }; } // namespace mch } // namespace o2 diff --git a/Detectors/MUON/MCH/Simulation/src/DEDigitizer.cxx b/Detectors/MUON/MCH/Simulation/src/DEDigitizer.cxx index 8d0988b76a7e6..7cb6814226555 100644 --- a/Detectors/MUON/MCH/Simulation/src/DEDigitizer.cxx +++ b/Detectors/MUON/MCH/Simulation/src/DEDigitizer.cxx @@ -56,9 +56,25 @@ void DEDigitizer::processHit(const Hit& hit, const InteractionRecord& collisionT auto chargeNonBending = charge / chargeCorr; // local position of the charge distribution - math_utils::Point3D pos(hit.GetX(), hit.GetY(), hit.GetZ()); + auto exitPoint = hit.exitPoint(); + auto entrancePoint = hit.entrancePoint(); + math_utils::Point3D lexit{}; + math_utils::Point3D lentrance{}; + mTransformation.MasterToLocal(exitPoint, lexit); + mTransformation.MasterToLocal(entrancePoint, lentrance); + + auto hitlengthZ = lentrance.Z() - lexit.Z(); + auto pitch = mResponse.getPitch(); + math_utils::Point3D lpos{}; - mTransformation.MasterToLocal(pos, lpos); + + if (abs(hitlengthZ) > pitch * 1.99) { + lpos.SetXYZ((lexit.X() + lentrance.X()) / 2., (lexit.Y() + lentrance.Y()) / 2., (lexit.Z() + lentrance.Z()) / 2.); + } else { + lpos.SetXYZ(lexit.X(), // take Bragg peak coordinates assuming electron drift parallel to z + lexit.Y(), // take Bragg peak coordinates assuming electron drift parallel to z + 0.0); // take wire position global coordinate negative + } auto localX = mResponse.getAnod(lpos.X()); auto localY = lpos.Y(); diff --git a/Detectors/MUON/MCH/Simulation/src/Response.cxx b/Detectors/MUON/MCH/Simulation/src/Response.cxx index 985b8d5575234..5c990284eee71 100644 --- a/Detectors/MUON/MCH/Simulation/src/Response.cxx +++ b/Detectors/MUON/MCH/Simulation/src/Response.cxx @@ -89,3 +89,38 @@ uint32_t Response::nSamples(float charge) const double signalParam[3] = {14., 13., 1.5}; return std::round(std::pow(charge / signalParam[1], 1. / signalParam[2]) + signalParam[0]); } +//_____________________________________________________________________ +float Response::eLossRatio(float logbetagamma) const +{ + // Ratio of particle mean eloss with respect MIP's Khalil Boudjemline, sep 2003, PhD.Thesis and Particle Data Book + /// copied from aliroot AliMUONv1.cxx + float eLossRatioParam[5] = {1.02138, -9.54149e-02, +7.83433e-02, -9.98208e-03, +3.83279e-04}; + return eLossRatioParam[0] + eLossRatioParam[1] * logbetagamma + eLossRatioParam[2] * std::pow(logbetagamma, 2) + eLossRatioParam[3] * std::pow(logbetagamma, 3) + eLossRatioParam[4] * std::pow(logbetagamma, 4); +} +//_____________________________________________________________________ +float Response::angleEffect10(float elossratio) const +{ + /// Angle effect in tracking chambers at theta =10 degres as a function of ElossRatio (Khalil BOUDJEMLINE sep 2003 Ph.D Thesis) (in micrometers) + /// copied from aliroot AliMUONv1.cxx + float angleEffectParam[3] = {1.90691e+02, -6.62258e+01, 1.28247e+01}; + return angleEffectParam[0] + angleEffectParam[1] * elossratio + angleEffectParam[2] * std::pow(elossratio, 2); +} +//_____________________________________________________________________ +float Response::angleEffectNorma(float elossratio) const +{ + /// Angle effect: Normalisation form theta=10 degres to theta between 0 and 10 (Khalil BOUDJEMLINE sep 2003 Ph.D Thesis) + /// Angle with respect to the wires assuming that chambers are perpendicular to the z axis. + /// copied from aliroot AliMUONv1.cxx + float angleEffectParam[4] = {4.148, -6.809e-01, 5.151e-02, -1.490e-03}; + return angleEffectParam[0] + angleEffectParam[1] * elossratio + angleEffectParam[2] * std::pow(elossratio, 2) + angleEffectParam[3] * std::pow(elossratio, 3); +} +//_____________________________________________________________________ +float Response::magAngleEffectNorma(float angle, float bfield) const +{ + /// Magnetic field effect: Normalisation form theta=16 degres (eq. 10 degrees B=0) to theta between -20 and 20 (Lamia Benhabib jun 2006 ) + /// Angle with respect to the wires assuming that chambers are perpendicular to the z axis. + /// copied from aliroot AliMUONv1.cxx + float angleEffectParam[7] = {8.6995, 25.4022, 13.8822, 2.4717, 1.1551, -0.0624, 0.0012}; + float aux = std::abs(angle - angleEffectParam[0] * bfield); + return 121.24 / ((angleEffectParam[1] + angleEffectParam[2] * std::abs(bfield)) + angleEffectParam[3] * aux + angleEffectParam[4] * std::pow(aux, 2) + angleEffectParam[5] * std::pow(aux, 3) + angleEffectParam[6] * std::pow(aux, 4)); +} diff --git a/Detectors/MUON/MCH/Status/src/StatusMapCreatorSpec.cxx b/Detectors/MUON/MCH/Status/src/StatusMapCreatorSpec.cxx index 377c7179bbad1..cd7422ee05ab5 100644 --- a/Detectors/MUON/MCH/Status/src/StatusMapCreatorSpec.cxx +++ b/Detectors/MUON/MCH/Status/src/StatusMapCreatorSpec.cxx @@ -96,6 +96,9 @@ class StatusMapCreatorTask LOGP(info, "Sending updated StatusMap of size {}", size(mStatusMap)); pc.outputs().snapshot(OutputRef{"statusmap"}, mStatusMap); mStatusMapUpdated = false; + } else { + LOGP(info, "Sending unchanged StatusMap of size {}", size(mStatusMap)); + pc.outputs().snapshot(OutputRef{"statusmap"}, mStatusMap); } } @@ -122,7 +125,7 @@ framework::DataProcessorSpec getStatusMapCreatorSpec(std::string_view specName) input += "rejectlist:MCH/REJECTLIST/0?lifetime=condition&ccdb-path=MCH/Calib/RejectList"; } - std::string output = "statusmap:MCH/STATUSMAP/0?lifetime=sporadic"; + std::string output = "statusmap:MCH/STATUSMAP/0"; std::vector outputs; auto matchers = select(output.c_str()); diff --git a/Detectors/MUON/MID/Calibration/CMakeLists.txt b/Detectors/MUON/MID/Calibration/CMakeLists.txt index 5c17e204065ca..4d2a33527d527 100644 --- a/Detectors/MUON/MID/Calibration/CMakeLists.txt +++ b/Detectors/MUON/MID/Calibration/CMakeLists.txt @@ -1,13 +1,13 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. See -# https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# 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". +# 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. +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. o2_add_library( MIDCalibration @@ -19,3 +19,4 @@ o2_target_root_dictionary(MIDCalibration HEADERS include/MIDCalibration/ChannelCalibrator.h include/MIDCalibration/ChannelCalibratorParam.h) add_subdirectory(exe) +add_subdirectory(macros) diff --git a/Detectors/MUON/MID/Calibration/macros/CMakeLists.txt b/Detectors/MUON/MID/Calibration/macros/CMakeLists.txt new file mode 100644 index 0000000000000..d665140902c43 --- /dev/null +++ b/Detectors/MUON/MID/Calibration/macros/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_test_root_macro( + ccdbUtils.C + PUBLIC_LINK_LIBRARIES O2::MIDCalibration O2::MathUtils + LABELS muon mid) diff --git a/Detectors/MUON/MID/Calibration/macros/ccdbUtils.C b/Detectors/MUON/MID/Calibration/macros/ccdbUtils.C new file mode 100644 index 0000000000000..0e3ac4bfbcfa5 --- /dev/null +++ b/Detectors/MUON/MID/Calibration/macros/ccdbUtils.C @@ -0,0 +1,208 @@ +// 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 MID/Calibration/macros/ccdbUtils.cxx +/// \brief Retrieve or upload MID calibration objects +/// \author Diego Stocco +/// \date 16 May 2022 + +#include +#include +#include +#include "TFile.h" +#include "TObjString.h" +#include "CCDB/CcdbApi.h" +#include "DataFormatsMID/ColumnData.h" +#include "DataFormatsMID/ROFRecord.h" +#include "DataFormatsMID/ROBoard.h" +#include "MIDRaw/ROBoardConfigHandler.h" +#include "MIDRaw/DecodedDataAggregator.h" +#include "MIDFiltering/FetToDead.h" + +const std::string BadChannelCCDBPath = "MID/Calib/BadChannels"; + +/// @brief Prints the list of bad channels from the CCDB +/// @param ccdbUrl CCDB url +/// @param timestamp Timestamp +/// @param verbose True for verbose output +void queryBadChannels(const char* ccdbUrl, long timestamp, bool verbose) +{ + o2::ccdb::CcdbApi api; + api.init(ccdbUrl); + std::map metadata; + auto* badChannels = api.retrieveFromTFileAny>(BadChannelCCDBPath.c_str(), metadata, timestamp); + if (!badChannels) { + std::cout << "Error: cannot find " << BadChannelCCDBPath << " in " << ccdbUrl << std::endl; + return; + } + std::cout << "number of bad channels = " << badChannels->size() << std::endl; + if (verbose) { + for (const auto& badChannel : *badChannels) { + std::cout << badChannel << "\n"; + } + } +} + +/// @brief Returns the masks from the DCS CCDB +/// @param ccdbUrl CCDB url +/// @param timestamp Timestamp +/// @param verbose True for verbose output +/// @return Masks as string +std::string queryDCSMasks(const char* ccdbUrl, long timestamp, bool verbose) +{ + o2::ccdb::CcdbApi api; + std::string maskCCDBPath = "MID/Calib/ElectronicsMasks"; + api.init(ccdbUrl); + std::map metadata; + auto* masks = api.retrieveFromTFileAny(maskCCDBPath.c_str(), metadata, timestamp); + if (!masks) { + std::cout << "Error: cannot find " << maskCCDBPath << " in " << ccdbUrl << std::endl; + return ""; + } + if (verbose) { + std::cout << masks->GetName() << "\n"; + } + return masks->GetName(); +} + +/// @brief Writes the masks from the DCS CCDB to a text file +/// @param ccdbUrl DCS CCDB url +/// @param timestamp Timestamp +/// @param outFilename Output text filename +void writeDCSMasks(const char* ccdbUrl, long timestamp, const char* outFilename = "masks.txt") +{ + auto masks = queryDCSMasks(ccdbUrl, timestamp, false); + std::ofstream outFile(outFilename); + if (!outFile.is_open()) { + std::cout << "Error: cannot write to file " << outFilename << std::endl; + return; + } + outFile << masks << std::endl; + outFile.close(); +} + +/// @brief Uploads the list of bad channels provided +/// @param ccdbUrl CCDB url +/// @param timestamp Timestamp +/// @param badChannels List of bad channels. Default is no bad channel +void uploadBadChannels(const char* ccdbUrl, long timestamp, std::vector badChannels = {}) +{ + o2::ccdb::CcdbApi api; + api.init(ccdbUrl); + std::map md; + std::cout << "storing default MID bad channels (valid from " << timestamp << ") to " << BadChannelCCDBPath << "\n"; + + api.storeAsTFileAny(&badChannels, BadChannelCCDBPath, md, timestamp, o2::ccdb::CcdbObjectInfo::INFINITE_TIMESTAMP); +} + +/// @brief Reads the DCS masks from a file +/// @param filename Root or txt filename +/// @return DCS masks as string +std::string readDCSMasksFile(std::string filename) +{ + std::string out; + if (filename.find(".root") != std::string::npos) { + TFile* file = TFile::Open(filename.data()); + auto obj = file->Get("ccdb_object"); + out = obj->GetName(); + delete file; + } else { + std::ifstream inFile(filename); + if (inFile.is_open()) { + std::stringstream ss; + ss << inFile.rdbuf(); + out = ss.str(); + } + } + return out; +} + +/// @brief Returns the list of masked channels from the DCS masks +/// @param masksTxt DCS masks as string +/// @return Vector of bad channels +std::vector getBadChannelsFromDCSMasks(const char* masksTxt) +{ + std::stringstream ss; + ss << masksTxt; + o2::mid::ROBoardConfigHandler cfgHandler(ss); + auto cfgMap = cfgHandler.getConfigMap(); + std::vector boards; + o2::mid::ROBoard board; + board.statusWord = o2::mid::raw::sCARDTYPE | o2::mid::raw::sACTIVE; + for (auto& item : cfgMap) { + board.boardId = item.second.boardId; + bool isMasked = item.second.configWord & o2::mid::crateconfig::sMonmoff; + board.firedChambers = 0; + for (size_t ich = 0; ich < 4; ++ich) { + board.patternsBP[ich] = isMasked ? item.second.masksBP[ich] : 0xFFFF; + board.patternsNBP[ich] = isMasked ? item.second.masksNBP[ich] : 0xFFFF; + if (board.patternsBP[ich] || board.patternsNBP[ich]) { + board.firedChambers |= 1 << ich; + } + } + boards.emplace_back(board); + } + std::vector rofs; + rofs.emplace_back(o2::InteractionRecord{2, 2}, o2::mid::EventType::Standard, 0, boards.size()); + o2::mid::DecodedDataAggregator aggregator; + aggregator.process(boards, rofs); + o2::mid::FetToDead ftd; + return ftd.process(aggregator.getData()); +} + +/// @brief Uploads the bad channels list from DCS mask to CCDB +/// @param filename DCS mask text filename +/// @param timestamp Timestamp +/// @param ccdbUrl CCDB url +void uploadBadChannelsFromDCSMask(const char* filename, long timestamp, const char* ccdbUrl, bool verbose) +{ + auto masks = readDCSMasksFile(filename); + auto badChannels = getBadChannelsFromDCSMasks(masks.data()); + if (verbose) { + for (auto& col : badChannels) { + std::cout << col << std::endl; + } + } + uploadBadChannels(ccdbUrl, timestamp, badChannels); +} + +/// @brief Utility to query or upload bad channels and masks from/to the CCDB +/// @param what Command to be executed +/// @param timestamp Timestamp +/// @param verbose True for verbose output +/// @param ccdbUrl CCDB url +void ccdbUtils(const char* what, long timestamp = 0, const char* inFilename = "mask.txt", bool verbose = false, const char* ccdbUrl = "http://ccdb-test.cern.ch:8080") +{ + if (timestamp == 0) { + timestamp = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + } + + std::vector whats = {"querybad", "uploadbad", "querymasks", "writemasks", "uploadbadfrommasks"}; + + if (what == whats[0]) { + queryBadChannels(ccdbUrl, timestamp, verbose); + } else if (what == whats[1]) { + uploadBadChannels(ccdbUrl, timestamp); + } else if (what == whats[2]) { + queryDCSMasks(ccdbUrl, timestamp, verbose); + } else if (what == whats[3]) { + writeDCSMasks(ccdbUrl, timestamp); + } else if (what == whats[4]) { + uploadBadChannelsFromDCSMask(inFilename, timestamp, ccdbUrl, verbose); + } else { + std::cout << "Unimplemented option chosen " << what << std::endl; + std::cout << "Available:\n"; + for (auto& str : whats) { + std::cout << str << " "; + } + std::cout << std::endl; + } +} diff --git a/Detectors/MUON/MID/Raw/src/ELinkManager.cxx b/Detectors/MUON/MID/Raw/src/ELinkManager.cxx index 1b9067addcf9b..d998db8856d7c 100644 --- a/Detectors/MUON/MID/Raw/src/ELinkManager.cxx +++ b/Detectors/MUON/MID/Raw/src/ELinkManager.cxx @@ -78,7 +78,8 @@ void ELinkManager::onDone(const ELinkDecoder& decoder, uint8_t crateId, uint8_t // To avoid flooding the logs, we warn only the first time we see it. auto& err = mErrors[uniqueId]; ++err; - if (err == 1) { + static unsigned int nTotalErr = 0; + if (err == 1 && nTotalErr++ < 3) { // This is the first time we see this faulty board, so we report it. ROBoard board{decoder.getStatusWord(), decoder.getTriggerWord(), raw::makeUniqueLocID(crateId, locId), decoder.getInputs()}; for (int ich = 0; ich < 4; ++ich) { diff --git a/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx index d19a9fc080766..1ed04baa77892 100644 --- a/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx @@ -48,6 +48,10 @@ void PHOSL1phaseCalibDevice::endOfStream(o2::framework::EndOfStreamContext& ec) mCalibrator->checkSlotsToFinalize(o2::calibration::INFINITE_TF); mCalibrator->endOfStream(); + if (mRunStartTime == 0 || mCalibrator->getCalibration() == 0) { // run not started || calibration was not produced + return; // do not create CCDB object + } + std::vector l1phase{mCalibrator->getCalibration()}; LOG(info) << "End of stream reached, sending output to CCDB"; // prepare all info to be sent to CCDB diff --git a/Detectors/PHOS/testsimulation/plot_dig_phos.C b/Detectors/PHOS/testsimulation/plot_dig_phos.C index 4b902bf3da9c0..5062ddfc35e05 100644 --- a/Detectors/PHOS/testsimulation/plot_dig_phos.C +++ b/Detectors/PHOS/testsimulation/plot_dig_phos.C @@ -1,3 +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. + /// \file plot_dig_phos.C /// \brief Simple macro to plot PHOS digits per event @@ -24,11 +35,11 @@ void plot_dig_phos(int ievent = 0, TString inputfile = "o2dig.root") TTree* digTree = (TTree*)gFile->Get("o2sim"); std::vector* mDigitsArray = nullptr; o2::dataformats::MCTruthContainer* labels = nullptr; - digTree->SetBranchAddress("PHSDigit", &mDigitsArray); - digTree->SetBranchAddress("PHSDigitMCTruth", &labels); + digTree->SetBranchAddress("PHOSDigit", &mDigitsArray); + digTree->SetBranchAddress("PHOSDigitMCTruth", &labels); if (!mDigitsArray) { - cout << "PHOS digits not in tree. Exiting ..." << endl; + cout << "PHOS digits not in tree. Exiting..." << endl; return; } digTree->GetEvent(ievent); diff --git a/Detectors/PHOS/workflow/CMakeLists.txt b/Detectors/PHOS/workflow/CMakeLists.txt index 910127e6af188..d6712b274fabd 100644 --- a/Detectors/PHOS/workflow/CMakeLists.txt +++ b/Detectors/PHOS/workflow/CMakeLists.txt @@ -10,7 +10,9 @@ # or submit itself to any jurisdiction. o2_add_library(PHOSWorkflow - SOURCES src/RecoWorkflow.cxx + SOURCES src/CellReaderSpec.cxx + src/DigitReaderSpec.cxx + src/RecoWorkflow.cxx src/ReaderSpec.cxx src/CellConverterSpec.cxx src/RawToCellConverterSpec.cxx diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/CellReaderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/CellReaderSpec.h new file mode 100644 index 0000000000000..113211418f4fb --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/CellReaderSpec.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CellReaderSpec.h + +#ifndef O2_PHOS_CELLREADER +#define O2_PHOS_CELLREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsPHOS/Cell.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsPHOS/MCLabel.h" + +namespace o2 +{ +namespace phos +{ + +class CellReader : public o2::framework::Task +{ + public: + CellReader(bool useMC = true); + ~CellReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mCells, *mCellsInp = &mCells; + std::vector mTRs, *mTRsInp = &mTRs; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginPHS; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mCellTreeName = "o2sim"; + std::string mCellBranchName = "PHOSCell"; + std::string mTRBranchName = "PHOSCellTrigRec"; + std::string mCellMCTruthBranchName = "PHOSCellTrueMC"; +}; + +/// create a processor spec +/// read PHOS Cell data from a root file +framework::DataProcessorSpec getPHOSCellReaderSpec(bool useMC = true); + +} // namespace phos +} // namespace o2 + +#endif /* O2_PHOS_CELLREADER */ diff --git a/Detectors/PHOS/workflow/include/PHOSWorkflow/DigitReaderSpec.h b/Detectors/PHOS/workflow/include/PHOSWorkflow/DigitReaderSpec.h new file mode 100644 index 0000000000000..3ee0ed9789379 --- /dev/null +++ b/Detectors/PHOS/workflow/include/PHOSWorkflow/DigitReaderSpec.h @@ -0,0 +1,68 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file DigitReaderSpec.h + +#ifndef O2_PHOS_DIGITREADER +#define O2_PHOS_DIGITREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsPHOS/Digit.h" +#include "DataFormatsPHOS/TriggerRecord.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DataFormatsPHOS/MCLabel.h" + +namespace o2 +{ +namespace phos +{ + +class DigitReader : public o2::framework::Task +{ + public: + DigitReader(bool useMC = true); + ~DigitReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mDigits, *mDigitsInp = &mDigits; + std::vector mTRs, *mTRsInp = &mTRs; + o2::dataformats::MCTruthContainer mMCTruth, *mMCTruthInp = &mMCTruth; + + o2::header::DataOrigin mOrigin = o2::header::gDataOriginPHS; + + bool mUseMC = true; // use MC truth + + std::unique_ptr mFile; + std::unique_ptr mTree; + std::string mInputFileName = ""; + std::string mDigitTreeName = "o2sim"; + std::string mDigitBranchName = "PHOSDigit"; + std::string mTRBranchName = "PHOSDigitTrigRecords"; + std::string mDigitMCTruthBranchName = "PHOSDigitMCTruth"; +}; + +/// create a processor spec +/// read PHOS Digit data from a root file +framework::DataProcessorSpec getPHOSDigitReaderSpec(bool useMC = true); + +} // namespace phos +} // namespace o2 + +#endif /* O2_PHOS_DIGITREADER */ diff --git a/Detectors/PHOS/workflow/src/CellConverterSpec.cxx b/Detectors/PHOS/workflow/src/CellConverterSpec.cxx index 8d145ff7a3279..330dd27c45251 100644 --- a/Detectors/PHOS/workflow/src/CellConverterSpec.cxx +++ b/Detectors/PHOS/workflow/src/CellConverterSpec.cxx @@ -37,10 +37,12 @@ void CellConverterSpec::init(framework::InitContext& ctx) void CellConverterSpec::run(framework::ProcessingContext& ctx) { - LOG(debug) << "[PHOSCellConverter - run] called"; - auto dataref = ctx.inputs().get("digits"); - auto const* phosheader = o2::framework::DataRefUtils::getHeader(dataref); - if (!phosheader->mHasPayload) { + // LOG(debug) << "[PHOSCellConverter - run] called"; + // auto dataref = ctx.inputs().get("digits"); + // auto const* phosheader = o2::framework::DataRefUtils::getHeader(dataref); + // if (!phosheader->mHasPayload) { + auto digitsTR = ctx.inputs().get>("digitTriggerRecords"); + if (!digitsTR.size()) { // nothing to process mOutputCells.clear(); ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", 0, o2::framework::Lifetime::Timeframe}, mOutputCells); mOutputCellTrigRecs.clear(); @@ -61,7 +63,6 @@ void CellConverterSpec::run(framework::ProcessingContext& ctx) mOutputCellTrigRecs.clear(); auto digits = ctx.inputs().get>("digits"); - auto digitsTR = ctx.inputs().get>("digitTriggerRecords"); std::unique_ptr> truthcont(nullptr); if (mPropagateMC) { truthcont = ctx.inputs().get*>("digitsmctr"); diff --git a/Detectors/PHOS/workflow/src/CellReaderSpec.cxx b/Detectors/PHOS/workflow/src/CellReaderSpec.cxx new file mode 100644 index 0000000000000..64f54c1baf189 --- /dev/null +++ b/Detectors/PHOS/workflow/src/CellReaderSpec.cxx @@ -0,0 +1,100 @@ +// 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 CellReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "PHOSWorkflow/CellReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::phos; + +namespace o2 +{ +namespace phos +{ + +CellReader::CellReader(bool useMC) +{ + mUseMC = useMC; +} + +void CellReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("phos-cells-infile")); + connectTree(mInputFileName); +} + +void CellReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mCells.size() << " Cells in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "CELLS", 0, Lifetime::Timeframe}, mCells); + pc.outputs().snapshot(Output{mOrigin, "CELLTRIGREC", 0, Lifetime::Timeframe}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "CELLSMCTR", 0, Lifetime::Timeframe}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void CellReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mCellTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mCellBranchName.c_str(), &mCellsInp); + if (mUseMC) { + if (mTree->GetBranch(mCellMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mCellMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getPHOSCellReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("PHS", "CELLS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("PHS", "CELLTRIGREC", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("PHS", "CELLSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "phos-cells-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"phos-cells-infile", VariantType::String, "phoscells.root", {"Name of the input Cell file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace phos +} // namespace o2 diff --git a/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx b/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx index 7dd46e49c03d5..27bce0f8745f4 100644 --- a/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx +++ b/Detectors/PHOS/workflow/src/ClusterizerSpec.cxx @@ -72,9 +72,11 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) if (mUseDigits) { LOG(debug) << "PHOSClusterizer - run on digits called"; - auto dataref = ctx.inputs().get("digits"); - auto const* phosheader = o2::framework::DataRefUtils::getHeader(dataref); - if (!phosheader->mHasPayload) { + // auto dataref = ctx.inputs().get("digits"); + // auto const* phosheader = o2::framework::DataRefUtils::getHeader(dataref); + // if (!phosheader->mHasPayload) { + auto digitsTR = ctx.inputs().get>("digitTriggerRecords"); + if (!digitsTR.size()) { // nothing to process mOutputClusters.clear(); ctx.outputs().snapshot(o2::framework::Output{"PHS", "CLUSTERS", 0, o2::framework::Lifetime::Timeframe}, mOutputClusters); if (mFullCluOutput) { @@ -89,9 +91,7 @@ void ClusterizerSpec::run(framework::ProcessingContext& ctx) } return; } - // auto digits = ctx.inputs().get>("digits"); auto digits = ctx.inputs().get>("digits"); - auto digitsTR = ctx.inputs().get>("digitTriggerRecords"); LOG(debug) << "[PHOSClusterizer - run] Received " << digitsTR.size() << " TR, running clusterizer ..."; // const o2::dataformats::MCTruthContainer* truthcont=nullptr; if (mPropagateMC) { diff --git a/Detectors/PHOS/workflow/src/DigitReaderSpec.cxx b/Detectors/PHOS/workflow/src/DigitReaderSpec.cxx new file mode 100644 index 0000000000000..737856051af0c --- /dev/null +++ b/Detectors/PHOS/workflow/src/DigitReaderSpec.cxx @@ -0,0 +1,100 @@ +// 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 DigitReaderSpec.cxx + +#include +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "PHOSWorkflow/DigitReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; +using namespace o2::phos; + +namespace o2 +{ +namespace phos +{ + +DigitReader::DigitReader(bool useMC) +{ + mUseMC = useMC; +} + +void DigitReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("phos-digits-infile")); + connectTree(mInputFileName); +} + +void DigitReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + mTree->GetEntry(ent); + LOG(info) << "Pushing " << mDigits.size() << " Digits in " << mTRs.size() << " TriggerRecords at entry " << ent; + pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0, Lifetime::Timeframe}, mDigits); + pc.outputs().snapshot(Output{mOrigin, "DIGITTRIGREC", 0, Lifetime::Timeframe}, mTRs); + if (mUseMC) { + pc.outputs().snapshot(Output{mOrigin, "DIGITSMCTR", 0, Lifetime::Timeframe}, mMCTruth); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void DigitReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mDigitTreeName.c_str())); + assert(mTree); + assert(mTree->GetBranch(mTRBranchName.c_str())); + + mTree->SetBranchAddress(mTRBranchName.c_str(), &mTRsInp); + mTree->SetBranchAddress(mDigitBranchName.c_str(), &mDigitsInp); + if (mUseMC) { + if (mTree->GetBranch(mDigitMCTruthBranchName.c_str())) { + mTree->SetBranchAddress(mDigitMCTruthBranchName.c_str(), &mMCTruthInp); + } else { + LOG(warning) << "MC-truth is missing, message will be empty"; + } + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getPHOSDigitReaderSpec(bool useMC) +{ + std::vector outputSpec; + outputSpec.emplace_back("PHS", "DIGITS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("PHS", "DIGITTRIGREC", 0, Lifetime::Timeframe); + if (useMC) { + outputSpec.emplace_back("PHS", "DIGITSMCTR", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "phos-digits-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(useMC)}, + Options{ + {"phos-digits-infile", VariantType::String, "phosdigits.root", {"Name of the input Digit file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace phos +} // namespace o2 diff --git a/Detectors/PHOS/workflow/src/RecoWorkflow.cxx b/Detectors/PHOS/workflow/src/RecoWorkflow.cxx index 431648b0ee02f..95e6c732f2046 100644 --- a/Detectors/PHOS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/PHOS/workflow/src/RecoWorkflow.cxx @@ -29,6 +29,8 @@ #include "PHOSWorkflow/WriterSpec.h" #include "PHOSWorkflow/RawToCellConverterSpec.h" #include "PHOSWorkflow/RawWriterSpec.h" +#include "PHOSWorkflow/DigitReaderSpec.h" +#include "PHOSWorkflow/CellReaderSpec.h" #include "Framework/DataSpecUtils.h" #include "SimulationDataFormat/MCTruthContainer.h" @@ -51,7 +53,8 @@ const std::unordered_map InputMap{ const std::unordered_map OutputMap{ {"cells", OutputType::Cells}, - {"clusters", OutputType::Clusters}}; + {"clusters", OutputType::Clusters}, + {"digits", OutputType::Digits}}; o2::framework::WorkflowSpec getWorkflow(bool disableRootInp, bool disableRootOut, @@ -104,7 +107,8 @@ o2::framework::WorkflowSpec getWorkflow(bool disableRootInp, // Digits to .... if (inputType == InputType::Digits) { if (!disableRootInp) { - specs.emplace_back(o2::phos::getDigitsReaderSpec(propagateMC)); + // specs.emplace_back(o2::phos::getDigitsReaderSpec(propagateMC)); + specs.emplace_back(o2::phos::getPHOSDigitReaderSpec(propagateMC)); } if (isEnabled(OutputType::Cells)) { // add converter for cells @@ -125,7 +129,8 @@ o2::framework::WorkflowSpec getWorkflow(bool disableRootInp, // Cells to if (inputType == InputType::Cells) { if (!disableRootInp) { - specs.emplace_back(o2::phos::getCellReaderSpec(propagateMC)); + // specs.emplace_back(o2::phos::getCellReaderSpec(propagateMC)); + specs.emplace_back(o2::phos::getPHOSCellReaderSpec(propagateMC)); } if (isEnabled(OutputType::Clusters)) { // add clusterizer diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx index bf20f0fe06039..a75254c490c6a 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx @@ -78,6 +78,8 @@ class TFReaderSpec : public o2f::Task std::unordered_map mSeenOutputMap; int mTFCounter = 0; int mTFBuilderCounter = 0; + int mNWaits = 0; + long mTotalWaitTime = 0; size_t mSelIDEntry = 0; // next TFID to select from the mInput.tfIDs (if non-empty) bool mRunning = false; TFReaderInp mInput; // command line inputs @@ -230,10 +232,6 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) }; while (1) { - if (mTFCounter >= mInput.maxTFs) { // done - stopProcessing(ctx); - break; - } if (mTFQueue.size()) { static o2f::RateLimiter limiter; limiter.check(ctx, mInput.tfRateLimit, mInput.minSHM); @@ -266,6 +264,10 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) nparts += msgIt.second->Size() / 2; device->Send(*msgIt.second.get(), msgIt.first); } + // FIXME: this is to pretend we did send some messages via DPL. + // we should really migrate everything to use FairMQDeviceProxy, + // however this is a small enough hack for now. + ctx.services().get().fakeDispatch(); tNow = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); deltaSending = mTFCounter ? tNow - tLastTF : 0; LOGP(info, "Sent TF {} of size {} with {} parts, {:.4f} s elapsed from previous TF.", mTFCounter, dataSize, nparts, double(deltaSending) * 1e-6); @@ -283,6 +285,9 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx) } usleep(5000); // wait 5ms for new TF to be built } + if (mTFCounter >= mInput.maxTFs) { // done + stopProcessing(ctx); + } } //____________________________________________________________ @@ -300,7 +305,7 @@ void TFReaderSpec::endOfStream(o2f::EndOfStreamContext& ec) //___________________________________________________________ void TFReaderSpec::stopProcessing(o2f::ProcessingContext& ctx) { - LOG(info) << mTFCounter << " TFs in " << mFileFetcher->getNLoops() << " loops were sent"; + LOGP(info, "{} TFs in {} loops were sent, spent {:.2} s in {} data waiting states", mTFCounter, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); mRunning = false; mFileFetcher->stop(); mFileFetcher.reset(); @@ -333,6 +338,8 @@ void TFReaderSpec::TFBuilder() // build TFs and add to the queue std::string tfFileName; auto sleepTime = std::chrono::microseconds(mInput.delay_us > 10000 ? mInput.delay_us : 10000); + bool waitAcknowledged = false; + long startWait = 0; while (mRunning && mDevice) { if (mTFQueue.size() >= size_t(mInput.maxTFCache)) { std::this_thread::sleep_for(sleepTime); @@ -352,9 +359,23 @@ void TFReaderSpec::TFBuilder() break; } if (tfFileName.empty()) { - std::this_thread::sleep_for(10ms); // fait for the files cache to be filled + if (!waitAcknowledged) { + startWait = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + waitAcknowledged = true; + } + std::this_thread::sleep_for(10ms); // wait for the files cache to be filled continue; } + if (waitAcknowledged) { + long waitTime = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count() - startWait; + mTotalWaitTime += waitTime; + if (++mNWaits > 1) { + LOGP(warn, "Resuming reading after waiting for data {:.2} s (accumulated {:.2} s delay in {} waits)", 1e-6 * waitTime, 1e-6 * mTotalWaitTime, mNWaits); + } + waitAcknowledged = false; + startWait = 0; + } + LOG(info) << "Processing file " << tfFileName; SubTimeFrameFileReader reader(tfFileName, mInput.detMask); size_t locID = 0; @@ -426,6 +447,10 @@ o2f::DataProcessorSpec o2::rawdd::getTFReaderSpec(o2::rawdd::TFReaderInp& rinp) continue; } // in case detectors were processed on FLP + if (id == DetID::CTP) { + spec.outputs.emplace_back(o2f::OutputSpec{o2f::OutputSpec{DetID::getDataOrigin(DetID::CTP), "LUMI", 0}}); + rinp.hdVec.emplace_back(o2h::DataHeader{"LUMI", DetID::getDataOrigin(DetID::CTP), 0, 0}); // in abcence of real data this will be sent + } if (id == DetID::TOF) { spec.outputs.emplace_back(o2f::OutputSpec{o2f::ConcreteDataTypeMatcher{DetID::getDataOrigin(DetID::TOF), "CRAWDATA"}}); rinp.hdVec.emplace_back(o2h::DataHeader{"CRAWDATA", DetID::getDataOrigin(DetID::TOF), 0xDEADBEEF, 0}); // in abcence of real data this will be sent @@ -465,9 +490,9 @@ o2f::DataProcessorSpec o2::rawdd::getTFReaderSpec(o2::rawdd::TFReaderInp& rinp) } } } - spec.outputs.emplace_back(o2f::OutputSpec{{"stfDist"}, o2h::gDataOriginFLP, o2h::gDataDescriptionDISTSTF, 0}); + o2f::DataSpecUtils::updateOutputList(spec.outputs, o2f::OutputSpec{{"stfDist"}, o2h::gDataOriginFLP, o2h::gDataDescriptionDISTSTF, 0}); if (!rinp.sup0xccdb) { - spec.outputs.emplace_back(o2f::OutputSpec{{"stfDistCCDB"}, o2h::gDataOriginFLP, o2h::gDataDescriptionDISTSTF, 0xccdb}); + o2f::DataSpecUtils::updateOutputList(spec.outputs, o2f::OutputSpec{{"stfDistCCDB"}, o2h::gDataOriginFLP, o2h::gDataDescriptionDISTSTF, 0xccdb}); } if (!rinp.metricChannel.empty()) { spec.options.emplace_back(o2f::ConfigParamSpec{"channel-config", o2f::VariantType::String, rinp.metricChannel, {"Out-of-band channel config for TF throttling"}}); diff --git a/Detectors/Raw/src/RawDumpSpec.cxx b/Detectors/Raw/src/RawDumpSpec.cxx index c85f3bd4d272c..345eba5cefdc0 100644 --- a/Detectors/Raw/src/RawDumpSpec.cxx +++ b/Detectors/Raw/src/RawDumpSpec.cxx @@ -17,6 +17,7 @@ #include "DetectorsRaw/RDHUtils.h" #include "DPLUtils/DPLRawParser.h" #include "CommonUtils/StringUtils.h" +#include "CommonConstants/Triggers.h" #include "Algorithm/RangeTokenizer.h" #include #include @@ -34,6 +35,14 @@ class RawDump : public Task public: static constexpr o2h::DataDescription DESCRaw{"RAWDATA"}, DESCCRaw{"CRAWDATA"}; + struct LinkInfo { + FILE* fileHandler = nullptr; + o2::header::RDHAny rdhSOX; + o2::InteractionRecord firstIR{}; + bool hasSOX{false}; + DetID detID{}; + }; + RawDump(bool TOFUncompressed = false); void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -41,7 +50,7 @@ class RawDump : public Task static std::string getReadoutType(DetID id); private: - FILE* getFile(o2h::DataOrigin detOr, const header::RDHAny* rdh); + LinkInfo& getLinkInfo(o2h::DataOrigin detOr, const header::RDHAny* rdh); std::string getFileName(DetID detID, const header::RDHAny* rdh); std::string getBaseFileNameITS(const header::RDHAny* rdh); std::string getBaseFileNameTPC(const header::RDHAny* rdh); @@ -63,10 +72,13 @@ class RawDump : public Task bool mFatalOnDeadBeef{false}; bool mSkipDump{false}; bool mTOFUncompressed{false}; + bool mImposeSOX{false}; int mVerbosity{0}; + int mTFCount{0}; uint64_t mTPCLinkRej{0}; // pattern of TPC links to reject + o2::InteractionRecord mFirstIR{}; std::string mOutDir{}; - std::unordered_map mDetFEEID2File{}; + std::unordered_map mLinksInfo{}; std::unordered_map mName2File{}; std::unordered_map mOrigin2DetID{}; std::array mConfigEntries{}; @@ -88,6 +100,7 @@ void RawDump::init(InitContext& ic) mVerbosity = ic.options().get("dump-verbosity"); mOutDir = ic.options().get("output-directory"); mSkipDump = ic.options().get("skip-dump"); + mImposeSOX = !ic.options().get("skip-impose-sox"); auto vrej = o2::RangeTokenizer::tokenize(ic.options().get("reject-tpc-links")); for (auto i : vrej) { if (i < 63) { @@ -125,21 +138,85 @@ void RawDump::run(ProcessingContext& pc) { DPLRawParser parser(pc.inputs()); - static DetID::mask_t repDeadBeef{}; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - auto const* raw = it.raw(); - auto const* dh = it.o2DataHeader(); + auto procDEADBEEF = [this](const o2::header::DataHeader* dh) { + static DetID::mask_t repDeadBeef{}; if (dh->subSpecification == 0xdeadbeef && dh->payloadSize == 0) { - if (mFatalOnDeadBeef) { + if (this->mFatalOnDeadBeef) { LOGP(fatal, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{}", dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit); } else { - if (!repDeadBeef[DetID(dh->dataOrigin.str)] || mVerbosity > 0) { + if (!repDeadBeef[DetID(dh->dataOrigin.str)] || this->mVerbosity > 0) { LOGP(warn, "Skipping input [{}/{}/{:#x}] TF#{} 1st_orbit:{}", dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit); repDeadBeef |= DetID::getMask(dh->dataOrigin.str); } + return false; + } + } + return true; + }; + + auto isRORC = [](DetID id) { + return id == DetID::PHS || id == DetID::EMC || id == DetID::HMP; + }; + + if (mTFCount == 0 && mImposeSOX) { // make sure all links payload starts with SOX + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + auto const* dh = it.o2DataHeader(); + if (!procDEADBEEF(dh)) { + continue; + } + const auto rdh = reinterpret_cast(it.raw()); + if (!RDHUtils::checkRDH(rdh, true)) { + o2::raw::RDHUtils::printRDH(rdh); + continue; + } + auto& lInfo = getLinkInfo(dh->dataOrigin, rdh); + if (!lInfo.firstIR.isDummy()) { // already processed continue; } + lInfo.firstIR = {0, o2::raw::RDHUtils::getTriggerOrbit(rdh)}; + if (lInfo.firstIR < mFirstIR) { + mFirstIR = lInfo.firstIR; + } + auto trig = o2::raw::RDHUtils::getTriggerType(rdh); + lInfo.hasSOX = trig & (o2::trigger::SOT | o2::trigger::SOC); + lInfo.rdhSOX = *rdh; + lInfo.detID = mOrigin2DetID[dh->dataOrigin]; + } + // now write RDH with SOX (if needed) + for (auto& lit : mLinksInfo) { + auto& lInfo = lit.second; + if (!lInfo.hasSOX && lInfo.fileHandler) { + auto trig = o2::raw::RDHUtils::getTriggerType(lInfo.rdhSOX); + if (o2::raw::RDHUtils::getTriggerIR(lInfo.rdhSOX) != mFirstIR) { // need to write cooked header + o2::raw::RDHUtils::setTriggerOrbit(lInfo.rdhSOX, mFirstIR.orbit); + o2::raw::RDHUtils::setOffsetToNext(lInfo.rdhSOX, sizeof(o2::header::RDHAny)); + o2::raw::RDHUtils::setMemorySize(lInfo.rdhSOX, sizeof(o2::header::RDHAny)); + o2::raw::RDHUtils::setPacketCounter(lInfo.rdhSOX, o2::raw::RDHUtils::getPacketCounter(lInfo.rdhSOX) - 1); + trig = isRORC(lInfo.detID) ? o2::trigger::SOT : (o2::trigger::SOC | o2::trigger::ORBIT | o2::trigger::HB | o2::trigger::TF); + o2::raw::RDHUtils::setTriggerType(lInfo.rdhSOX, trig); + o2::raw::RDHUtils::setStop(lInfo.rdhSOX, 0x1); + if (mVerbosity > 0) { + LOGP(info, "Writing cooked up RDH with SOX"); + o2::raw::RDHUtils::printRDH(lInfo.rdhSOX); + } + auto ws = std::fwrite(&lInfo.rdhSOX, 1, sizeof(o2::header::RDHAny), lInfo.fileHandler); + if (ws != sizeof(o2::header::RDHAny)) { + LOGP(fatal, "Failed to write cooked up RDH with SOX"); + } + lInfo.hasSOX = true; // flag that it is already written + } // otherwhise data has RDH with orbit matching to SOX, we will simply set a flag while writing the data + } else if (lInfo.fileHandler && o2::raw::RDHUtils::getTriggerOrbit(lInfo.rdhSOX) != mFirstIR.orbit) { + o2::raw::RDHUtils::printRDH(lInfo.rdhSOX); + LOGP(error, "Original data had SOX set but the orbit differs from the smallest seen {}, keep original one", mFirstIR.orbit); + } + } + } + + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + auto const* dh = it.o2DataHeader(); + if (!procDEADBEEF(dh)) { + continue; } const auto rdh = reinterpret_cast(it.raw()); if (!RDHUtils::checkRDH(rdh, true)) { @@ -150,14 +227,36 @@ void RawDump::run(ProcessingContext& pc) o2::raw::RDHUtils::printRDH(rdh); } FILE* fh = nullptr; - if (!mSkipDump && (fh = getFile(dh->dataOrigin, rdh))) { + auto& lInfo = getLinkInfo(dh->dataOrigin, rdh); + if (!mSkipDump && lInfo.fileHandler) { auto sz = o2::raw::RDHUtils::getOffsetToNext(rdh); - auto ws = std::fwrite(raw, 1, sz, fh); + auto raw = it.raw(); + if (mTFCount == 0 && !lInfo.hasSOX && o2::raw::RDHUtils::getTriggerIR(rdh) == mFirstIR) { // need to add SOX bit to existing RDH + auto trig = o2::raw::RDHUtils::getTriggerType(rdh); + trig |= isRORC(lInfo.detID) ? o2::trigger::SOT : (o2::trigger::SOC | o2::trigger::ORBIT | o2::trigger::HB | o2::trigger::TF); + auto rdhC = *rdh; + o2::raw::RDHUtils::setTriggerType(rdhC, trig); + if (mVerbosity > 0) { + LOGP(info, "Write existing RDH with SOX added"); + o2::raw::RDHUtils::printRDH(rdhC); + } + auto ws = std::fwrite(&rdhC, 1, sizeof(o2::header::RDHAny), lInfo.fileHandler); + if (ws != sizeof(o2::header::RDHAny)) { + LOGP(fatal, "Failed to write RDH with SOX added"); + } + raw += sizeof(o2::header::RDHAny); + sz -= sizeof(o2::header::RDHAny); + if (o2::raw::RDHUtils::getStop(rdhC)) { + lInfo.hasSOX = true; + } + } + auto ws = std::fwrite(raw, 1, sz, lInfo.fileHandler); if (ws != sz) { LOGP(fatal, "Failed to write payload of {} bytes", sz); } } } + mTFCount++; } //____________________________________________________________ @@ -186,39 +285,38 @@ void RawDump::endOfStream(EndOfStreamContext& ec) } //_____________________________________________________________________ -FILE* RawDump::getFile(o2h::DataOrigin detOr, const header::RDHAny* rdh) +RawDump::LinkInfo& RawDump::getLinkInfo(o2h::DataOrigin detOr, const header::RDHAny* rdh) { uint32_t feeid = RDHUtils::getFEEID(rdh); uint64_t id = (uint64_t(detOr) << 32) + feeid; - auto fhandler = mDetFEEID2File[id]; - if (!fhandler) { + auto& linkInfo = mLinksInfo[id]; + if (!linkInfo.fileHandler) { DetID detID = mOrigin2DetID[detOr]; auto name = getFileName(detID, rdh); if (name.empty()) { - return nullptr; // reject data of this RDH + return linkInfo; // reject data of this RDH } - fhandler = mName2File[name]; - if (!fhandler) { - fhandler = std::fopen(name.c_str(), "w"); - if (!fhandler) { + linkInfo.fileHandler = mName2File[name]; + if (!linkInfo.fileHandler) { + linkInfo.fileHandler = std::fopen(name.c_str(), "w"); + if (!linkInfo.fileHandler) { LOGP(fatal, "Failed to create file {} for Det={} / FeeID=0x{:05x}", name, detOr.str, feeid); } - mName2File[name] = fhandler; + mName2File[name] = linkInfo.fileHandler; mConfigEntries[detID] += fmt::format( "[input-{}-{}]\n" "dataOrigin = {}\n" "dataDescription = {}\n" "readoutCard = {}\n" "filePath = {}\n\n", - detOr.str, mFilesPerDet[detID]++, detOr.str, (detID == DetID::TOF || mTOFUncompressed) ? DESCRaw.str : DESCCRaw.str, getReadoutType(detID), o2::utils::Str::getFullPath(name)); + detOr.str, mFilesPerDet[detID]++, detOr.str, (detID != DetID::TOF || mTOFUncompressed) ? DESCRaw.str : DESCCRaw.str, getReadoutType(detID), o2::utils::Str::getFullPath(name)); } if (mVerbosity > 0) { RDHUtils::printRDH(rdh); LOGP(info, "Write Det={}/0x{:05x} to {}", detOr.str, feeid, o2::utils::Str::getFullPath(name)); } - mDetFEEID2File[id] = fhandler; } - return fhandler; + return linkInfo; } //_____________________________________________________________________ @@ -588,6 +686,7 @@ DataProcessorSpec getRawDumpSpec(DetID::mask_t detMask, bool TOFUncompressed) ConfigParamSpec{"skip-dump", VariantType::Bool, false, {"do not produce binary data"}}, ConfigParamSpec{"dump-verbosity", VariantType::Int, 0, {"0:minimal, 1:report Det/FeeID->filename, 2: print RDH"}}, ConfigParamSpec{"reject-tpc-links", VariantType::String, "", {"comma-separated list TPC links to reject"}}, + ConfigParamSpec{"skip-impose-sox", VariantType::Bool, false, {"do not impose SOX for 1st TF"}}, ConfigParamSpec{"output-directory", VariantType::String, "./", {"Output directory (create if needed)"}}}}; } diff --git a/Detectors/Raw/src/RawFileReader.cxx b/Detectors/Raw/src/RawFileReader.cxx index be9bc4060b8fb..2d666a6800fe7 100644 --- a/Detectors/Raw/src/RawFileReader.cxx +++ b/Detectors/Raw/src/RawFileReader.cxx @@ -378,7 +378,7 @@ bool RawFileReader::LinkData::preprocessCRUPage(const RDHAny& rdh, bool newSPage reader->imposeFirstTF(irOfSOX.orbit); } } - auto newTFCalc = reader->getTFAutodetect() != FirstTFDetection::Pending && (blocks.empty() || HBU.getTF(blocks.back().ir) < HBU.getTF(ir)); + auto newTFCalc = reader->getTFAutodetect() != FirstTFDetection::Pending && (blocks.empty() || HBU.getTF(blocks.back().ir) != HBU.getTF(ir)); // TF change if (cruDetector) { newTF = (triggerType & o2::trigger::TF); newHB = (triggerType & o2::trigger::HB); diff --git a/Detectors/Raw/src/RawFileReaderWorkflow.cxx b/Detectors/Raw/src/RawFileReaderWorkflow.cxx index 57a9e57cc322d..38d379aee9aea 100644 --- a/Detectors/Raw/src/RawFileReaderWorkflow.cxx +++ b/Detectors/Raw/src/RawFileReaderWorkflow.cxx @@ -348,10 +348,16 @@ void RawReaderSpecs::run(o2f::ProcessingContext& ctx) if (mTFCounter) { // delay sending std::this_thread::sleep_for(std::chrono::microseconds((size_t)mDelayUSec)); } + bool sentSomething = false; for (auto& msgIt : messagesPerRoute) { LOG(info) << "Sending " << msgIt.second->Size() / 2 << " parts to channel " << msgIt.first; device->Send(*msgIt.second.get(), msgIt.first); + sentSomething = msgIt.second->Size() > 0; } + if (sentSomething) { + ctx.services().get().fakeDispatch(); + } + mTimer.Stop(); LOGP(info, "Sent payload of {} bytes in {} parts in {} messages for TF#{} firstTForbit={} timeStamp={} | Timing: {}", tfSize, tfNParts, diff --git a/Detectors/TOF/base/src/Utils.cxx b/Detectors/TOF/base/src/Utils.cxx index 02d0621c09752..265cb0247b544 100644 --- a/Detectors/TOF/base/src/Utils.cxx +++ b/Detectors/TOF/base/src/Utils.cxx @@ -272,14 +272,28 @@ int Utils::addMaskBC(int mask, int channel) int mask2 = (mask >> 16); int cmask = 1; int used = 0; + int weight = 1; + int candidates = 0; + for (int ibit = 0; ibit < 16; ibit++) { // counts candidates + if (mask & cmask) { + candidates++; + } + cmask *= 2; + } + + if (candidates) { + weight = 16 / candidates; + } + + cmask = 1; for (int ibit = 0; ibit < 16; ibit++) { if (mask & cmask) { - mMaskBCchan[channel][ibit]++; - mMaskBC[ibit]++; + mMaskBCchan[channel][ibit] += weight; + mMaskBC[ibit] += weight; } if (mask2 & cmask) { - mMaskBCchanUsed[channel][ibit]++; - mMaskBCUsed[ibit]++; + mMaskBCchanUsed[channel][ibit] += weight; + mMaskBCUsed[ibit] += weight; used = ibit - 8; } cmask *= 2; diff --git a/Detectors/TOF/compression/include/TOFCompression/Compressor.h b/Detectors/TOF/compression/include/TOFCompression/Compressor.h index c807b5cfc0fe2..280aa163e53c2 100644 --- a/Detectors/TOF/compression/include/TOFCompression/Compressor.h +++ b/Detectors/TOF/compression/include/TOFCompression/Compressor.h @@ -40,6 +40,7 @@ class Compressor inline bool run() { rewind(); + mEncoderPointerMax = reinterpret_cast(mEncoderBuffer + mEncoderBufferSizeInt); if (mDecoderCONET) { mDecoderPointerMax = reinterpret_cast(mDecoderBuffer + mDecoderBufferSize); while (mDecoderPointer < mDecoderPointerMax) { @@ -89,10 +90,20 @@ class Compressor void setDecoderBuffer(const char* val) { mDecoderBuffer = val; }; void setEncoderBuffer(char* val) { mEncoderBuffer = val; }; void setDecoderBufferSize(long val) { mDecoderBufferSize = val; }; - void setEncoderBufferSize(long val) { mEncoderBufferSize = val; }; + void setEncoderBufferSize(long val) + { + mEncoderBufferSize = val; + mEncoderBufferSizeInt = mEncoderBufferSize / 4; + }; inline uint32_t getDecoderByteCounter() const { return reinterpret_cast(mDecoderPointer) - mDecoderBuffer; }; - inline uint32_t getEncoderByteCounter() const { return reinterpret_cast(mEncoderPointer) - mEncoderBuffer; }; + inline uint32_t getEncoderByteCounter() const + { + if (reinterpret_cast(mEncoderPointer) < mEncoderBuffer) { + return 0; + } + return reinterpret_cast(mEncoderPointer) - mEncoderBuffer; + }; // benchmarks double mIntegratedBytes = 0.; @@ -139,13 +150,21 @@ class Compressor /** encoder private functions and data members **/ - void encoderSpider(int itrm); + int encoderSpider(int itrm); inline void encoderRewind() { mEncoderPointer = reinterpret_cast(mEncoderBuffer); }; - inline void encoderNext() { mEncoderPointer++; }; + inline int encoderNext() + { + if (mEncoderPointer + 1 >= mEncoderPointerMax) { + return 1; + }; + mEncoderPointer++; + return 0; + }; std::ofstream mEncoderFile; char* mEncoderBuffer = nullptr; long mEncoderBufferSize; + long mEncoderBufferSizeInt; uint32_t* mEncoderPointer = nullptr; uint32_t* mEncoderPointerMax = nullptr; uint32_t* mEncoderPointerStart = nullptr; diff --git a/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h b/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h index e18cde9d3dc4e..cd3823857677d 100644 --- a/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h +++ b/Detectors/TOF/compression/include/TOFCompression/CompressorTask.h @@ -33,7 +33,7 @@ template class CompressorTask : public Task { public: - CompressorTask() = default; + CompressorTask(long payloadLim = -1) : mPayloadLimit(payloadLim) {} ~CompressorTask() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -41,6 +41,7 @@ class CompressorTask : public Task private: Compressor mCompressor; int mOutputBufferSize; + long mPayloadLimit = -1; }; } // namespace tof diff --git a/Detectors/TOF/compression/include/TOFCompression/CompressorTaskOld.h b/Detectors/TOF/compression/include/TOFCompression/CompressorTaskOld.h index 12beead1087ba..ed78dd88caa64 100644 --- a/Detectors/TOF/compression/include/TOFCompression/CompressorTaskOld.h +++ b/Detectors/TOF/compression/include/TOFCompression/CompressorTaskOld.h @@ -33,7 +33,7 @@ template class CompressorTaskOld : public Task { public: - CompressorTaskOld() = default; + CompressorTaskOld(long payloadLim = -1) : mPayloadLimit(payloadLim) {} ~CompressorTaskOld() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -41,6 +41,7 @@ class CompressorTaskOld : public Task private: Compressor mCompressor; int mOutputBufferSize; + long mPayloadLimit = -1; }; } // namespace tof diff --git a/Detectors/TOF/compression/src/Compressor.cxx b/Detectors/TOF/compression/src/Compressor.cxx index 75f5fac714fb2..ef76e386733ee 100644 --- a/Detectors/TOF/compression/src/Compressor.cxx +++ b/Detectors/TOF/compression/src/Compressor.cxx @@ -209,6 +209,13 @@ bool Compressor::processHBF() } /** copy RDH open to encoder buffer **/ + + if (mEncoderPointer + mDecoderRDH->headerSize >= mEncoderPointerMax) { + LOG(error) << "link = " << rdh->feeId << ": beyond the buffer size mEncoderPointer+mDecoderRDH->headerSize = " << mEncoderPointer + mDecoderRDH->headerSize << " >= " + << "mEncoderPointerMax = " << mEncoderPointerMax; + encoderRewind(); + return true; + } std::memcpy(mEncoderPointer, mDecoderRDH, mDecoderRDH->headerSize); mEncoderPointer = reinterpret_cast(reinterpret_cast(mEncoderPointer) + rdh->headerSize); @@ -246,6 +253,11 @@ bool Compressor::processHBF() /** copy RDH close to encoder buffer **/ /** CAREFUL WITH THE PAGE COUNTER **/ + if (mEncoderPointer + rdh->headerSize >= mEncoderPointerMax) { + LOG(error) << "link = " << rdh->feeId << ": beyond the buffer size mEncoderPointer+rdh->headerSize = " << mEncoderPointer + rdh->headerSize << " >= " + << "mEncoderPointerMax = " << mEncoderPointerMax; + return true; + } mEncoderRDH = reinterpret_cast(mEncoderPointer); std::memcpy(mEncoderRDH, rdh, rdh->headerSize); mEncoderRDH->memorySize = rdh->headerSize; @@ -402,7 +414,8 @@ bool Compressor::processDRM() /** encode Crate Header **/ *mEncoderPointer = 0x80000000; *mEncoderPointer |= GET_DRMHEADW1_PARTSLOTMASK(*mDecoderSummary.drmHeadW1) << 12; - *mEncoderPointer |= GET_DRMDATAHEADER_DRMID(*mDecoderSummary.drmDataHeader) << 24; + // R+OLD *mEncoderPointer |= GET_DRMDATAHEADER_DRMID(*mDecoderSummary.drmDataHeader) << 24; + *mEncoderPointer |= (mDecoderRDH->feeId & 0xFF) << 24; *mEncoderPointer |= GET_DRMHEADW3_GBTBUNCHCNT(*mDecoderSummary.drmHeadW3); if (verbose && mEncoderVerbose) { auto crateHeader = reinterpret_cast(mEncoderPointer); @@ -411,7 +424,10 @@ bool Compressor::processDRM() auto slotPartMask = crateHeader->slotPartMask; printf("%s %08x Crate header (drmID=%d, bunchID=%d, slotPartMask=0x%x) %s \n", colorGreen, *mEncoderPointer, drmID, bunchID, slotPartMask, colorReset); } - encoderNext(); + if (encoderNext()) { + encoderRewind(); + return true; + } /** encode Crate Orbit **/ *mEncoderPointer = *mDecoderSummary.tofOrbit; @@ -420,7 +436,10 @@ bool Compressor::processDRM() auto orbitID = crateOrbit->orbitID; printf("%s %08x Crate orbit (orbitID=%u) %s \n", colorGreen, *mEncoderPointer, orbitID, colorReset); } - encoderNext(); + if (encoderNext()) { + encoderRewind(); + return true; + } /** loop over DRM payload **/ int nsteps = 0; @@ -481,7 +500,10 @@ bool Compressor::processDRM() auto NumberOfErrors = CrateTrailer->numberOfErrors; printf("%s %08x Crate trailer (EventCounter=%d, NumberOfDiagnostics=%d, NumberOfErrors=%d) %s \n", colorGreen, *mEncoderPointer, EventCounter, NumberOfDiagnostics, NumberOfErrors, colorReset); } - encoderNext(); + if (encoderNext()) { + encoderRewind(); + return true; + } /** encode Diagnostic Words **/ for (int iword = 0; iword < mCheckerSummary.nDiagnosticWords; ++iword) { @@ -493,7 +515,10 @@ bool Compressor::processDRM() auto faultBits = Diagnostic->faultBits; printf("%s %08x Diagnostic (slotId=%d, faultBits=0x%x) %s \n", colorGreen, *mEncoderPointer, slotId, faultBits, colorReset); } - encoderNext(); + if (encoderNext()) { + encoderRewind(); + return true; + } } /** encode TDC errors **/ @@ -513,7 +538,10 @@ bool Compressor::processDRM() auto tdcID = Error->tdcID; printf("%s %08x Error (slotId=%d, chain=%d, tdcId=%d, errorFlags=0x%x) %s \n", colorGreen, *mEncoderPointer, slotID, chain, tdcID, errorFlags, colorReset); } - encoderNext(); + if (encoderNext()) { + encoderRewind(); + return true; + } } #endif mDecoderSummary.trmErrors[itrm][ichain] = 0; @@ -680,7 +708,10 @@ bool Compressor::processTRM() /** encoder Spider **/ if (mDecoderSummary.hasHits[itrm][0] || mDecoderSummary.hasHits[itrm][1]) { - encoderSpider(itrm); + if (encoderSpider(itrm)) { + encoderRewind(); + return true; + } } /** success **/ @@ -821,7 +852,7 @@ bool Compressor::decoderParanoid() } template -void Compressor::encoderSpider(int itrm) +int Compressor::encoderSpider(int itrm) { /** encoder spider **/ @@ -912,7 +943,10 @@ void Compressor::encoderSpider(int itrm) auto TRMID = FrameHeader->trmID; printf("%s %08x Frame header (TRMID=%d, FrameID=%d, NumberOfHits=%d) %s \n", colorGreen, *mEncoderPointer, TRMID, FrameID, NumberOfHits, colorReset); } - encoderNext(); + if (encoderNext()) { + encoderRewind(); + return true; + } // packed hits for (int ihit = 0; ihit < mSpiderSummary.nFramePackedHits[iframe]; ++ihit) { @@ -926,11 +960,15 @@ void Compressor::encoderSpider(int itrm) auto TOT = PackedHit->tot; printf("%s %08x Packed hit (Chain=%d, TDCID=%d, Channel=%d, Time=%d, TOT=%d) %s \n", colorGreen, *mEncoderPointer, Chain, TDCID, Channel, Time, TOT, colorReset); } - encoderNext(); + if (encoderNext()) { + encoderRewind(); + return true; + } } mSpiderSummary.nFramePackedHits[iframe] = 0; } + return 0; } template diff --git a/Detectors/TOF/compression/src/CompressorTask.cxx b/Detectors/TOF/compression/src/CompressorTask.cxx index 90235c86f40cf..cad61e9ed0576 100644 --- a/Detectors/TOF/compression/src/CompressorTask.cxx +++ b/Detectors/TOF/compression/src/CompressorTask.cxx @@ -30,7 +30,11 @@ namespace o2::tof template void CompressorTask::init(InitContext& ic) { - LOG(info) << "Compressor init"; + if (mPayloadLimit < 0) { + LOG(info) << "Compressor init"; + } else { + LOG(info) << "Compressor init with Payload limit at " << mPayloadLimit; + } auto decoderCONET = ic.options().get("tof-compressor-conet-mode"); auto decoderVerbose = ic.options().get("tof-compressor-decoder-verbose"); @@ -137,6 +141,11 @@ void CompressorTask::run(ProcessingContext& pc) auto payloadIn = ref.payload; auto payloadInSize = DataRefUtils::getPayloadSize(ref); + if (mPayloadLimit > -1 && payloadInSize > mPayloadLimit) { + LOG(error) << "Payload larger than limit (" << mPayloadLimit << "), payload = " << payloadInSize; + continue; + } + /** prepare compressor **/ mCompressor.setDecoderBuffer(payloadIn); mCompressor.setDecoderBufferSize(payloadInSize); diff --git a/Detectors/TOF/compression/src/CompressorTaskOld.cxx b/Detectors/TOF/compression/src/CompressorTaskOld.cxx index 333b79865de2d..1a4e28393a8c3 100644 --- a/Detectors/TOF/compression/src/CompressorTaskOld.cxx +++ b/Detectors/TOF/compression/src/CompressorTaskOld.cxx @@ -36,7 +36,11 @@ namespace tof template void CompressorTaskOld::init(InitContext& ic) { - LOG(info) << "Compressor init"; + if (mPayloadLimit < 0) { + LOG(info) << "Compressor init"; + } else { + LOG(info) << "Compressor init with Payload limit at " << mPayloadLimit; + } auto decoderCONET = ic.options().get("tof-compressor-conet-mode"); auto decoderVerbose = ic.options().get("tof-compressor-decoder-verbose"); @@ -166,6 +170,11 @@ void CompressorTaskOld::run(ProcessingContext& pc) auto payloadIn = ref.payload; auto payloadInSize = DataRefUtils::getPayloadSize(ref); + if (mPayloadLimit > -1 && payloadInSize > mPayloadLimit) { + LOG(error) << "Payload larger than limit (" << mPayloadLimit << "), payload = " << payloadInSize; + continue; + } + /** prepare compressor **/ mCompressor.setDecoderBuffer(payloadIn); mCompressor.setDecoderBufferSize(payloadInSize); diff --git a/Detectors/TOF/compression/src/tof-compressor.cxx b/Detectors/TOF/compression/src/tof-compressor.cxx index 9d248fcaf8db2..4358ceae84bdb 100644 --- a/Detectors/TOF/compression/src/tof-compressor.cxx +++ b/Detectors/TOF/compression/src/tof-compressor.cxx @@ -36,6 +36,7 @@ void customize(std::vector& workflowOptions) auto verbose = ConfigParamSpec{"tof-compressor-verbose", VariantType::Bool, false, {"Enable verbose compressor"}}; auto paranoid = ConfigParamSpec{"tof-compressor-paranoid", VariantType::Bool, false, {"Enable paranoid compressor"}}; auto ignoreStf = ConfigParamSpec{"ignore-dist-stf", VariantType::Bool, false, {"do not subscribe to FLP/DISTSUBTIMEFRAME/0 message (no lost TF recovery)"}}; + auto payloadlim = ConfigParamSpec{"payload-limit", VariantType::Int64, -1ll, {"Payload limit in Byte (-1 -> no limits)"}}; workflowOptions.push_back(config); workflowOptions.push_back(outputDesc); @@ -43,6 +44,7 @@ void customize(std::vector& workflowOptions) workflowOptions.push_back(verbose); workflowOptions.push_back(paranoid); workflowOptions.push_back(ignoreStf); + workflowOptions.push_back(payloadlim); workflowOptions.emplace_back(ConfigParamSpec{"old", VariantType::Bool, false, {"use the non-DPL version of the compressor"}}); workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); } @@ -60,6 +62,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) auto paranoid = cfgc.options().get("tof-compressor-paranoid"); auto ignoreStf = cfgc.options().get("ignore-dist-stf"); auto old = cfgc.options().get("old"); + auto payloadLim = cfgc.options().get("payload-limit"); std::vector outputs; outputs.emplace_back(OutputSpec(ConcreteDataTypeMatcher{"TOF", "CRAWDATA"})); @@ -68,30 +71,30 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) if (rdhVersion == o2::raw::RDHUtils::getVersion()) { if (!verbose && !paranoid) { if (old) { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } else { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } } if (!verbose && paranoid) { if (old) { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } else { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } } if (verbose && !paranoid) { if (old) { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } else { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } } if (verbose && paranoid) { if (old) { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } else { - algoSpec = AlgorithmSpec{adaptFromTask>()}; + algoSpec = AlgorithmSpec{adaptFromTask>(payloadLim)}; } } } @@ -132,7 +135,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) outputs, algoSpec, Options{ - {"tof-compressor-output-buffer-size", VariantType::Int, 0, {"Encoder output buffer size (in bytes). Zero = automatic (careful)."}}, + {"tof-compressor-output-buffer-size", VariantType::Int, 1048576, {"Encoder output buffer size (in bytes). Zero = automatic (careful)."}}, {"tof-compressor-conet-mode", VariantType::Bool, false, {"Decoder CONET flag"}}, {"tof-compressor-decoder-verbose", VariantType::Bool, false, {"Decoder verbose flag"}}, {"tof-compressor-encoder-verbose", VariantType::Bool, false, {"Encoder verbose flag"}}, diff --git a/Detectors/TOF/workflow/src/TOFMergeIntegrateClusterSpec.cxx b/Detectors/TOF/workflow/src/TOFMergeIntegrateClusterSpec.cxx index e2066461cc236..1a609df1c5212 100644 --- a/Detectors/TOF/workflow/src/TOFMergeIntegrateClusterSpec.cxx +++ b/Detectors/TOF/workflow/src/TOFMergeIntegrateClusterSpec.cxx @@ -159,7 +159,7 @@ o2::framework::DataProcessorSpec getTOFMergeIntegrateClusterSpec() inputs.emplace_back("itofcq", o2::header::gDataOriginTOF, "ITOFCQ", 0, Lifetime::Sporadic); auto ccdbRequest = std::make_shared(true, // orbitResetTime - false, // GRPECS=true for nHBF per TF + true, // GRPECS=true for nHBF per TF false, // GRPLHCIF false, // GRPMagField false, // askMatLUT diff --git a/Detectors/TPC/base/include/TPCBase/CDBInterface.h b/Detectors/TPC/base/include/TPCBase/CDBInterface.h index d601f66db9d99..73ef44f8a510c 100644 --- a/Detectors/TPC/base/include/TPCBase/CDBInterface.h +++ b/Detectors/TPC/base/include/TPCBase/CDBInterface.h @@ -86,6 +86,8 @@ enum class CDBType { CalCorrMapRef, ///< Cluster correction reference map (static distortions) /// CalCorrDerivMap, ///< Cluster correction map (derivative map) + /// + CalTimeSeries, ///< integrated DCAs for longer time interval }; /// Upload intervention type @@ -145,6 +147,8 @@ const std::unordered_map CDBTypeMap{ {CDBType::CalCorrMapRef, "TPC/Calib/CorrectionMapRefV2"}, // derivative map correction {CDBType::CalCorrDerivMap, "TPC/Calib/CorrectionMapDerivativeV2"}, + // time series + {CDBType::CalTimeSeries, "TPC/Calib/TimeSeries"}, }; /// Poor enum reflection ... diff --git a/Detectors/TPC/base/src/Painter.cxx b/Detectors/TPC/base/src/Painter.cxx index c85ad0164a0fc..7339a11e4c8e0 100644 --- a/Detectors/TPC/base/src/Painter.cxx +++ b/Detectors/TPC/base/src/Painter.cxx @@ -1134,9 +1134,11 @@ std::vector painter::makeSummaryCanvases(const LtrCalibData& ltr, std: calibValMsg->AddText(fmt::format("dvCorrectionA: {}", ltr.dvCorrectionA).data()); calibValMsg->AddText(fmt::format("dvCorrectionC: {}", ltr.dvCorrectionC).data()); calibValMsg->AddText(fmt::format("dvCorrection: {}", ltr.getDriftVCorrection()).data()); - calibValMsg->AddText(fmt::format("dvAbsolute: {}", ltr.getDriftVCorrection() * ltr.refVDrift).data()); + calibValMsg->AddText(fmt::format("dvAbsolute: {}", ltr.refVDrift / ltr.getDriftVCorrection()).data()); calibValMsg->AddText(fmt::format("dvOffsetA: {}", ltr.dvOffsetA).data()); calibValMsg->AddText(fmt::format("dvOffsetC: {}", ltr.dvOffsetC).data()); + calibValMsg->AddText(fmt::format("t0A: {}", ltr.getT0A()).data()); + calibValMsg->AddText(fmt::format("t0C: {}", ltr.getT0C()).data()); calibValMsg->AddText(fmt::format("nTracksA: {}", ltr.nTracksA).data()); calibValMsg->AddText(fmt::format("nTracksC: {}", ltr.nTracksC).data()); calibValMsg->AddText(fmt::format("#LTdEdx A#GT: {}", dEdxSumA).data()); diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h index eed7989882c0c..cf77c69374f77 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibLaserTracks.h @@ -23,6 +23,7 @@ #define TPC_CalibLaserTracks_H_ #include +#include #include "CommonConstants/MathConstants.h" #include "CommonUtils/TreeStreamRedirector.h" @@ -140,7 +141,7 @@ class CalibLaserTracks bool getWriteDebugTree() const { return mWriteDebugTree; } /// extract DV correction and T0 offset - TimePair fit(const std::vector& trackMatches) const; + TimePair fit(const std::vector& trackMatches, std::string_view info) const; /// sort TimePoint vectors void sort(std::vector& trackMatches); diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h index d8b0634736240..54b9d6f98ee35 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h @@ -26,6 +26,7 @@ #include "DataFormatsTPC/TrackCuts.h" #include "DataFormatsTPC/Defs.h" #include "DataFormatsTPC/CalibdEdxCorrection.h" +#include "DetectorsBase/Propagator.h" // boost includes #include @@ -97,6 +98,9 @@ class CalibdEdx mFitLowCutFactor = lowCutFactor; } + /// setting the material type for track propagation + void setMaterialType(o2::base::Propagator::MatCorrType materialType) { mMatType = materialType; } + /// Fill histograms using tracks data. void fill(const TrackTPC& tracks); void fill(const gsl::span); @@ -152,7 +156,9 @@ class CalibdEdx Hist mHist; ///< dEdx multidimensional histogram CalibdEdxCorrection mCalib{}; ///< Calibration output - ClassDefNV(CalibdEdx, 2); + o2::base::Propagator::MatCorrType mMatType{}; ///< material type for track propagation + + ClassDefNV(CalibdEdx, 3); }; } // namespace o2::tpc diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibratordEdx.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibratordEdx.h index 3119d575b1723..1b90e525cf6dc 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibratordEdx.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibratordEdx.h @@ -29,6 +29,7 @@ #include "DetectorsCalibration/TimeSlot.h" #include "TPCCalibration/CalibdEdx.h" #include "CommonUtils/TreeStreamRedirector.h" +#include "DetectorsBase/Propagator.h" namespace o2::tpc { @@ -58,6 +59,7 @@ class CalibratordEdx final : public o2::calibration::TimeSlotCalibration values) { mElectronCut = values; } + void setMaterialType(o2::base::Propagator::MatCorrType materialType) { mMatType = materialType; } /// \brief Check if there are enough data to compute the calibration. /// \return false if any of the histograms has less entries than mMinEntries @@ -104,6 +106,7 @@ class CalibratordEdx final : public o2::calibration::TimeSlotCalibration mElectronCut{}; ///< Values passed to CalibdEdx::setElectronCut TrackCuts mCuts; ///< Cut object + o2::base::Propagator::MatCorrType mMatType{}; ///< material type for track propagation TFinterval mTFIntervals; ///< start and end time frame IDs of each calibration time slots TimeInterval mTimeIntervals; ///< start and end times of each calibration time slots @@ -111,7 +114,7 @@ class CalibratordEdx final : public o2::calibration::TimeSlotCalibration mDebugOutputStreamer; ///< Debug output streamer - ClassDefOverride(CalibratordEdx, 1); + ClassDefOverride(CalibratordEdx, 2); }; } // namespace o2::tpc diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h index 0771b922a19d4..3f832e35557fb 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h @@ -48,12 +48,17 @@ class CorrectionMapsLoader : public o2::gpu::CorrectionMapsHelper void extractCCDBInputs(o2::framework::ProcessingContext& pc); void updateVDrift(float vdriftCorr, float vdrifRef, float driftTimeOffset = 0); void init(o2::framework::InitContext& ic); + void copySettings(const CorrectionMapsLoader& src); + static void requestCCDBInputs(std::vector& inputs, std::vector& options, bool requestCTPLumi = false, int lumiScaleMode = 0); static void addOptions(std::vector& options); protected: static void addOption(std::vector& options, o2::framework::ConfigParamSpec&& osp); static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); + + float mInstLumiFactor = 1.0; // multiplicative factor for inst. lumi + int mCTPLumiSource = 0; // 0: main, 1: alternative CTP lumi source #endif }; diff --git a/Detectors/TPC/calibration/include/TPCCalibration/RobustAverage.h b/Detectors/TPC/calibration/include/TPCCalibration/RobustAverage.h index 9b942b35a33dd..debf449189f15 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/RobustAverage.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/RobustAverage.h @@ -57,23 +57,23 @@ class RobustAverage /// reserve memory for member /// \param maxValues maximum number of values which will be averaged. Copy of values will be done. - void reserve(const unsigned int maxValues) { mValues.reserve(maxValues); } + void reserve(const unsigned int maxValues); /// clear the stored values void clear(); /// \param value value which will be added to the list of stored values for averaging /// \param weight weight of the value - void addValue(const float value, const float weight); - - /// \param value value which will be added to the list of stored values for averaging - void addValue(const float value); + void addValue(const float value, const float weight = 1); /// returns the filtered average value /// \param sigma maximum accepted standard deviation: sigma*stdev ///\param interQuartileRange number of points in inner quartile to consider std::pair getFilteredAverage(const float sigma = 3, const float interQuartileRange = 0.9); + /// remove all the point which are abs(val - val_median)>maxAbsMedian + std::tuple filterPointsMedian(const float maxAbsMedian, const float sigma = 5); + /// \return returns mean of stored values float getMean() const { return mValues.empty() ? 0 : getMean(mValues.begin(), mValues.end()); } @@ -89,9 +89,15 @@ class RobustAverage /// \return returns stored values const auto& getValues() { return mValues; } + /// \return returns stored values + const auto& getWeigths() { return mWeights; } + /// values which will be averaged and filtered void print() const; + // sorting values and weights + void sort(); + private: std::vector mValues{}; ///< values which will be averaged and filtered std::vector mWeights{}; ///< weights of each value diff --git a/Detectors/TPC/calibration/include/TPCCalibration/SACDecoder.h b/Detectors/TPC/calibration/include/TPCCalibration/SACDecoder.h index bf8c3bf9e83bf..d16efc8995e8d 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/SACDecoder.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/SACDecoder.h @@ -31,7 +31,6 @@ #include "DataFormatsTPC/SAC.h" using o2::constants::lhc::LHCBunchSpacingMUS; -using std::size_t; namespace o2::tpc::sac { diff --git a/Detectors/TPC/calibration/src/CalibLaserTracks.cxx b/Detectors/TPC/calibration/src/CalibLaserTracks.cxx index cc796462152ce..81e3d9f99dde5 100644 --- a/Detectors/TPC/calibration/src/CalibLaserTracks.cxx +++ b/Detectors/TPC/calibration/src/CalibLaserTracks.cxx @@ -19,6 +19,8 @@ #include "TPCCalibration/CalibLaserTracks.h" #include "TLinearFitter.h" #include +#include +#include using namespace o2::tpc; void CalibLaserTracks::fill(std::vector const& tracks) @@ -94,6 +96,7 @@ void CalibLaserTracks::processTrack(const TrackTPC& track) zTrack += zOffset; parOutAtLtr.setZ(zTrack); } else if (mTriggerPos > 0) { + const float zOffset = mTriggerPos; } if (std::abs(zTrack) > 300) { @@ -321,8 +324,8 @@ void CalibLaserTracks::finalize() //______________________________________________________________________________ void CalibLaserTracks::fillCalibData(LtrCalibData& calibData, const std::vector& pairsA, const std::vector& pairsC) { - auto dvA = fit(pairsA); - auto dvC = fit(pairsC); + auto dvA = fit(pairsA, "A-Side"); + auto dvC = fit(pairsC, "C-Side"); calibData.creationTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); calibData.refVDrift = mDriftV; calibData.dvOffsetA = dvA.x1; @@ -335,7 +338,7 @@ void CalibLaserTracks::fillCalibData(LtrCalibData& calibData, const std::vector< } //______________________________________________________________________________ -TimePair CalibLaserTracks::fit(const std::vector& trackMatches) const +TimePair CalibLaserTracks::fit(const std::vector& trackMatches, std::string_view info) const { if (!trackMatches.size()) { return TimePair(); @@ -346,12 +349,17 @@ TimePair CalibLaserTracks::fit(const std::vector& trackMatches) const fit.ClearPoints(); uint64_t meanTime = 0; + double minZrec = 1000; + double maxZrec = -1000; for (const auto& point : trackMatches) { double x = point.x1; double y = point.x2; fit.AddPoint(&x, y); meanTime += point.time; + + minZrec = std::min(y, minZrec); + maxZrec = std::max(y, maxZrec); } meanTime /= uint64_t(trackMatches.size()); @@ -360,6 +368,11 @@ TimePair CalibLaserTracks::fit(const std::vector& trackMatches) const const int minPoints = 6; if (trackMatches.size() < size_t(minPoints / robustFraction)) { + LOGP(warning, "{} - Not enough points to perform the fit: {} < {}", info, trackMatches.size(), size_t(minPoints / robustFraction)); + return TimePair({0, 0, meanTime}); + } + if (std::abs(maxZrec - minZrec) < 50) { + LOGP(warning, "{} - All points seem to be from one Layer: abs({}cm - {}cm) < 50cm, will not do fitting", info, trackMatches.size(), maxZrec, minZrec); return TimePair({0, 0, meanTime}); } @@ -385,7 +398,7 @@ void CalibLaserTracks::print() const { if (mFinalized) { LOGP(info, - "Processed {} TFs from {} - {}; found tracks: {} / {}; T0 offsets: {} / {}; dv correction factors: {} / {} for A- / C-Side, reference: {}", + "Processed {} TFs from {} - {}; found tracks: {} / {}; fit offsets (cm): {} / {}; T0s (us): {} / {}; dv correction factors: {} / {} for A- / C-Side, reference: {}", mCalibData.processedTFs, mCalibData.firstTime, mCalibData.lastTime, @@ -393,6 +406,8 @@ void CalibLaserTracks::print() const mCalibData.nTracksC, mCalibData.dvOffsetA, mCalibData.dvOffsetC, + mCalibData.getT0A(), + mCalibData.getT0C(), mCalibData.dvCorrectionA, mCalibData.dvCorrectionC, mCalibData.refVDrift); @@ -404,13 +419,15 @@ void CalibLaserTracks::print() const mCalibData.lastTime); LOGP(info, - "Last processed TF from {} - {}; found tracks: {} / {}; T0 offsets: {} / {}; dv correction factors: {} / {} for A- / C-Side, reference: {}", + "Last processed TF from {} - {}; found tracks: {} / {}; fit offsets (cm): {} / {}; T0s (us): {} / {}; dv correction factors: {} / {} for A- / C-Side, reference: {}", mCalibDataTF.firstTime, mCalibDataTF.lastTime, mCalibDataTF.nTracksA, mCalibDataTF.nTracksC, mCalibDataTF.dvOffsetA, mCalibDataTF.dvOffsetC, + mCalibDataTF.getT0A(), + mCalibDataTF.getT0C(), mCalibDataTF.dvCorrectionA, mCalibDataTF.dvCorrectionC, mCalibDataTF.refVDrift); diff --git a/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx b/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx index b57aabe469484..97927107d56a9 100644 --- a/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx +++ b/Detectors/TPC/calibration/src/CalibPadGainTracks.cxx @@ -25,6 +25,7 @@ #include "GPUO2Interface.h" #include "DataFormatsTPC/ClusterNative.h" #include "DataFormatsTPC/VDriftCorrFact.h" +#include "DetectorsBase/Propagator.h" // root includes #include "TFile.h" @@ -92,8 +93,10 @@ void CalibPadGainTracks::processTrack(o2::tpc::TrackTPC track, o2::gpu::GPUO2Int const float xPosition = Mapper::instance().getPadCentre(PadPos(rowIndex, 0)).X(); if (!mPropagateTrack) { refit->setTrackReferenceX(xPosition); + } else { + track.rotate(o2::math_utils::detail::sector2Angle(sectorIndex)); } - const bool check = mPropagateTrack ? track.propagateTo(xPosition, mField) : ((refit->RefitTrackAsGPU(track, false, true) < 0) ? false : true); // propagate this track to the plane X=xk (cm) in the field "b" (kG) + const bool check = mPropagateTrack ? o2::base::Propagator::Instance()->PropagateToXBxByBz(track, xPosition, 0.9f, 2., o2::base::Propagator::MatCorrType::USEMatCorrLUT) : ((refit->RefitTrackAsGPU(track, false, true) < 0) ? false : true); // propagate this track to the plane X=xk (cm) in the field "b" (kG) if (!check || std::isnan(track.getParam(1))) { continue; diff --git a/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx b/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx index a964de03f891b..2d8c34810324b 100644 --- a/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx +++ b/Detectors/TPC/calibration/src/CalibPadGainTracksBase.cxx @@ -212,7 +212,7 @@ void CalibPadGainTracksBase::finalize(const int minEntries, const float minRelga const auto& histo = mPadHistosDet->getCalArray(roc).getData()[pad]; unsigned int entries = histo.getEntries(); const auto stat = histo.getStatisticsData(low, high); - const auto cog = std::clamp(static_cast(stat.mCOG), minRelgain, maxRelgain); + const auto cog = static_cast(stat.mCOG); // subtract underflow and overflow entries to check if only the valid entries are > 0 if (histo.isUnderflowSet()) { @@ -235,6 +235,14 @@ void CalibPadGainTracksBase::finalize(const int minEntries, const float minRelga } } normalizeGain(*mGainMap.get()); + + // clamp to min/max + for (int roc = 0; roc < ROC::MaxROC; ++roc) { + const auto padsInRoc = ROC(roc).isIROC() ? Mapper::getPadsInIROC() : Mapper::getPadsInOROC(); + for (int pad = 0; pad < padsInRoc; ++pad) { + mGainMap->getCalArray(roc).getData()[pad] = std::clamp(mGainMap->getCalArray(roc).getData()[pad], minRelgain, maxRelgain); + } + } } void CalibPadGainTracksBase::normalizeGain(CalPad& calPad) @@ -265,7 +273,7 @@ void CalibPadGainTracksBase::normalize(std::vector& data, const std::vect int padStart = 0; for (const auto pads : nPads) { auto median = TMath::Median(pads, data.data() + padStart); - std::for_each(data.data() + padStart, data.data() + padStart + pads, [median](auto& val) { val /= (val > 0) ? median : 1; }); + std::for_each(data.data() + padStart, data.data() + padStart + pads, [median](auto& val) { val /= ((val > 0) && (val != 1)) ? median : 1; }); padStart += pads; } } diff --git a/Detectors/TPC/calibration/src/CalibdEdx.cxx b/Detectors/TPC/calibration/src/CalibdEdx.cxx index f8e138d166ae7..c012a4a2b3ec9 100644 --- a/Detectors/TPC/calibration/src/CalibdEdx.cxx +++ b/Detectors/TPC/calibration/src/CalibdEdx.cxx @@ -29,7 +29,6 @@ #include "DataFormatsTPC/TrackCuts.h" #include "Framework/Logger.h" #include "TPCBase/ParameterGas.h" -#include "DetectorsBase/Propagator.h" // root includes #include "TFile.h" @@ -82,7 +81,7 @@ void CalibdEdx::fill(const TrackTPC& track) constexpr std::array xks{108.475f, 151.7f, 188.8f, 227.65f}; // propagate track - const bool okProp = o2::base::Propagator::Instance()->PropagateToXBxByBz(cpTrack, xks[roc], 0.9f, 2., o2::base::Propagator::MatCorrType::USEMatCorrNONE); + const bool okProp = o2::base::Propagator::Instance()->PropagateToXBxByBz(cpTrack, xks[roc], 0.9f, 2., mMatType); if (!okProp) { continue; } diff --git a/Detectors/TPC/calibration/src/CalibratordEdx.cxx b/Detectors/TPC/calibration/src/CalibratordEdx.cxx index 32d138e28b153..f16fb6f19ebdf 100644 --- a/Detectors/TPC/calibration/src/CalibratordEdx.cxx +++ b/Detectors/TPC/calibration/src/CalibratordEdx.cxx @@ -76,6 +76,7 @@ CalibratordEdx::Slot& CalibratordEdx::emplaceNewSlot(bool front, TFType tstart, container->set2DFitThreshold(mFitThreshold[2]); const auto [cut, iterations, cutLowFactor] = mElectronCut; container->setElectronCut(cut, iterations, cutLowFactor); + container->setMaterialType(mMatType); slot.setContainer(std::move(container)); return slot; diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index 1c9a5852a6953..92432162872a9 100644 --- a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx @@ -42,9 +42,20 @@ void CorrectionMapsLoader::extractCCDBInputs(ProcessingContext& pc) { pc.inputs().get("tpcCorrMap"); pc.inputs().get("tpcCorrMapRef"); // not used at the moment + const int maxDumRep = 5; + int dumRep = 0; + o2::ctp::LumiInfo lumiObj; + static o2::ctp::LumiInfo lumiPrev; if (getUseCTPLumi() && mInstLumiOverride <= 0.) { - auto lumiObj = pc.inputs().get("CTPLumi"); - setInstLumi(lumiObj.getLumi()); + if (pc.inputs().get>("CTPLumi").size() == sizeof(o2::ctp::LumiInfo)) { + lumiPrev = lumiObj = pc.inputs().get("CTPLumi"); + } else { + if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { + LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); + } + lumiObj = lumiPrev; + } + setInstLumi(mInstLumiFactor * (mCTPLumiSource == 0 ? lumiObj.getLumi() : lumiObj.getLumiAlt())); } } @@ -72,6 +83,8 @@ void CorrectionMapsLoader::addOptions(std::vector& options) addOption(options, ConfigParamSpec{"corrmap-lumi-mean", VariantType::Float, 0.f, {"override TPC corr.map mean lumi (if > 0), disable corrections if < 0"}}); addOption(options, ConfigParamSpec{"corrmap-lumi-inst", VariantType::Float, 0.f, {"override instantaneous CTP lumi (if > 0) for TPC corr.map scaling, disable corrections if < 0"}}); addOption(options, ConfigParamSpec{"corrmap-lumi-mode", VariantType::Int, 0, {"scaling mode: (default) 0 = static + scale * full; 1 = full + scale * derivative"}}); + addOption(options, ConfigParamSpec{"ctp-lumi-factor", VariantType::Float, 1.0f, {"scaling to apply to instantaneous lumi from CTP (but not corrmap-lumi-inst)"}}); + addOption(options, ConfigParamSpec{"ctp-lumi-source", VariantType::Int, 0, {"CTP lumi source: 0 = LumiInfo.getLumi(), 1 = LumiInfo.getLumiAlt()"}}); } //________________________________________________________ @@ -125,12 +138,29 @@ void CorrectionMapsLoader::init(o2::framework::InitContext& ic) mMeanLumiOverride = ic.options().get("corrmap-lumi-mean"); mInstLumiOverride = ic.options().get("corrmap-lumi-inst"); mLumiScaleMode = ic.options().get("corrmap-lumi-mode"); + mInstLumiFactor = ic.options().get("ctp-lumi-factor"); + mCTPLumiSource = ic.options().get("ctp-lumi-source"); if (mMeanLumiOverride != 0.) { setMeanLumi(mMeanLumiOverride); } if (mInstLumiOverride != 0.) { setInstLumi(mInstLumiOverride); } - LOGP(info, "CTP Lumi request for TPC corr.map scaling={}, override values: lumiMean={} lumiInst={} lumiScaleMode={}", getUseCTPLumi() ? "ON" : "OFF", mMeanLumiOverride, mInstLumiOverride, mLumiScaleMode); + LOGP(info, "CTP Lumi request for TPC corr.map scaling={}, override values: lumiMean={} lumiInst={} lumiScaleMode={}, LumiInst scale={}, CTP Lumi source={}", + getUseCTPLumi() ? "ON" : "OFF", mMeanLumiOverride, mInstLumiOverride, mLumiScaleMode, mInstLumiFactor, mCTPLumiSource); +} + +//________________________________________________________ +void CorrectionMapsLoader::copySettings(const CorrectionMapsLoader& src) +{ + setInstLumi(src.getInstLumi(), false); + setMeanLumi(src.getMeanLumi(), false); + setUseCTPLumi(src.getUseCTPLumi()); + setMeanLumiOverride(src.getMeanLumiOverride()); + setInstLumiOverride(src.getInstLumiOverride()); + setLumiScaleMode(src.getLumiScaleMode()); + mInstLumiFactor = src.mInstLumiFactor; + mCTPLumiSource = src.mCTPLumiSource; } + #endif // #ifndef GPUCA_GPUCODE_DEVICE diff --git a/Detectors/TPC/calibration/src/RobustAverage.cxx b/Detectors/TPC/calibration/src/RobustAverage.cxx index 1bbe65856e10c..3b223c2ef0cbc 100644 --- a/Detectors/TPC/calibration/src/RobustAverage.cxx +++ b/Detectors/TPC/calibration/src/RobustAverage.cxx @@ -13,6 +13,12 @@ #include "Framework/Logger.h" #include +void o2::tpc::RobustAverage::reserve(const unsigned int maxValues) +{ + mValues.reserve(maxValues); + mWeights.reserve(maxValues); +} + std::pair o2::tpc::RobustAverage::getFilteredAverage(const float sigma, const float interQuartileRange) { if (mValues.empty()) { @@ -28,7 +34,7 @@ std::pair o2::tpc::RobustAverage::getFilteredAverage(const float s */ // 1. Sort the values - std::sort(mValues.begin(), mValues.end()); + sort(); // 2. Use only the values in the Interquartile Range (inner n%) const auto upper = mValues.begin() + mValues.size() * interQuartileRange; @@ -48,6 +54,81 @@ std::pair o2::tpc::RobustAverage::getFilteredAverage(const float s return std::pair(getFilteredMean(median, stdev, sigma), stdev); } +std::tuple o2::tpc::RobustAverage::filterPointsMedian(const float maxAbsMedian, const float sigma) +{ + if (mValues.empty()) { + return {0, 0, 0, 0}; + } + + // 1. Sort the values + sort(); + + // 2. median + const float median = mValues[mValues.size() / 2]; + + // 3. select points larger and smaller than specified max value + const auto upperV0 = std::upper_bound(mValues.begin(), mValues.end(), median + maxAbsMedian); + const auto lowerV0 = std::lower_bound(mValues.begin(), mValues.end(), median - maxAbsMedian); + + if (upperV0 == lowerV0) { + return {0, 0, 0, 0}; + } + + // 4. get RMS of selected values + const float stdev = getStdDev(median, lowerV0, upperV0); + + // 5. define RMS cut + const float sigmastddev = sigma * stdev; + const float minVal = median - sigmastddev; + const float maxVal = median + sigmastddev; + const auto upper = std::upper_bound(mValues.begin(), mValues.end(), maxVal); + const auto lower = std::lower_bound(mValues.begin(), mValues.end(), minVal); + + if (upper == lower) { + return {0, 0, 0, 0}; + } + + const int indexUp = upper - mValues.begin(); + const int indexLow = lower - mValues.begin(); + + // 7. get filtered median + const float medianFilterd = mValues[(indexUp - indexLow) / 2 + indexLow]; + + // 8. weighted mean + const auto upperW = mWeights.begin() + indexUp; + const auto lowerW = mWeights.begin() + indexLow; + const float wMean = getWeightedMean(lower, upper, lowerW, upperW); + + // 9. number of points passing the cuts + const int nEntries = indexUp - indexLow; + + return {medianFilterd, wMean, stdev, nEntries}; +} + +void o2::tpc::RobustAverage::sort() +{ + const size_t nVals = mValues.size(); + if (mValues.size() != mWeights.size()) { + LOGP(warning, "values and errors haave different size"); + return; + } + std::vector tmpIdx(nVals); + std::iota(tmpIdx.begin(), tmpIdx.end(), 0); + std::sort(tmpIdx.begin(), tmpIdx.end(), [&](std::size_t i, std::size_t j) { return (mValues[i] < mValues[j]); }); + + std::vector mValues_tmp; + std::vector mWeights_tmp; + mValues_tmp.reserve(nVals); + mWeights_tmp.reserve(nVals); + for (int i = 0; i < nVals; ++i) { + const int idx = tmpIdx[i]; + mValues_tmp.emplace_back(mValues[idx]); + mWeights_tmp.emplace_back(mWeights[idx]); + } + mValues.swap(mValues_tmp); + mWeights.swap(mWeights_tmp); +} + float o2::tpc::RobustAverage::getStdDev(const float mean, std::vector::const_iterator begin, std::vector::const_iterator end) { const auto size = std::distance(begin, end); @@ -110,8 +191,3 @@ void o2::tpc::RobustAverage::addValue(const float value, const float weight) mValues.emplace_back(value); mWeights.emplace_back(weight); } - -void o2::tpc::RobustAverage::addValue(const float value) -{ - mValues.emplace_back(value); -} diff --git a/Detectors/TPC/calibration/src/TrackDump.cxx b/Detectors/TPC/calibration/src/TrackDump.cxx index f309dc5e77179..ca8d61643e7ee 100644 --- a/Detectors/TPC/calibration/src/TrackDump.cxx +++ b/Detectors/TPC/calibration/src/TrackDump.cxx @@ -107,7 +107,7 @@ void TrackDump::filter(const gsl::span tracks, ClusterNativeAcce } } if (writeGlobal) { - tree << "cls" << clustersGlobalEvent; + tree << "clsGlo=" << clustersGlobalEvent; } if (writeMC) { tree << "TPCTracksMCTruth=" << tpcTracksMCTruth; @@ -128,7 +128,7 @@ void TrackDump::filter(const gsl::span tracks, ClusterNativeAcce std::vector clustersUn; fillClNativeAdd(clusterIndex, clustersUn, &excludes); auto& cltree = (*treeDumpUn) << "clsn"; - cltree << "cls=" << clusters + cltree << "cls=" << clustersUn << "\n"; } } diff --git a/Detectors/TPC/calibration/src/VDriftHelper.cxx b/Detectors/TPC/calibration/src/VDriftHelper.cxx index 13f8e3e5bcc27..034888998e567 100644 --- a/Detectors/TPC/calibration/src/VDriftHelper.cxx +++ b/Detectors/TPC/calibration/src/VDriftHelper.cxx @@ -58,7 +58,7 @@ void VDriftHelper::accountLaserCalibration(const LtrCalibData* calib, long fallB } // old entries of laser calib have no update time assigned long updateTS = calib->creationTime > 0 ? calib->creationTime : fallBackTimeStamp; - LOG(info) << "accountLaserCalibration " << calib->getDriftVCorrection() << " t " << updateTS << " vs " << mVDLaser.creationTime; + LOG(info) << "accountLaserCalibration " << calib->refVDrift << " / " << calib->getDriftVCorrection() << " t " << updateTS << " vs " << mVDLaser.creationTime; // old entries of laser calib have no reference assigned float ref = calib->refVDrift > 0. ? calib->refVDrift : o2::tpc::ParameterGas::Instance().DriftV; float corr = calib->getDriftVCorrection(); @@ -71,7 +71,7 @@ void VDriftHelper::accountLaserCalibration(const LtrCalibData* calib, long fallB mUpdated = true; mSource = Source::Laser; if (mMayRenormSrc & (0x1U << Source::Laser)) { // this was 1st setting? - if (corr != 1.f) { // this may happen if old-style (non-normalized) standalone or non-normalized run-time laset calibration is used + if (corr != 1.f) { // this may happen if old-style (non-normalized) standalone or non-normalized run-time laset calibration is used LOGP(warn, "VDriftHelper: renorming initinal TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDLaser.refVDrift, mVDLaser.corrFact, mVDLaser.getVDrift(), getSourceName(mSource)); mVDLaser.normalize(); // renorm reference to have correction = 1. } @@ -95,7 +95,7 @@ void VDriftHelper::accountDriftCorrectionITSTPCTgl(const VDriftCorrFact* calib) mVDTPCITSTgl = *calib; mUpdated = true; mSource = Source::ITSTPCTgl; - if (mMayRenormSrc & (0x1U << Source::ITSTPCTgl)) { // this was 1st setting? + if (mMayRenormSrc & (0x1U << Source::ITSTPCTgl)) { // this was 1st setting? if (!mForceParamDrift && mVDTPCITSTgl.corrFact != 1.f) { // this may happen if calibration from prevous run is used LOGP(warn, "VDriftHelper: renorming initinal TPC refVDrift={}/correction={} to {}/1.0, source: {}", mVDTPCITSTgl.refVDrift, mVDTPCITSTgl.corrFact, mVDTPCITSTgl.getVDrift(), getSourceName(mSource)); mVDTPCITSTgl.normalize(); // renorm reference to have correction = 1. diff --git a/Detectors/TPC/dcs/src/DCSConfigSpec.cxx b/Detectors/TPC/dcs/src/DCSConfigSpec.cxx index c01b5c83d6593..fb8b61574b60b 100644 --- a/Detectors/TPC/dcs/src/DCSConfigSpec.cxx +++ b/Detectors/TPC/dcs/src/DCSConfigSpec.cxx @@ -33,6 +33,8 @@ #include "Framework/ConfigParamRegistry.h" #include "DetectorsCalibration/Utils.h" #include "CommonUtils/StringUtils.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbObjectInfo.h" #include "CCDB/CcdbApi.h" #include "CommonUtils/NameConf.h" @@ -197,7 +199,11 @@ void DCSConfigDevice::updateRunInfo(gsl::span configBuff) const long startValRCT = std::stol(data[1]); const long endValRCT = startValRCT + 48l * 60l * 60l * 1000l; if (!mDontWriteRunInfo) { + o2::ccdb::CcdbObjectInfo w(CDBTypeMap.at(CDBType::ConfigRunInfo), "", "", md, startValRCT, endValRCT); mCCDBApi.storeAsBinaryFile(&tempChar, sizeof(tempChar), "tmp.dat", "char", CDBTypeMap.at(CDBType::ConfigRunInfo), md, startValRCT, endValRCT); + if (!mCCDBApi.isSnapshotMode()) { + o2::ccdb::adjustOverriddenEOV(mCCDBApi, w); + } } std::string mdInfo = "["; diff --git a/Detectors/TPC/qc/include/TPCQC/Helpers.h b/Detectors/TPC/qc/include/TPCQC/Helpers.h index 9469121bf8f19..23eeb8286104e 100644 --- a/Detectors/TPC/qc/include/TPCQC/Helpers.h +++ b/Detectors/TPC/qc/include/TPCQC/Helpers.h @@ -22,6 +22,7 @@ #include "TH2F.h" #include +#include #include "TPCBase/CalDet.h" namespace o2 @@ -58,6 +59,10 @@ void setStyleHistogram(TH1& histo); void setStyleHistogramsInMap(std::unordered_map>>& mapOfvectors); // set nice style of histograms in a map void setStyleHistogramsInMap(std::unordered_map>& mapOfHisto); +// set nice style of histograms in a map of vectors +void setStyleHistogramsInMap(std::unordered_map>>& mapOfvectors); +// set nice style of histograms in a map +void setStyleHistogramsInMap(std::unordered_map>& mapOfHisto); /// Check if at least one pad in refPedestal and pedestal differs by 3*refNoise to see if new ZS calibration data should be uploaded to the FECs. /// @param refPedestal /// @param refNoise @@ -70,4 +75,4 @@ bool newZSCalib(const o2::tpc::CalDet& refPedestal, const o2::tpc::CalDet } // namespace tpc } // namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/TPC/qc/include/TPCQC/Tracks.h b/Detectors/TPC/qc/include/TPCQC/Tracks.h index 899d0878fa401..509855673ee48 100644 --- a/Detectors/TPC/qc/include/TPCQC/Tracks.h +++ b/Detectors/TPC/qc/include/TPCQC/Tracks.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -27,9 +28,6 @@ #include "TH1F.h" #include "TH2F.h" -// o2 includes -#include "DataFormatsTPC/Defs.h" - namespace o2 { namespace tpc @@ -77,6 +75,7 @@ class Tracks mCutMinnCls = nClusterCut; mCutMindEdxTot = dEdxTot; } + // Just for backward compatibility with crrent QC, temporary, will be removed in the next PR /// get 1D histograms std::vector& getHistograms1D() { return mHist1D; } @@ -93,14 +92,14 @@ class Tracks const std::vector& getHistogramRatios1D() const { return mHistRatio1D; } /// get ratios of 1D histograms - std::unordered_map>& getMapHist() { return mMapHist; } - const std::unordered_map>& getMapHist() const { return mMapHist; } + std::unordered_map>& getMapHist() { return mMapHist; } + const std::unordered_map>& getMapHist() const { return mMapHist; } private: float mCutAbsEta = 1.f; // Eta cut int mCutMinnCls = 60; // minimum N clusters float mCutMindEdxTot = 20.f; // dEdxTot min value - std::unordered_map> mMapHist; + std::unordered_map> mMapHist; std::vector mHist1D{}; ///< Initialize vector of 1D histograms std::vector mHist2D{}; ///< Initialize vector of 2D histograms std::vector mHistRatio1D{}; ///< Initialize vector of ratios of 1D histograms diff --git a/Detectors/TPC/qc/src/Helpers.cxx b/Detectors/TPC/qc/src/Helpers.cxx index 3eb2501edc57e..2abf79f93836a 100644 --- a/Detectors/TPC/qc/src/Helpers.cxx +++ b/Detectors/TPC/qc/src/Helpers.cxx @@ -99,6 +99,25 @@ void helpers::setStyleHistogramsInMap(std::unordered_map>>& mapOfvectors) +{ + for (const auto& keyValue : mapOfvectors) { + for (auto& hist : keyValue.second) { + helpers::setStyleHistogram(*hist); + } + } +} + +//______________________________________________________________________________ +void helpers::setStyleHistogramsInMap(std::unordered_map>& mapOfHisto) +{ + for (const auto& keyValue : mapOfHisto) { + helpers::setStyleHistogram(*(keyValue.second)); + } +} + //______________________________________________________________________________ bool helpers::newZSCalib(const o2::tpc::CalDet& refPedestal, const o2::tpc::CalDet& refNoise, const o2::tpc::CalDet& pedestal) { @@ -119,4 +138,4 @@ bool helpers::newZSCalib(const o2::tpc::CalDet& refPedestal, const o2::tp } return false; -} \ No newline at end of file +} diff --git a/Detectors/TPC/qc/src/Tracks.cxx b/Detectors/TPC/qc/src/Tracks.cxx index 49615c3c786a6..878212c1e0429 100644 --- a/Detectors/TPC/qc/src/Tracks.cxx +++ b/Detectors/TPC/qc/src/Tracks.cxx @@ -16,18 +16,20 @@ // root includes #include "TFile.h" -#include "TMathBase.h" // o2 includes #include "DataFormatsTPC/TrackTPC.h" #include "DataFormatsTPC/dEdxInfo.h" +#include "GPUCommonArray.h" +#include "DetectorsBase/Propagator.h" #include "TPCQC/Tracks.h" #include "TPCQC/Helpers.h" ClassImp(o2::tpc::qc::Tracks); using namespace o2::tpc::qc; - +// DCA histograms +const std::vector types{"A_Pos", "A_Neg", "C_Pos", "C_Neg"}; //______________________________________________________________________________ void Tracks::initializeHistograms() { @@ -78,6 +80,11 @@ void Tracks::initializeHistograms() mMapHist["hPhiAsideRatio"] = std::make_unique("hPhiAsideRatio", "Azimuthal angle, A side, ratio neg./pos. ;phi", 360, 0., 2 * M_PI); mMapHist["hPhiCsideRatio"] = std::make_unique("hPhiCsideRatio", "Azimuthal angle, C side, ratio neg./pos. ;phi", 360, 0., 2 * M_PI); mMapHist["hPtRatio"] = std::make_unique("hPtRatio", "Transverse momentum, ratio neg./pos. ;p_T", logPtBinning.size() - 1, logPtBinning.data()); + + // DCA Histograms + for (const auto type : types) { + mMapHist[fmt::format("hDCAr_{}", type).data()] = std::make_unique(fmt::format("hDCAr_{}", type).data(), fmt::format("DCAr {};phi;DCAr (cm)", type).data(), 360, 0, o2::math_utils::twoPid(), 250, -10., 10.); + } } //______________________________________________________________________________ void Tracks::resetHistograms() @@ -100,7 +107,7 @@ bool Tracks::processTrack(const o2::tpc::TrackTPC& track) const auto hasASideOnly = track.hasASideClustersOnly(); const auto hasCSideOnly = track.hasCSideClustersOnly(); - double absEta = TMath::Abs(eta); + const auto absEta = std::abs(eta); // ===| histogram filling before cuts |=== mMapHist["hNClustersBeforeCuts"]->Fill(nCls); @@ -117,6 +124,31 @@ bool Tracks::processTrack(const o2::tpc::TrackTPC& track) mMapHist["hNClustersAfterCuts"]->Fill(nCls); mMapHist["hEta"]->Fill(eta); + //---| propagate to 0,0,0 |--- + // + // propagator instance must be configured before (LUT, MagField) + auto propagator = o2::base::Propagator::Instance(true); + const int type = (track.getQ2Pt() < 0) + 2 * track.hasCSideClustersOnly(); + auto dcaHist = mMapHist[fmt::format("hDCAr_{}", types[type]).data()].get(); + + if (propagator->getMatLUT() && propagator->hasMagFieldSet()) { + // ---| fill DCA histos |--- + o2::gpu::gpustd::array dca; + const o2::math_utils::Point3D refPoint{0, 0, 0}; + o2::track::TrackPar propTrack(track); + if (propagator->propagateToDCABxByBz(refPoint, propTrack, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &dca)) { + const auto phi = o2::math_utils::to02PiGen(track.getPhi()); + dcaHist->Fill(phi, dca[0]); + } + } else { + static bool reported = false; + if (!reported) { + LOGP(error, "o2::base::Propagator not properly initialized, MatLUT ({}) and / or Field ({}) missing, will not fill DCA histograms", (void*)propagator->getMatLUT(), (void*)propagator->hasMagFieldSet()); + reported = true; + } + dcaHist->SetTitle(fmt::format("DCAr {} o2::base::Propagator not properly initialized", types[type]).data()); + } + if (hasASideOnly == 1) { mMapHist["hPhiAside"]->Fill(phi); } else if (hasCSideOnly == 1) { diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index 6242b50eab449..ab49d0d49d79b 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -40,6 +40,26 @@ namespace tpc namespace detail { +struct TriggerInfo { + uint32_t firstOrbit = -1; + std::vector deltaOrbit; + std::vector deltaBC; + std::vector triggerType; + void clear() + { + firstOrbit = -1; + deltaOrbit.clear(); + deltaBC.clear(); + triggerType.clear(); + } + void resize(size_t s) + { + deltaOrbit.resize(s); + deltaBC.resize(s); + triggerType.resize(s); + } +}; + template struct combinedType { using type = std::conditional_t<(A + B > 16), uint32_t, std::conditional_t<(A + B > 8), uint16_t, uint8_t>>; @@ -107,10 +127,10 @@ class CTFCoder : public o2::ctf::CTFCoderBase /// entropy-encode compressed clusters to flat buffer template - o2::ctf::CTFIOSize encode(VEC& buff, const CompressedClusters& ccl, const CompressedClusters& cclFiltered, std::vector* rejectHits = nullptr, std::vector* rejectTracks = nullptr, std::vector* rejectTrackHits = nullptr, std::vector* rejectTrackHitsReduced = nullptr); + o2::ctf::CTFIOSize encode(VEC& buff, const CompressedClusters& ccl, const CompressedClusters& cclFiltered, const detail::TriggerInfo& trigComp, std::vector* rejectHits = nullptr, std::vector* rejectTracks = nullptr, std::vector* rejectTrackHits = nullptr, std::vector* rejectTrackHitsReduced = nullptr); - template - o2::ctf::CTFIOSize decode(const CTF::base& ec, VEC& buff); + template + o2::ctf::CTFIOSize decode(const CTF::base& ec, VEC& buff, TRIGVEC& buffTrig); void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) final; @@ -161,7 +181,7 @@ void CTFCoder::buildCoder(ctf::CTFCoderBase::OpType coderType, const CTF::contai /// entropy-encode clusters to buffer with CTF template -o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const CompressedClusters& ccl, const CompressedClusters& cclFiltered, std::vector* rejectHits, std::vector* rejectTracks, std::vector* rejectTrackHits, std::vector* rejectTrackHitsReduced) +o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const CompressedClusters& ccl, const CompressedClusters& cclFiltered, const detail::TriggerInfo& trigComp, std::vector* rejectHits, std::vector* rejectTracks, std::vector* rejectTrackHits, std::vector* rejectTrackHitsReduced) { using MD = o2::ctf::Metadata::OptStore; using namespace detail; @@ -189,7 +209,10 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const CompressedClusters& ccl, co MD::EENCODE_OR_PACK, // sigmaPadU MD::EENCODE_OR_PACK, // sigmaTimeU MD::EENCODE_OR_PACK, // nTrackClusters - MD::EENCODE_OR_PACK // nSliceRowClusters + MD::EENCODE_OR_PACK, // nSliceRowClusters + MD::EENCODE_OR_PACK, // TrigBCInc + MD::EENCODE_OR_PACK, // TrigOrbitInc + MD::EENCODE_OR_PACK // TrigType }; // book output size with some margin @@ -202,7 +225,7 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const CompressedClusters& ccl, co flags |= CTFHeader::CombinedColumns; } ec->setHeader(CTFHeader{o2::detectors::DetID::TPC, 0, 1, 0, // dummy timestamp, version 1.0 - cclFiltered, flags}); + cclFiltered, flags, trigComp.firstOrbit, (uint16_t)trigComp.triggerType.size()}); assignDictVersion(static_cast(ec->getHeader())); ec->setANSHeader(mANSVersion); @@ -286,6 +309,11 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const CompressedClusters& ccl, co encodeTPC(ccl.nTrackClusters, ccl.nTrackClusters + ccl.nTracks, CTF::BLCnTrackClusters, 0, rejectTracks); encodeTPC(ccl.nSliceRowClusters, ccl.nSliceRowClusters + ccl.nSliceRows, CTF::BLCnSliceRowClusters, 0); + + encodeTPC(trigComp.deltaOrbit.begin(), trigComp.deltaOrbit.end(), CTF::BLCTrigOrbitInc, 0); + encodeTPC(trigComp.deltaBC.begin(), trigComp.deltaBC.end(), CTF::BLCTrigBCInc, 0); + encodeTPC(trigComp.triggerType.begin(), trigComp.triggerType.end(), CTF::BLCTrigType, 0); + CTF::get(buff.data())->print(getPrefix(), mVerbosity); finaliseCTFOutput(buff); iosize.rawIn = iosize.ctfIn; @@ -293,8 +321,8 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const CompressedClusters& ccl, co } /// decode entropy-encoded bloks to TPC CompressedClusters into the externally provided vector (e.g. PMR vector from DPL) -template -o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VEC& buffVec) +template +o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VEC& buffVec, TRIGVEC& buffTrig) { using namespace detail; CompressedClusters cc; @@ -374,6 +402,29 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VEC& buffVec) decodeTPC(cc.nTrackClusters, CTF::BLCnTrackClusters); decodeTPC(cc.nSliceRowClusters, CTF::BLCnSliceRowClusters); + + static TriggerInfo trigInfo; + trigInfo.resize(header.nTriggers); + decodeTPC(trigInfo.deltaOrbit.data(), CTF::BLCTrigOrbitInc); + decodeTPC(trigInfo.deltaBC.data(), CTF::BLCTrigBCInc); + decodeTPC(trigInfo.triggerType.data(), CTF::BLCTrigType); + // convert trigger info to output format + uint32_t prevOrbit = header.firstOrbitTrig; + uint16_t prevBC = 0; + int freeSlot = 0; + for (uint16_t it = 0; it < header.nTriggers; it++) { + if (trigInfo.deltaOrbit[it] || !it) { // start new HBF, deltaBC has absolute BC meaning + freeSlot = 0; + auto& t = buffTrig.emplace_back(); + t.orbit = prevOrbit + trigInfo.deltaOrbit[it]; + t.triggerWord.triggerEntries[freeSlot++] = (trigInfo.deltaBC[it] & 0xFFF) | ((trigInfo.triggerType[it] & 0x7) << 12) | 0x8000; + prevBC = trigInfo.deltaBC[it]; + prevOrbit = t.orbit; + } else { // continue existing HBF + prevBC += trigInfo.deltaBC[it]; + buffTrig.back().triggerWord.triggerEntries[freeSlot++] = (prevBC & 0xFFF) | ((trigInfo.triggerType[it] & 0x7) << 12) | 0x8000; + } + } iosize.rawIn = iosize.ctfIn; return iosize; } diff --git a/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h b/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h index 436fabfc9d620..359dc5d64b72d 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/Digitizer.h @@ -22,8 +22,6 @@ #include -using std::vector; - class TTree; class TH3; diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h index 9c457141e5cee..79400c3d3d214 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/DataContainer3D.h @@ -189,6 +189,7 @@ struct DataContainer3D { /// operator overload DataContainer3D& operator*=(const DataT value); DataContainer3D& operator+=(const DataContainer3D& other); + DataContainer3D& operator*=(const DataContainer3D& other); DataContainer3D& operator-=(const DataContainer3D& other); private: diff --git a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h index 62109f009e436..014be1f2cc50e 100644 --- a/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h +++ b/Detectors/TPC/spacecharge/include/TPCSpaceCharge/SpaceCharge.h @@ -180,6 +180,14 @@ class SpaceCharge /// \param formulaStruct struct containing a method to evaluate the potential void setPotentialBoundaryFromFormula(const AnalyticalFields& formulaStruct); + /// adding the boundary potential from other sc object which same number of vertices! + /// \param other other SC object which boundary potential witll be added + /// \param scaling sclaing factor which used to scale the others boundary potential + void addBoundaryPotential(const SpaceCharge& other, const Side side, const float scaling = 1); + + /// setting the boundary potential to 0 for zzMax + void resetBoundaryPotentialToZeroInRangeZ(float zMin, float zMax, const Side side); + /// step 0: this function fills the potential using an analytical formula /// \param formulaStruct struct containing a method to evaluate the potential void setPotentialFromFormula(const AnalyticalFields& formulaStruct); @@ -292,6 +300,9 @@ class SpaceCharge /// \param scalingFactor scaling factor for the space-charge density void scaleChargeDensitySector(const float scalingFactor, const Sector sector); + /// scaling the space-charge density for given stack + void scaleChargeDensityStack(const float scalingFactor, const Sector sector, const GEMstack stack); + /// add space charge density from other object (this.mDensity = this.mDensity + other.mDensity) /// \param otherSC other space-charge object, which charge will be added to current object void addChargeDensity(const SpaceCharge& otherSC); @@ -320,6 +331,9 @@ class SpaceCharge template > void calcGlobalCorrections(const Fields& formulaStruct, const int type = 3); + /// calculate the global corrections using the electric fields (interface for python) + void calcGlobalCorrectionsEField(const Side side, const int type = 3) { calcGlobalCorrections(getElectricFieldsInterpolator(side), type); } + /// step 5: calculate global distortions by using the electric field or the local distortions (SLOW) /// \param formulaStruct struct containing a method to evaluate the electric field Er, Ez, Ephi or the local distortions /// \param maxIterations maximum steps which are are performed to reach the central electrode (in general this is not necessary, but in case of problems this value aborts the calculation) @@ -372,6 +386,9 @@ class SpaceCharge /// \param phi global phi coordinate DataT getPotentialCyl(const DataT z, const DataT r, const DataT phi, const Side side) const; + /// get the potential for list of given coordinate + std::vector getPotentialCyl(const std::vector& z, const std::vector& r, const std::vector& phi, const Side side) const; + /// get the electric field for given coordinate /// \param z global z coordinate /// \param r global r coordinate @@ -1112,11 +1129,19 @@ class SpaceCharge /// \param deltaPot delta potential which will be set at the copper rod void setDeltaVoltageRotatedClipOFC(const float deltaPot, const Side side, const int sector) { initRodAlignmentVoltages(MisalignmentType::RotatedClip, FCType::OFC, sector, side, deltaPot); } + /// IFC charge up: set a linear rising delta potential from the CE to given z + /// \param deltaPot maximum value of the delta potential in V + /// \param zMaxDeltaPot z position where the maximum delta potential of deltaPot will be set + /// \param type functional form of the delta potential: 0=linear, 1= 1/x, 2=flat, 3=linear falling, 4=flat no z dependence + /// \param zStart usually at 0 to start the rising of the potential at the IFC + void setIFCChargeUpRisingPot(const float deltaPot, const float zMaxDeltaPot, const int type, const float zStart, const float offs, const Side side); + /// IFC charge up: set a linear rising delta potential from the CE to given z position which falls linear down to 0 at the readout /// \param deltaPot maximum value of the delta potential in V - /// \param cutOff set the delta potential to 0 for z>zMaxDeltaPot /// \param zMaxDeltaPot z position where the maximum delta potential of deltaPot will be set - void setIFCChargeUpLinear(const float deltaPot, const float zMaxDeltaPot, const bool cutOff, const Side side); + /// \param type function which is used to set falling potential: 0=linear falling off, 1=1/x falling off, 2=1/x steeply falling, 3=linear with offset + /// \param offs if offs != 0 the potential doesnt fall to 0. E.g. deltaPot=400V and offs=-10V -> Potential falls from 400V at zMaxDeltaPot to -10V at z=250cm + void setIFCChargeUpFallingPot(const float deltaPot, const float zMaxDeltaPot, const int type, const float zEnd, const float offs, const Side side); /// setting the global corrections directly from input function provided in global coordinates /// \param gCorr function returning global corrections for given global coordinate diff --git a/Detectors/TPC/spacecharge/src/DataContainer3D.cxx b/Detectors/TPC/spacecharge/src/DataContainer3D.cxx index c3ca2cbe75b11..c9a39e940873d 100644 --- a/Detectors/TPC/spacecharge/src/DataContainer3D.cxx +++ b/Detectors/TPC/spacecharge/src/DataContainer3D.cxx @@ -247,6 +247,13 @@ DataContainer3D& DataContainer3D::operator-=(const DataContainer3D return *this; } +template +DataContainer3D& DataContainer3D::operator*=(const DataContainer3D& other) +{ + std::transform(mData.begin(), mData.end(), other.mData.begin(), mData.begin(), std::multiplies<>()); + return *this; +} + template size_t DataContainer3D::getIndexZ(size_t index, const int nz, const int nr, const int nphi) { diff --git a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx index 96e86853deae5..3ed00345159bc 100644 --- a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx +++ b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx @@ -1290,7 +1290,8 @@ void SpaceCharge::calcGlobalDistortions(const Fields& formulaStruct, cons } const DataT z0Tmp = z0 + dzDist + iter * stepSize; // starting z position - if (getSide(z0Tmp) != side) { + // do not do check for first iteration + if ((getSide(z0Tmp) != side) && iter) { LOGP(error, "Aborting calculation of distortions for iZ: {}, iR: {}, iPhi: {} due to change in the sides!", iZ, iR, iPhi); break; } @@ -1561,6 +1562,18 @@ DataT SpaceCharge::getPotentialCyl(const DataT z, const DataT r, const Da return mInterpolatorPotential[side](z, r, phi); } +template +std::vector SpaceCharge::getPotentialCyl(const std::vector& z, const std::vector& r, const std::vector& phi, const Side side) const +{ + const auto nPoints = z.size(); + std::vector potential(nPoints); +#pragma omp parallel for num_threads(sNThreads) + for (size_t i = 0; i < nPoints; ++i) { + potential[i] = getPotentialCyl(z[i], r[i], phi[i], side); + } + return potential; +} + template void SpaceCharge::getElectricFieldsCyl(const DataT z, const DataT r, const DataT phi, const Side side, DataT& eZ, DataT& eR, DataT& ePhi) const { @@ -1607,7 +1620,7 @@ void SpaceCharge::getCorrectionsCyl(const std::vector& z, const st corrRPhi.resize(nPoints); #pragma omp parallel for num_threads(sNThreads) for (size_t i = 0; i < nPoints; ++i) { - getLocalCorrectionsCyl(z[i], r[i], phi[i], side, corrZ[i], corrR[i], corrRPhi[i]); + getCorrectionsCyl(z[i], r[i], phi[i], side, corrZ[i], corrR[i], corrRPhi[i]); } } @@ -3296,19 +3309,156 @@ void SpaceCharge::initRodAlignmentVoltages(const MisalignmentType misalig } template -void SpaceCharge::setIFCChargeUpLinear(const float deltaPot, const float zMaxDeltaPot, const bool cutOff, const Side side) +void SpaceCharge::addBoundaryPotential(const SpaceCharge& other, const Side side, const float scaling) +{ + if (other.mPotential[side].getData().empty()) { + LOGP(info, "Other space-charge object is empty!"); + return; + } + + if ((mParamGrid.NRVertices != other.mParamGrid.NRVertices) || (mParamGrid.NZVertices != other.mParamGrid.NZVertices) || (mParamGrid.NPhiVertices != other.mParamGrid.NPhiVertices)) { + LOGP(info, "Different number of vertices found in input file. Initializing new space charge object with nR {} nZ {} nPhi {} vertices", other.mParamGrid.NRVertices, other.mParamGrid.NZVertices, other.mParamGrid.NPhiVertices); + SpaceCharge scTmp(mBField.getBField(), other.mParamGrid.NZVertices, other.mParamGrid.NRVertices, other.mParamGrid.NPhiVertices, false); + scTmp.mC0 = mC0; + scTmp.mC1 = mC1; + scTmp.mC2 = mC2; + *this = std::move(scTmp); + } + + initContainer(mPotential[side], true); + + for (size_t iPhi = 0; iPhi < mParamGrid.NPhiVertices; ++iPhi) { + for (size_t iZ = 1; iZ < mParamGrid.NZVertices; ++iZ) { + const size_t iRFirst = 0; + mPotential[side](iZ, iRFirst, iPhi) += scaling * other.mPotential[side](iZ, iRFirst, iPhi); + + const size_t iRLast = mParamGrid.NRVertices - 1; + mPotential[side](iZ, iRLast, iPhi) += scaling * other.mPotential[side](iZ, iRLast, iPhi); + } + } + + for (size_t iPhi = 0; iPhi < mParamGrid.NPhiVertices; ++iPhi) { + for (size_t iR = 0; iR < mParamGrid.NRVertices; ++iR) { + const size_t iZFirst = 0; + mPotential[side](iZFirst, iR, iPhi) += scaling * other.mPotential[side](iZFirst, iR, iPhi); + + const size_t iZLast = mParamGrid.NZVertices - 1; + mPotential[side](iZLast, iR, iPhi) += scaling * other.mPotential[side](iZLast, iR, iPhi); + } + } +} + +template +void SpaceCharge::resetBoundaryPotentialToZeroInRangeZ(float zMin, float zMax, const Side side) { - std::function chargeUpIFCLinear = [cutOff, zMax = getZMax(Side::A), deltaPot, zMaxDeltaPot](const DataT z) { + const float zMaxAbs = std::abs(zMax); + for (size_t iPhi = 0; iPhi < mParamGrid.NPhiVertices; ++iPhi) { + for (size_t iZ = 1; iZ < mParamGrid.NZVertices; ++iZ) { + const DataT z = std::abs(getZVertex(iZ, side)); + if ((z < zMin) || (z > zMax)) { + const size_t iRFirst = 0; + mPotential[side](iZ, iRFirst, iPhi) = 0; + + const size_t iRLast = mParamGrid.NRVertices - 1; + mPotential[side](iZ, iRLast, iPhi) = 0; + } + } + } + + for (size_t iPhi = 0; iPhi < mParamGrid.NPhiVertices; ++iPhi) { + for (size_t iR = 0; iR < mParamGrid.NRVertices; ++iR) { + const size_t iZFirst = 0; + const float zFirst = std::abs(getZVertex(iZFirst, side)); + if ((zFirst < zMin) || (zFirst > zMax)) { + mPotential[side](iZFirst, iR, iPhi) = 0; + } + + const size_t iZLast = mParamGrid.NZVertices - 1; + const float zLast = std::abs(getZVertex(iZLast, side)); + if ((zLast < zMin) || (zLast > zMax)) { + mPotential[side](iZLast, iR, iPhi) = 0; + } + } + } +} + +template +void SpaceCharge::setIFCChargeUpRisingPot(const float deltaPot, const float zMaxDeltaPot, const int type, const float zStart, const float offs, const Side side) +{ + std::function chargeUpIFCLinear = [zStart, type, offs, deltaPot, zMaxDeltaPot](const DataT z) { const float absZ = std::abs(z); const float absZMaxDeltaPot = std::abs(zMaxDeltaPot); - if (absZ <= absZMaxDeltaPot) { - return static_cast(deltaPot / absZMaxDeltaPot * absZ); + if ((absZ <= absZMaxDeltaPot) && (absZ >= zStart)) { + // 1/x + if (type == 1) { + const float offsZ = 1; + const float zMaxDeltaPotTmp = zMaxDeltaPot - zStart + offsZ; + const float p1 = deltaPot / (1 / offsZ - 1 / zMaxDeltaPotTmp); + const float p2 = -p1 / zMaxDeltaPotTmp; + const float absZShifted = zMaxDeltaPotTmp - (absZ - zStart); + DataT pot = p2 + p1 / absZShifted; + return pot; + } else if (type == 0 || type == 4) { + // linearly rising potential + return static_cast(deltaPot / (absZMaxDeltaPot - zStart) * (absZ - zStart) + offs); + } else if (type == 2) { + // flat + return DataT(deltaPot); + } else if (type == 3) { + // linear falling + return static_cast(-deltaPot / (absZMaxDeltaPot - zStart) * (absZ - zStart) + deltaPot); + } else { + return DataT(0); + } + } else if (type == 4) { + // flat no z dependence + return DataT(offs); } else { - if (cutOff) { + return DataT(0); + } + }; + setPotentialBoundaryInnerRadius(chargeUpIFCLinear, side); +} + +template +void SpaceCharge::setIFCChargeUpFallingPot(const float deltaPot, const float zMaxDeltaPot, const int type, const float zEnd, const float offs, const Side side) +{ + std::function chargeUpIFCLinear = [zEnd, type, offs, zMax = getZMax(Side::A), deltaPot, zMaxDeltaPot](const DataT z) { + const float absZ = std::abs(z); + const float absZMaxDeltaPot = std::abs(zMaxDeltaPot); + + bool check = (absZ >= absZMaxDeltaPot); + if (type == 0 || type == 3) { + check = (absZ >= absZMaxDeltaPot); + } + + if (check && (absZ <= zEnd)) { + // 1/x dependency + if (type == 1) { + const float p1 = (deltaPot - offs) / (1 / zMaxDeltaPot - 1 / zEnd); + const float p2 = offs - p1 / zEnd; + DataT pot = p2 + p1 / absZ; + return pot; + } else if (type == 2) { + // 1/x dependency steep fall off! + const float offsZ = 1; + const float zEndTmp = zEnd - zMaxDeltaPot + offsZ; + const float p1 = deltaPot / (1 / offsZ - 1 / zEndTmp); + const float p2 = -p1 / zEndTmp; + const float absZShifted = absZ - zMaxDeltaPot; + DataT pot = p2 + p1 / absZShifted; + return pot; + } else if (type == 0 || type == 3) { + // linearly falling potential + const float zPos = absZ - zEnd; + return static_cast(deltaPot / (absZMaxDeltaPot - zEnd) * zPos + offs); + } else { return DataT(0); } - const float zPos = absZ - zMax; - return static_cast(deltaPot / (absZMaxDeltaPot - zMax) * zPos); + } else if (type == 3) { + return DataT(offs); + } else { + return DataT(0); } }; setPotentialBoundaryInnerRadius(chargeUpIFCLinear, side); @@ -3519,6 +3669,31 @@ void SpaceCharge::scaleChargeDensitySector(const float scalingFactor, con } } +template +void SpaceCharge::scaleChargeDensityStack(const float scalingFactor, const Sector sector, const GEMstack stack) +{ + const Side side = sector.side(); + initContainer(mDensity[side], true); + const int verticesPerSector = mParamGrid.NPhiVertices / SECTORSPERSIDE; + const int sectorInSide = sector % SECTORSPERSIDE; + const int iPhiFirst = sectorInSide * verticesPerSector; + const int iPhiLast = iPhiFirst + verticesPerSector; + for (unsigned int iR = 0; iR < mParamGrid.NRVertices; ++iR) { + const DataT radius = getRVertex(iR, side); + for (unsigned int iPhi = iPhiFirst; iPhi < iPhiLast; ++iPhi) { + const DataT phi = getPhiVertex(iR, side); + const GlobalPosition3D pos(getXFromPolar(radius, phi), getYFromPolar(radius, phi), ((side == Side::A) ? 10 : -10)); + const auto& mapper = o2::tpc::Mapper::instance(); + const o2::tpc::DigitPos digiPadPos = mapper.findDigitPosFromGlobalPosition(pos); + if (digiPadPos.isValid() && digiPadPos.getCRU().gemStack() == stack) { + for (unsigned int iZ = 0; iZ < mParamGrid.NZVertices; ++iZ) { + mDensity[side](iZ, iR, iPhi) *= scalingFactor; + } + } + } + } +} + using DataTD = double; template class o2::tpc::SpaceCharge; diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index 7d504630bc476..a12ccc83b1cc2 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -23,6 +23,7 @@ o2_add_library(TPCWorkflow src/CalDetMergerPublisherSpec.cxx src/KryptonClustererSpec.cxx src/KryptonRawFilterSpec.cxx + src/OccupancyFilterSpec.cxx src/SACProcessorSpec.cxx src/IDCToVectorSpec.cxx src/CalibdEdxSpec.cxx @@ -38,7 +39,12 @@ o2_add_library(TPCWorkflow src/TPCIntegrateClusterReaderSpec.cxx src/TPCIntegrateClusterSpec.cxx src/TPCIntegrateClusterWriterSpec.cxx + src/TPCTriggerWriterSpec.cxx src/TPCMergeIntegrateClusterSpec.cxx + src/TPCTimeSeriesSpec.cxx + src/TPCTimeSeriesWriterSpec.cxx + src/TPCTimeSeriesReaderSpec.cxx + src/TPCMergeTimeSeriesSpec.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC O2::DPLUtils O2::TPCReconstruction @@ -170,6 +176,11 @@ o2_add_executable(krypton-raw-filter SOURCES src/tpc-krypton-raw-filter.cxx PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) +o2_add_executable(occupancy-filter + COMPONENT_NAME tpc + SOURCES src/tpc-occupancy-filter.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + o2_add_executable(idc-to-vector COMPONENT_NAME tpc SOURCES src/tpc-idc-to-vector.cxx @@ -220,6 +231,21 @@ o2_add_executable(merge-integrate-cluster-workflow COMPONENT_NAME tpc PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) +o2_add_executable(time-series-workflow + COMPONENT_NAME tpc + SOURCES src/tpc-time-series.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +o2_add_executable(time-series-reader-workflow + SOURCES src/time-series-reader.cxx + COMPONENT_NAME tpc + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +o2_add_executable(merge-time-series-workflow + SOURCES src/time-series-merge-integrator.cxx + COMPONENT_NAME tpc + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + o2_add_test(workflow COMPONENT_NAME tpc LABELS tpc workflow diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h index 5ba1590dec7e2..3222d24667760 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibLaserTracksSpec.h @@ -15,6 +15,7 @@ /// @file CalibLaserTracksSpec.h /// @brief Device to run tpc laser track calibration +#include "TFile.h" #include "TPCCalibration/CalibLaserTracks.h" #include "DetectorsCalibration/Utils.h" #include "CommonUtils/MemFileHelper.h" @@ -39,7 +40,8 @@ class CalibLaserTracksDevice : public o2::framework::Task public: void init(o2::framework::InitContext& ic) final { - mCalib.setWriteDebugTree(ic.options().get("write-debug")); + mWriteDebug = ic.options().get("write-debug"); + mCalib.setWriteDebugTree(mWriteDebug); mMinNumberTFs = ic.options().get("min-tfs"); mOnlyPublishOnEOS = ic.options().get("only-publish-on-eos"); mNormalize = !ic.options().get("ignore-normalization"); @@ -62,6 +64,11 @@ class CalibLaserTracksDevice : public o2::framework::Task return; } mTPCVDriftHelper.extractCCDBInputs(pc); + if (mTPCVDriftHelper.isUpdated()) { + mTPCVDriftHelper.acknowledgeUpdate(); + mCalib.setVDriftRef(mTPCVDriftHelper.getVDriftObject().getVDrift()); + LOGP(info, "Updated reference drift velocity to: {}", mTPCVDriftHelper.getVDriftObject().getVDrift()); + } const auto startTime = dph->startTime; const auto endTime = dph->startTime + dph->duration; mRunNumber = processing_helpers::getRunNumber(pc); @@ -90,22 +97,19 @@ class CalibLaserTracksDevice : public o2::framework::Task void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final { if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { - if (mTPCVDriftHelper.isUpdated()) { - mTPCVDriftHelper.acknowledgeUpdate(); - mCalib.setVDriftRef(mTPCVDriftHelper.getVDriftObject().getVDrift()); - } return; } } private: - CalibLaserTracks mCalib; ///< laser track calibration component + CalibLaserTracks mCalib; ///< laser track calibration component o2::tpc::VDriftHelper mTPCVDriftHelper{}; uint64_t mRunNumber{0}; ///< processed run number int mMinNumberTFs{100}; ///< minimum number of TFs required for good calibration bool mPublished{false}; ///< if calibration was already published bool mOnlyPublishOnEOS{false}; ///< if to only publish the calibration on EOS, not during running bool mNormalize{true}; ///< normalize reference to have mean correction = 1 + bool mWriteDebug{false}; ///< Write debug output //________________________________________________________________ void sendOutput(DataAllocator& output) @@ -120,7 +124,7 @@ class CalibLaserTracksDevice : public o2::framework::Task if (mNormalize) { ltrCalib.normalize(0.); - LOGP(info, "After normalization: correction factors: {} / {} for A- / C-Side, reference: {}", ltrCalib.dvCorrectionA, ltrCalib.dvCorrectionC, ltrCalib.refVDrift); + LOGP(info, "After normalization: correction factors: {} / {} for A- / C-Side, reference: {}, vdrift correction: {}", ltrCalib.dvCorrectionA, ltrCalib.dvCorrectionC, ltrCalib.refVDrift, ltrCalib.getDriftVCorrection()); } if (ltrCalib.getDriftVCorrection() == 0) { LOG(error) << "Extracted drift correction is 0, something is wrong, will not upload the object"; @@ -148,6 +152,11 @@ class CalibLaserTracksDevice : public o2::framework::Task output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "TPC_CalibLtr", 0}, w); mPublished = true; + + if (mWriteDebug) { + TFile f("LaserTracks.snapshot.root", "recreate"); + f.WriteObject(<rCalib, "ccdb_object"); + } } }; diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h index 36a2cb0cf6508..783320330d10b 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibProcessingHelper.h @@ -28,7 +28,7 @@ class RawReaderCRU; namespace calib_processing_helper { -uint64_t processRawData(o2::framework::InputRecord& inputs, std::unique_ptr& reader, bool useOldSubspec = false, const std::vector& sectors = {}, size_t* nerrors = nullptr, uint32_t syncOffsetReference = 144, uint32_t decoderType = 1, bool useTrigger = true); +uint64_t processRawData(o2::framework::InputRecord& inputs, std::unique_ptr& reader, bool useOldSubspec = false, const std::vector& sectors = {}, size_t* nerrors = nullptr, uint32_t syncOffsetReference = 144, uint32_t decoderType = 1, bool useTrigger = true, bool returnOnNoTrigger = false); /// absolute BC relative to TF start (firstOrbit) std::vector getFilter(o2::framework::InputRecord& inputs); diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibdEdxSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibdEdxSpec.h index 290bfbc558f8f..bafad0ef5a20c 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CalibdEdxSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibdEdxSpec.h @@ -17,6 +17,7 @@ #define O2_TPC_TPCCALIBDEDXSPEC_H_ #include "Framework/DataProcessorSpec.h" +#include "DetectorsBase/Propagator.h" using namespace o2::framework; @@ -24,7 +25,7 @@ namespace o2::tpc { /// create a processor spec -o2::framework::DataProcessorSpec getCalibdEdxSpec(); +o2::framework::DataProcessorSpec getCalibdEdxSpec(const o2::base::Propagator::MatCorrType matType); } // namespace o2::tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CalibratordEdxSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CalibratordEdxSpec.h index dd8ebd7d3e62d..b34c877879269 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/CalibratordEdxSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CalibratordEdxSpec.h @@ -17,6 +17,7 @@ #define O2_TPC_TPCCALIBRATORDEDXSPEC_H_ #include "Framework/DataProcessorSpec.h" +#include "DetectorsBase/Propagator.h" using namespace o2::framework; @@ -24,7 +25,7 @@ namespace o2::tpc { /// create a processor spec -o2::framework::DataProcessorSpec getCalibratordEdxSpec(); +o2::framework::DataProcessorSpec getCalibratordEdxSpec(const o2::base::Propagator::MatCorrType matType); } // namespace o2::tpc diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/OccupancyFilterSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/OccupancyFilterSpec.h new file mode 100644 index 0000000000000..7fcf67d0f8f71 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/OccupancyFilterSpec.h @@ -0,0 +1,29 @@ +// 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 OccupancyFilterSpec.h +/// @author Jens Wiechula +/// @since 2021-11-19 +/// @brief Processor spec for filtering krypton raw data + +#ifndef TPC_OccupancyFilterSpec_H_ +#define TPC_OccupancyFilterSpec_H_ + +#include "Framework/DataProcessorSpec.h" + +namespace o2::tpc +{ + +o2::framework::DataProcessorSpec getOccupancyFilterSpec(); + +} // namespace o2::tpc + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h index f9a945cef7434..6d4275398ab5d 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/RecoWorkflow.h @@ -65,6 +65,7 @@ enum struct OutputType { Digits, ZSRaw, QA, NoSharedClusterMap, + TPCTriggers }; using CompletionPolicyData = std::vector; diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h index 66dffa8a8347c..f87b5129b3d33 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h @@ -85,8 +85,8 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task mPadGainTracks.setdEdxMax(maxdEdx); mPadGainTracks.doNotNomalize(donotnormalize); - const auto propagateTrack = ic.options().get("propagateTrack"); - mPadGainTracks.setPropagateTrack(propagateTrack); + const auto propagateTrack = ic.options().get("do-not-propagateTrack"); + mPadGainTracks.setPropagateTrack(!propagateTrack); const auto dedxRegionType = ic.options().get("dedxRegionType"); mPadGainTracks.setdEdxRegion(static_cast(dedxRegionType)); @@ -271,14 +271,14 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c {"etaMax", VariantType::Float, 1.f, {"maximum eta of the tracks which are used for the pad-by-pad gain map"}}, {"disable-log-transform", VariantType::Bool, false, {"Disable the transformation of q/dedx -> log(1 + q/dedx)"}}, {"do-not-normalize", VariantType::Bool, false, {"Do not normalize the cluster charge to the dE/dx"}}, - {"mindEdx", VariantType::Float, 0.f, {"Minimum accepted dE/dx value"}}, - {"maxdEdx", VariantType::Float, -1.f, {"Maximum accepted dE/dx value (-1=accept all dE/dx)"}}, + {"mindEdx", VariantType::Float, 10.f, {"Minimum accepted dE/dx value"}}, + {"maxdEdx", VariantType::Float, 500.f, {"Maximum accepted dE/dx value (-1=accept all dE/dx)"}}, {"minClusters", VariantType::Int, 50, {"minimum number of clusters of tracks which are used for the pad-by-pad gain map"}}, {"gainMapFile", VariantType::String, "", {"file to reference gain map, which will be used for correcting the cluster charge"}}, {"dedxRegionType", VariantType::Int, 2, {"using the dE/dx per chamber (0), stack (1) or per sector (2)"}}, {"dedxType", VariantType::Int, 0, {"recalculating the dE/dx (0), using it from tracking (1)"}}, {"chargeType", VariantType::Int, 0, {"Using qMax (0) or qTot (1) for the dE/dx and the pad-by-pad histograms"}}, - {"propagateTrack", VariantType::Bool, false, {"Propagating the track instead of performing a refit for obtaining track parameters."}}, + {"do-not-propagateTrack", VariantType::Bool, false, {"Performing a refit for obtaining track parameters instead of propagating."}}, {"useEveryNthTF", VariantType::Int, 10, {"Using only a fraction of the data: 1: Use every TF, 10: Use only every tenth TF."}}, {"maxTracksPerTF", VariantType::Int, 10000, {"Maximum number of processed tracks per TF (-1 for processing all tracks)"}}, }; @@ -288,7 +288,7 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c false, // GRPECS=true false, // GRPLHCIF true, // GRPMagField - false, // askMatLUT + true, // askMatLUT o2::base::GRPGeomRequest::None, // geometry inputs); diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeIDCSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeIDCSpec.h index 7f13680b72eb7..6e589cd6c4e8b 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeIDCSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeIDCSpec.h @@ -93,8 +93,13 @@ class TPCDistributeIDCSpec : public o2::framework::Task LOGP(info, "Updating ORBITRESET"); std::fill(mSendCCDBOutputOrbitReset.begin(), mSendCCDBOutputOrbitReset.end(), true); } else if (matcher == ConcreteDataMatcher("GLO", "GRPECS", 0)) { - LOGP(info, "Updating GRPECS"); - std::fill(mSendCCDBOutputGRPECS.begin(), mSendCCDBOutputGRPECS.end(), true); + // check if received object is valid + if (o2::base::GRPGeomHelper::instance().getGRPECS()->getRun() != 0) { + LOGP(info, "Updating GRPECS"); + std::fill(mSendCCDBOutputGRPECS.begin(), mSendCCDBOutputGRPECS.end(), true); + } else { + LOGP(info, "Detected default GRPECS object"); + } } } diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCMergeTimeSeriesSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCMergeTimeSeriesSpec.h new file mode 100644 index 0000000000000..1a3b1a3554ddb --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCMergeTimeSeriesSpec.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_TPC_TPCMERGETIMESERIESSPEC_SPEC +#define O2_TPC_TPCMERGETIMESERIESSPEC_SPEC + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace tpc +{ + +o2::framework::DataProcessorSpec getTPCMergeTimeSeriesSpec(); + +} // end namespace tpc +} // end namespace o2 + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesReaderSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesReaderSpec.h new file mode 100644 index 0000000000000..f3ac7aa18fdfb --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesReaderSpec.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_TPC_TPCTIMESERIESREADERSPEC_SPEC +#define O2_TPC_TPCTIMESERIESREADERSPEC_SPEC + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace tpc +{ + +o2::framework::DataProcessorSpec getTPCTimeSeriesReaderSpec(); + +} // end namespace tpc +} // end namespace o2 + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesSpec.h new file mode 100644 index 0000000000000..c2c21c7f40649 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesSpec.h @@ -0,0 +1,30 @@ +// 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_TPC_TPCTIMESERIESSPEC_SPEC +#define O2_TPC_TPCTIMESERIESSPEC_SPEC + +#include "Framework/DataProcessorSpec.h" +#include "DetectorsBase/Propagator.h" + +namespace o2 +{ +namespace tpc +{ +static constexpr header::DataDescription getDataDescriptionTimeSeries() { return header::DataDescription{"TIMESERIES"}; } +static constexpr header::DataDescription getDataDescriptionTPCTimeSeriesTFId() { return header::DataDescription{"ITPCTSTFID"}; } + +o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, const o2::base::Propagator::MatCorrType matType); + +} // end namespace tpc +} // end namespace o2 + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesWriterSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesWriterSpec.h new file mode 100644 index 0000000000000..ab56884b7a6e6 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTimeSeriesWriterSpec.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_TPC_TPCTIMESERIESWRITERSPEC_SPEC +#define O2_TPC_TPCTIMESERIESWRITERSPEC_SPEC + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace tpc +{ + +o2::framework::DataProcessorSpec getTPCTimeSeriesWriterSpec(); + +} // end namespace tpc +} // end namespace o2 + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCTriggerWriterSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTriggerWriterSpec.h new file mode 100644 index 0000000000000..b7858fe688eb9 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCTriggerWriterSpec.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_TPC_TPCTRIGGERWRITER_SPEC +#define O2_TPC_TPCTRIGGERWRITER_SPEC + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace tpc +{ + +o2::framework::DataProcessorSpec getTPCTriggerWriterSpec(); + +} // end namespace tpc +} // end namespace o2 + +#endif diff --git a/Detectors/TPC/workflow/readers/CMakeLists.txt b/Detectors/TPC/workflow/readers/CMakeLists.txt index 74eca5dff0139..80e967c287404 100644 --- a/Detectors/TPC/workflow/readers/CMakeLists.txt +++ b/Detectors/TPC/workflow/readers/CMakeLists.txt @@ -13,6 +13,7 @@ o2_add_library(TPCReaderWorkflow SOURCES src/ClusterReaderSpec.cxx src/PublisherSpec.cxx src/TrackReaderSpec.cxx + src/TriggerReaderSpec.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC diff --git a/Detectors/TPC/workflow/readers/include/TPCReaderWorkflow/TPCSectorCompletionPolicy.h b/Detectors/TPC/workflow/readers/include/TPCReaderWorkflow/TPCSectorCompletionPolicy.h index bb321eaa7bbd5..33abded35624a 100644 --- a/Detectors/TPC/workflow/readers/include/TPCReaderWorkflow/TPCSectorCompletionPolicy.h +++ b/Detectors/TPC/workflow/readers/include/TPCReaderWorkflow/TPCSectorCompletionPolicy.h @@ -20,6 +20,7 @@ #include "Framework/InputSpec.h" #include "Framework/InputSpan.h" #include "Framework/DeviceSpec.h" +#include "Framework/DataProcessingHeader.h" #include "DataFormatsTPC/TPCSectorHeader.h" #include "Headers/DataHeaderHelpers.h" #include "TPCBase/Sector.h" @@ -29,6 +30,7 @@ #include #include #include +#include namespace o2 { @@ -89,7 +91,7 @@ class TPCSectorCompletionPolicy return std::regex_match(device.name.begin(), device.name.end(), std::regex(expression.c_str())); }; - auto callback = [bRequireAll = mRequireAll, inputMatchers = mInputMatchers, externalInputMatchers = mExternalInputMatchers, pTpcSectorMask = mTpcSectorMask](framework::InputSpan const& inputs) -> framework::CompletionPolicy::CompletionOp { + auto callback = [bRequireAll = mRequireAll, inputMatchers = mInputMatchers, externalInputMatchers = mExternalInputMatchers, pTpcSectorMask = mTpcSectorMask, orderCheck = mOrderCheck](framework::InputSpan const& inputs) -> framework::CompletionPolicy::CompletionOp { unsigned long tpcSectorMask = pTpcSectorMask ? *pTpcSectorMask : 0xFFFFFFFFF; std::bitset validSectors = 0; bool haveMatchedInput = false; @@ -165,6 +167,7 @@ class TPCSectorCompletionPolicy } } + o2::framework::CompletionPolicy::CompletionOp retVal = framework::CompletionPolicy::CompletionOp::Wait; // If the flag Config::RequireAll is set in the constructor arguments we require // data from all inputs in addition to the sector matching condition // To be fully correct we would need to require data from all inputs not going @@ -175,7 +178,7 @@ class TPCSectorCompletionPolicy (!bRequireAll || nActiveInputRoutes == inputs.size())) { // we can process if there is input for all sectors, the required sectors are // transported as part of the sector header - return framework::CompletionPolicy::CompletionOp::Consume; + retVal = framework::CompletionPolicy::CompletionOp::Consume; } else if (activeSectors == 0 && nActiveInputRoutes == inputs.size()) { // no sector header is transmitted, this is the case for e.g. the ZS raw data // we simply require input on all routes, this is also the default of DPL DataRelayer @@ -184,13 +187,25 @@ class TPCSectorCompletionPolicy // Currently, the workflow has multiple O2 messages per input route, but they all come in // a single multipart message. So it works fine, and we disable the warning below, but there // is a potential problem. Need to fix this on the level of the workflow. - //if (nMaxPartsPerRoute > 1) { + // if (nMaxPartsPerRoute ) { // LOG(warning) << "No sector information is provided with the data, data set is complete with data on all input routes. But there are multiple parts on at least one route and this policy might not be complete, no check possible if other parts on some routes are still missing. It is adviced to add a custom policy."; //} - return framework::CompletionPolicy::CompletionOp::Consume; + retVal = framework::CompletionPolicy::CompletionOp::Consume; } - return framework::CompletionPolicy::CompletionOp::Wait; + if (retVal != framework::CompletionPolicy::CompletionOp::Wait && orderCheck && *orderCheck && **orderCheck) { + for (auto& input : inputs) { + auto* dph = framework::DataRefUtils::getHeader(input); + if (!dph) { + continue; + } + if (!(**orderCheck)(dph->startTime)) { + retVal = framework::CompletionPolicy::CompletionOp::Retry; + } + break; + } + } + return retVal; }; return framework::CompletionPolicy{"TPCSectorCompletionPolicy", matcher, callback}; } @@ -211,6 +226,8 @@ class TPCSectorCompletionPolicy } } else if constexpr (std::is_same_v*>) { mExternalInputMatchers = arg; + } else if constexpr (std::is_same_v**>) { + mOrderCheck = arg; } else if constexpr (std::is_same_v || std::is_same_v) { mTpcSectorMask = arg; } else { @@ -223,6 +240,7 @@ class TPCSectorCompletionPolicy std::string mProcessorName; std::vector mInputMatchers; + std::function** mOrderCheck = nullptr; // The external input matchers behave as the internal ones with the following differences: // - They are controlled externally and the external entity can modify them, e.g. after parsing command line arguments. // - They are all matched independently, it is not sufficient that one of them is present for all sectors diff --git a/Detectors/TPC/workflow/readers/include/TPCReaderWorkflow/TriggerReaderSpec.h b/Detectors/TPC/workflow/readers/include/TPCReaderWorkflow/TriggerReaderSpec.h new file mode 100644 index 0000000000000..a7f6f467c7d09 --- /dev/null +++ b/Detectors/TPC/workflow/readers/include/TPCReaderWorkflow/TriggerReaderSpec.h @@ -0,0 +1,59 @@ +// 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 TriggerReaderSpec.h + +#ifndef O2_TPC_TRIGGERREADER +#define O2_TPC_TRIGGERREADER + +#include "TFile.h" +#include "TTree.h" + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsTPC/ZeroSuppression.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tpc +{ +///< DPL device to read and send the TPC tracks (+MC) info + +class TriggerReader : public Task +{ + public: + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + + private: + void connectTree(const std::string& filename); + + std::vector* mTrig = nullptr; + + std::unique_ptr mFile; + std::unique_ptr mTree; + + std::string mInputFileName = "tpctriggers.root"; + std::string mTriggerTreeName = "triggers"; + std::string mTriggerBranchName = "Triggers"; +}; + +/// create a processor spec +/// read TPC track data from a root file +framework::DataProcessorSpec getTPCTriggerReaderSpec(); + +} // namespace tpc +} // namespace o2 + +#endif /* O2_TPC_TRIGGERREADER */ diff --git a/Detectors/TPC/workflow/readers/src/TriggerReaderSpec.cxx b/Detectors/TPC/workflow/readers/src/TriggerReaderSpec.cxx new file mode 100644 index 0000000000000..2c363f4f7ed93 --- /dev/null +++ b/Detectors/TPC/workflow/readers/src/TriggerReaderSpec.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. + +/// @file TriggerReaderSpec.cxx + +#include +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "TPCReaderWorkflow/TriggerReaderSpec.h" +#include "CommonUtils/NameConf.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tpc +{ + +void TriggerReader::init(InitContext& ic) +{ + mInputFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), + ic.options().get("infile")); + connectTree(mInputFileName); +} + +void TriggerReader::run(ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + mTree->GetEntry(ent); + + pc.outputs().snapshot(Output{"TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe}, *mTrig); + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void TriggerReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + if (!(mFile && !mFile->IsZombie())) { + throw std::runtime_error("Error opening tree file"); + } + mTree.reset((TTree*)mFile->Get(mTriggerTreeName.c_str())); + if (!mTree) { + throw std::runtime_error("Error opening tree"); + } + + mTree->SetBranchAddress(mTriggerBranchName.c_str(), &mTrig); + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getTPCTriggerReaderSpec() +{ + std::vector outputSpec; + outputSpec.emplace_back("TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "tpc-trigger-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask()}, + Options{ + {"infile", VariantType::String, "tpctriggers.root", {"Name of the input triggers file"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}}}; +} + +} // namespace tpc +} // namespace o2 diff --git a/Detectors/TPC/workflow/src/CalibProcessingHelper.cxx b/Detectors/TPC/workflow/src/CalibProcessingHelper.cxx index d335e55bd37aa..1abd248b69d6c 100644 --- a/Detectors/TPC/workflow/src/CalibProcessingHelper.cxx +++ b/Detectors/TPC/workflow/src/CalibProcessingHelper.cxx @@ -73,7 +73,7 @@ std::vector calib_processing_helper::getFilter(o2::fra return filter; } -uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inputs, std::unique_ptr& reader, bool useOldSubspec, const std::vector& sectors, size_t* nerrors, uint32_t syncOffsetReference, uint32_t decoderType, bool useTrigger) +uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inputs, std::unique_ptr& reader, bool useOldSubspec, const std::vector& sectors, size_t* nerrors, uint32_t syncOffsetReference, uint32_t decoderType, bool useTrigger, bool returnOnNoTrigger) { std::vector filter = getFilter(inputs); @@ -85,6 +85,9 @@ uint64_t calib_processing_helper::processRawData(o2::framework::InputRecord& inp bool readFirstZS = false; const auto triggerBC = ((decoderType == 1 && useTrigger)) ? getTriggerBCoffset(inputs, filter) : -1; + if (returnOnNoTrigger && (triggerBC < 0)) { + return 0; + } // for LinkZS data the maximum sync offset is needed to align the data properly. // getBCsyncOffsetReference only works, if the full TF is seen. Alternatively, this value could be set @@ -435,13 +438,19 @@ int calib_processing_helper::getTriggerBCoffset(const char* data, size_t size, u return -1; } const auto triggerWord = (TriggerWordDLBZS*)(data + size - sizeof(TPCZSHDRV2) - sizeof(TriggerWordDLBZS)); - // Check only first trigger word assuming we will only have laser triggers - const int iTrg = 0; - if (!triggerWord->isValid(iTrg)) { - return -1; + // Only process calibration (Laser) triggers + int iTrg; + for (iTrg = 0; iTrg < TriggerWordDLBZS::MaxTriggerEntries; ++iTrg) { + if (triggerWord->isValid(iTrg)) { + if (triggerWord->getTriggerType(iTrg) == TriggerWordDLBZS::Cal) { + break; + } + } else { + return -1; + } } - const auto triggerBC = triggerWord->getTriggerBC(0); + const auto triggerBC = triggerWord->getTriggerBC(iTrg); const auto orbit = RDHUtils::getHeartBeatOrbit(rdh); const int orbitBC = ((orbit - firstOrbit) * o2::constants::lhc::LHCMaxBunches) + triggerBC; LOGP(debug, "TriggerWordDLBZS {}: Type = {}, orbit = {}, firstOrbit = {}, BC = {}, oribitBC = {}", iTrg, triggerWord->getTriggerType(iTrg), orbit, firstOrbit, triggerWord->getTriggerBC(iTrg), orbitBC); diff --git a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx index 6fa676f7a34a4..8491fe6c772b6 100644 --- a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx @@ -38,7 +38,7 @@ namespace o2::tpc class CalibdEdxDevice : public Task { public: - CalibdEdxDevice(std::shared_ptr req) : mCCDBRequest(req) {} + CalibdEdxDevice(std::shared_ptr req, const o2::base::Propagator::MatCorrType matType) : mCCDBRequest(req), mMatType(matType) {} void init(framework::InitContext& ic) final { @@ -64,6 +64,7 @@ class CalibdEdxDevice : public Task mCalib->set1DFitThreshold(minEntries1D); mCalib->set2DFitThreshold(minEntries2D); mCalib->setElectronCut(fitThreshold, fitPasses, fitThresholdLowFactor); + mCalib->setMaterialType(mMatType); } void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final @@ -115,14 +116,16 @@ class CalibdEdxDevice : public Task } std::shared_ptr mCCDBRequest; + const o2::base::Propagator::MatCorrType mMatType{}; int mDumpToFile{}; uint64_t mRunNumber{0}; ///< processed run number uint64_t mTimeStampStart{0}; ///< time stamp for first TF for CCDB output std::unique_ptr mCalib; }; -DataProcessorSpec getCalibdEdxSpec() +DataProcessorSpec getCalibdEdxSpec(const o2::base::Propagator::MatCorrType matType) { + const bool enableAskMatLUT = matType == o2::base::Propagator::MatCorrType::USEMatCorrLUT; std::vector outputs; outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "TPC_CalibdEdx"}, Lifetime::Sporadic); outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TPC_CalibdEdx"}, Lifetime::Sporadic); @@ -131,7 +134,7 @@ DataProcessorSpec getCalibdEdxSpec() false, // GRPECS=true false, // GRPLHCIF true, // GRPMagField - false, // askMatLUT + enableAskMatLUT, // askMatLUT o2::base::GRPGeomRequest::None, // geometry inputs, true, @@ -141,7 +144,7 @@ DataProcessorSpec getCalibdEdxSpec() "tpc-calib-dEdx", inputs, outputs, - adaptFromTask(ccdbRequest), + adaptFromTask(ccdbRequest, matType), Options{ {"min-entries-sector", VariantType::Int, 1000, {"min entries per GEM stack to enable sector by sector correction. Below this value we only perform one fit per ROC type (IROC, OROC1, ...; no side nor sector information)."}}, {"min-entries-1d", VariantType::Int, 10000, {"minimum entries per stack to fit 1D correction"}}, diff --git a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx index e3aba703e945f..fe931d70742b7 100644 --- a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx @@ -41,7 +41,7 @@ namespace o2::tpc class CalibratordEdxDevice : public Task { public: - CalibratordEdxDevice(std::shared_ptr req) : mCCDBRequest(req) {} + CalibratordEdxDevice(std::shared_ptr req, const o2::base::Propagator::MatCorrType matType) : mCCDBRequest(req), mMatType(matType) {} void init(framework::InitContext& ic) final { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); @@ -72,6 +72,7 @@ class CalibratordEdxDevice : public Task mCalibrator->setSlotLength(slotLength); mCalibrator->setMaxSlotsDelay(maxDelay); mCalibrator->setElectronCut({fitThreshold, fitPasses, fitThresholdLowFactor}); + mCalibrator->setMaterialType(mMatType); if (dumpData) { mCalibrator->enableDebugOutput("calibratordEdx.root"); @@ -127,12 +128,14 @@ class CalibratordEdxDevice : public Task } std::unique_ptr mCalibrator; + const o2::base::Propagator::MatCorrType mMatType{}; std::shared_ptr mCCDBRequest; uint64_t mRunNumber{0}; ///< processed run number }; -DataProcessorSpec getCalibratordEdxSpec() +DataProcessorSpec getCalibratordEdxSpec(const o2::base::Propagator::MatCorrType matType) { + const bool enableAskMatLUT = matType == o2::base::Propagator::MatCorrType::USEMatCorrLUT; std::vector outputs; outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "TPC_CalibdEdx"}, Lifetime::Sporadic); outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TPC_CalibdEdx"}, Lifetime::Sporadic); @@ -141,7 +144,7 @@ DataProcessorSpec getCalibratordEdxSpec() true, // GRPECS=true false, // GRPLHCIF true, // GRPMagField - false, // askMatLUT + enableAskMatLUT, // askMatLUT o2::base::GRPGeomRequest::None, // geometry inputs, true, @@ -150,7 +153,7 @@ DataProcessorSpec getCalibratordEdxSpec() "tpc-calibrator-dEdx", inputs, outputs, - adaptFromTask(ccdbRequest), + adaptFromTask(ccdbRequest, matType), Options{ {"tf-per-slot", VariantType::UInt32, 6000u, {"number of TFs per calibration time slot"}}, {"max-delay", VariantType::UInt32, 10u, {"number of slots in past to consider"}}, diff --git a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx index e0f27913c9d2b..4ff3573918722 100644 --- a/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyDecoderSpec.cxx @@ -17,6 +17,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "DataFormatsTPC/CompressedClusters.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "TPCWorkflow/EntropyDecoderSpec.h" using namespace o2::framework; @@ -48,9 +49,10 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) auto buff = pc.inputs().get>("ctf_TPC"); auto& compclusters = pc.outputs().make>(OutputRef{"output"}); + auto& triggers = pc.outputs().make>(OutputRef{"trigger"}); if (buff.size()) { const auto ctfImage = o2::tpc::CTF::getImage(buff.data()); - iosize = mCTFCoder.decode(ctfImage, compclusters); + iosize = mCTFCoder.decode(ctfImage, compclusters, triggers); } pc.outputs().snapshot({"ctfrep", 0}, iosize); mTimer.Stop(); @@ -75,6 +77,7 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) "tpc-entropy-decoder", inputs, Outputs{OutputSpec{{"output"}, "TPC", "COMPCLUSTERSFLAT", 0, Lifetime::Timeframe}, + OutputSpec{{"trigger"}, "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe}, OutputSpec{{"ctfrep"}, "TPC", "CTFDECREP", 0, Lifetime::Timeframe}}, AlgorithmSpec{adaptFromTask(verbosity)}, Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, diff --git a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx index 28226752528bf..7e03748da0bc4 100644 --- a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx @@ -16,6 +16,7 @@ #include "TPCWorkflow/EntropyEncoderSpec.h" #include "DataFormatsTPC/CompressedClusters.h" +#include "DataFormatsTPC/ZeroSuppression.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "Headers/DataHeader.h" @@ -112,6 +113,7 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mCTFCoder.updateTimeDependentParams(pc, true); CompressedClusters clusters; + static o2::tpc::detail::TriggerInfo trigComp; if (mFromFile) { auto tmp = pc.inputs().get("input"); @@ -128,12 +130,28 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) } clusters = *tmp; } + auto triggers = pc.inputs().get>("trigger"); auto cput = mTimer.CpuTime(); mTimer.Start(false); auto& buffer = pc.outputs().make>(Output{"TPC", "CTFDATA", 0, Lifetime::Timeframe}); std::vector rejectHits, rejectTracks, rejectTrackHits, rejectTrackHitsReduced; CompressedClusters clustersFiltered = clusters; std::vector, std::vector>> tmpBuffer(std::max(mNThreads, 1)); + + // prepare trigger info + trigComp.clear(); + for (const auto& trig : triggers) { + for (int it = 0; it < o2::tpc::TriggerWordDLBZS::MaxTriggerEntries; it++) { + if (trig.triggerWord.isValid(it)) { + trigComp.deltaOrbit.push_back(trig.orbit); + trigComp.deltaBC.push_back(trig.triggerWord.getTriggerBC(it)); + trigComp.triggerType.push_back(trig.triggerWord.getTriggerType(it)); + } else { + break; + } + } + } + if (mSelIR) { if (clusters.nTracks && clusters.solenoidBz != -1e6f && clusters.solenoidBz != mParam->bzkG) { throw std::runtime_error("Configured solenoid Bz does not match value used for track model encoding"); @@ -258,7 +276,29 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) clustersFiltered.timeDiffU = tmpBuffer[0].first.data(); clustersFiltered.padDiffU = tmpBuffer[0].second.data(); } - auto iosize = mCTFCoder.encode(buffer, clusters, clustersFiltered, mSelIR ? &rejectHits : nullptr, mSelIR ? &rejectTracks : nullptr, mSelIR ? &rejectTrackHits : nullptr, mSelIR ? &rejectTrackHitsReduced : nullptr); + + // transform trigger info to differential form + uint32_t prevOrbit = -1; + uint16_t prevBC = -1; + if (trigComp.triggerType.size()) { + prevOrbit = trigComp.firstOrbit = trigComp.deltaOrbit[0]; + prevBC = trigComp.deltaBC[0]; + trigComp.deltaOrbit[0] = 0; + for (size_t it = 1; it < trigComp.triggerType.size(); it++) { + if (trigComp.deltaOrbit[it] == prevOrbit) { + auto bc = trigComp.deltaBC[it]; + trigComp.deltaBC[it] -= prevBC; + prevBC = bc; + trigComp.deltaOrbit[it] = 0; + } else { + auto orb = trigComp.deltaOrbit[it]; + trigComp.deltaOrbit[it] -= prevOrbit; + prevOrbit = orb; + } + } + } + + auto iosize = mCTFCoder.encode(buffer, clusters, clustersFiltered, trigComp, mSelIR ? &rejectHits : nullptr, mSelIR ? &rejectTracks : nullptr, mSelIR ? &rejectTrackHits : nullptr, mSelIR ? &rejectTrackHitsReduced : nullptr); pc.outputs().snapshot({"ctfrep", 0}, iosize); mTimer.Stop(); if (mSelIR) { @@ -278,6 +318,7 @@ DataProcessorSpec getEntropyEncoderSpec(bool inputFromFile, bool selIR) std::vector inputs; header::DataDescription inputType = inputFromFile ? header::DataDescription("COMPCLUSTERS") : header::DataDescription("COMPCLUSTERSFLAT"); inputs.emplace_back("input", "TPC", inputType, 0, Lifetime::Timeframe); + inputs.emplace_back("trigger", "TPC", "TRIGGERWORDS", 0, Lifetime::Timeframe); inputs.emplace_back("ctfdict", "TPC", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TPC/Calib/CTFDictionaryTree")); std::shared_ptr ggreq; diff --git a/Detectors/TPC/workflow/src/FileReaderWorkflow.cxx b/Detectors/TPC/workflow/src/FileReaderWorkflow.cxx index b083a5133fae9..c50b5685cdcd0 100644 --- a/Detectors/TPC/workflow/src/FileReaderWorkflow.cxx +++ b/Detectors/TPC/workflow/src/FileReaderWorkflow.cxx @@ -12,6 +12,7 @@ /// @file FileReaderWorkflow.cxx #include "TPCReaderWorkflow/ClusterReaderSpec.h" +#include "TPCReaderWorkflow/TriggerReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" #include "Algorithm/RangeTokenizer.h" @@ -67,6 +68,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) if (isEnabled(Input::Clusters)) { specs.emplace_back(o2::tpc::getClusterReaderSpec(doMC)); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + specs.emplace_back(o2::tpc::getTPCTriggerReaderSpec()); + } } if (isEnabled(Input::Tracks)) { diff --git a/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx b/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx index 6be5977e42717..6c90bfc86b536 100644 --- a/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx +++ b/Detectors/TPC/workflow/src/MIPTrackFilterSpec.cxx @@ -52,6 +52,7 @@ class MIPTrackFilterDevice : public Task unsigned int mProcessEveryNthTF{1}; ///< process every Nth TF only int mMaxTracksPerTF{-1}; ///< max number of MIP tracks processed per TF uint32_t mTFCounter{0}; ///< counter to keep track of the TFs + int mProcessNFirstTFs{0}; ///< number of first TFs which are not sampled }; void MIPTrackFilterDevice::init(framework::InitContext& ic) @@ -70,6 +71,7 @@ void MIPTrackFilterDevice::init(framework::InitContext& ic) if (mProcessEveryNthTF <= 0) { mProcessEveryNthTF = 1; } + mProcessNFirstTFs = ic.options().get("process-first-n-TFs"); if (mProcessEveryNthTF > 1) { std::mt19937 rng(std::time(nullptr)); @@ -87,8 +89,8 @@ void MIPTrackFilterDevice::init(framework::InitContext& ic) void MIPTrackFilterDevice::run(ProcessingContext& pc) { - if (mTFCounter++ % mProcessEveryNthTF) { - const auto currentTF = processing_helpers::getCurrentTF(pc); + const auto currentTF = processing_helpers::getCurrentTF(pc); + if ((mTFCounter++ % mProcessEveryNthTF) && (currentTF >= mProcessNFirstTFs)) { LOGP(info, "Skipping TF {}", currentTF); return; } @@ -153,7 +155,8 @@ DataProcessorSpec getMIPTrackFilterSpec() {"max-dedx", VariantType::Double, 200., {"maximum dEdx cut"}}, {"min-clusters", VariantType::Int, 60, {"minimum number of clusters in a track"}}, {"processEveryNthTF", VariantType::Int, 1, {"Using only a fraction of the data: 1: Use every TF, 10: Process only every tenth TF."}}, - {"maxTracksPerTF", VariantType::Int, -1, {"Maximum number of processed tracks per TF (-1 for processing all tracks)"}}}}; + {"maxTracksPerTF", VariantType::Int, -1, {"Maximum number of processed tracks per TF (-1 for processing all tracks)"}}, + {"process-first-n-TFs", VariantType::Int, 1, {"Number of first TFs which are not sampled"}}}}; } } // namespace o2::tpc diff --git a/Detectors/TPC/workflow/src/OccupancyFilterSpec.cxx b/Detectors/TPC/workflow/src/OccupancyFilterSpec.cxx new file mode 100644 index 0000000000000..9891d80a39503 --- /dev/null +++ b/Detectors/TPC/workflow/src/OccupancyFilterSpec.cxx @@ -0,0 +1,138 @@ +// 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 +#include +#include +#include +#include +#include + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Task.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Headers/DataHeader.h" + +#include "DataFormatsTPC/TPCSectorHeader.h" +#include "DataFormatsTPC/Digit.h" +#include "TPCWorkflow/OccupancyFilterSpec.h" + +using namespace o2::framework; +using namespace o2::header; +using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; + +namespace o2::tpc +{ + +class OccupancyFilterDevice : public o2::framework::Task +{ + public: + OccupancyFilterDevice() = default; + + void init(o2::framework::InitContext& ic) final + { + mOccupancyThreshold = ic.options().get("occupancy-threshold"); + mAdcValueThreshold = ic.options().get("adc-threshold"); + mMinimumTimeStamp = ic.options().get("min-timestamp"); + mFilterAdcValue = ic.options().get("filter-adc"); + mFilterTimeStamp = ic.options().get("filter-timestamp"); + LOGP(debug, "got minimum timestamp {}", mMinimumTimeStamp); + } + + void run(o2::framework::ProcessingContext& pc) final + { + for (auto const& inputRef : InputRecordWalker(pc.inputs())) { + auto const* sectorHeader = DataRefUtils::getHeader(inputRef); + if (sectorHeader == nullptr) { + LOGP(error, "sector header missing on header stack for input on ", inputRef.spec->binding); + continue; + } + + const int sector = sectorHeader->sector(); + auto inDigitsO = pc.inputs().get>(inputRef); + LOGP(debug, "processing sector {} with {} input digits", sector, inDigitsO.size()); + + std::vector digitsPerTimeBin(int(128 * 445.5)); + + for (const auto& digit : inDigitsO) { + if ((digit.getChargeFloat() > mAdcValueThreshold) && (digit.getTimeStamp() > mMinimumTimeStamp)) { + ++digitsPerTimeBin[digit.getTimeStamp()]; + } + } + + bool isAboveThreshold{false}; + for (const auto& timeBinOccupancy : digitsPerTimeBin) { + if (timeBinOccupancy > mOccupancyThreshold) { + LOGP(info, "Sector {}, timeBinOccupancy {} > occupancy-threshold {}", sector, timeBinOccupancy, mOccupancyThreshold); + isAboveThreshold = true; + break; + } + } + + std::vector cpDigits; + if (isAboveThreshold) { + std::copy_if(inDigitsO.begin(), inDigitsO.end(), std::back_inserter(cpDigits), + [this](const auto& digit) { + return (!mFilterTimeStamp || (digit.getTimeStamp() > mMinimumTimeStamp)) && (!mFilterAdcValue || (digit.getChargeFloat() > mAdcValueThreshold)); + }); + } + snapshot(pc.outputs(), cpDigits, sector); + + ++mProcessedTFs; + LOGP(info, "Number of processed time frames: {}", mProcessedTFs); + } + } + + private: + float mOccupancyThreshold{50.f}; + float mAdcValueThreshold{0.f}; + long mMinimumTimeStamp{0LL}; + bool mFilterAdcValue{false}; + bool mFilterTimeStamp{false}; + uint32_t mProcessedTFs{0}; + + //____________________________________________________________________________ + void snapshot(DataAllocator& output, std::vector& digits, int sector) + { + o2::tpc::TPCSectorHeader header{sector}; + header.activeSectors = (0x1 << sector); + output.snapshot(Output{gDataOriginTPC, "FILTERDIG", static_cast(sector), Lifetime::Sporadic, header}, digits); + } +}; + +o2::framework::DataProcessorSpec getOccupancyFilterSpec() +{ + using device = o2::tpc::OccupancyFilterDevice; + + std::vector inputs{ + InputSpec{"digits", gDataOriginTPC, "DIGITS", 0, Lifetime::Timeframe}, + }; + + std::vector outputs; + outputs.emplace_back(gDataOriginTPC, "FILTERDIG", 0, Lifetime::Sporadic); + + return DataProcessorSpec{ + "tpc-occupancy-filter", + inputs, + outputs, + AlgorithmSpec{adaptFromTask()}, + Options{ + {"occupancy-threshold", VariantType::Float, 50.f, {"threshold for occupancy in one time bin"}}, + {"adc-threshold", VariantType::Float, 0.f, {"threshold for adc value"}}, + {"min-timestamp", VariantType::Int64, 0LL, {"minimum time bin"}}, + {"filter-adc", VariantType::Bool, false, {"filter the data by the given adc threshold"}}, + {"filter-timestamp", VariantType::Bool, false, {"filter the data by the given minimum timestamp"}}, + } // end Options + }; // end DataProcessorSpec +} +} // namespace o2::tpc diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 6f49db19b3c29..4e2ea2447306e 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -19,6 +19,7 @@ #include "Framework/Logger.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "TPCWorkflow/RecoWorkflow.h" +#include "TPCWorkflow/TPCTriggerWriterSpec.h" #include "TPCReaderWorkflow/PublisherSpec.h" #include "TPCWorkflow/ClustererSpec.h" #include "TPCWorkflow/ClusterDecoderRawSpec.h" @@ -40,6 +41,7 @@ #include "DataFormatsTPC/Helpers.h" #include "DataFormatsTPC/ZeroSuppression.h" #include "TPCReaderWorkflow/ClusterReaderSpec.h" +#include "TPCReaderWorkflow/TriggerReaderSpec.h" #include #include @@ -91,7 +93,8 @@ const std::unordered_map OutputMap{ {"send-clusters-per-sector", OutputType::SendClustersPerSector}, {"zsraw", OutputType::ZSRaw}, {"qa", OutputType::QA}, - {"no-shared-cluster-map", OutputType::NoSharedClusterMap}}; + {"no-shared-cluster-map", OutputType::NoSharedClusterMap}, + {"tpc-triggers", OutputType::TPCTriggers}}; framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vector const& tpcSectors, unsigned long tpcSectorMask, std::vector const& laneConfiguration, bool propagateMC, unsigned nLanes, std::string const& cfgInput, std::string const& cfgOutput, bool disableRootInput, @@ -204,6 +207,9 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto propagateMC)); } else if (inputType == InputType::Clusters) { specs.emplace_back(o2::tpc::getClusterReaderSpec(propagateMC, &tpcSectors, &laneConfiguration)); + if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { + specs.emplace_back(o2::tpc::getTPCTriggerReaderSpec()); + } } else if (inputType == InputType::CompClusters) { // TODO: need to check if we want to store the MC labels alongside with compressed clusters // for the moment reading of labels is disabled (last parameter is false) @@ -414,6 +420,10 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto (caClusterer || decompressTPC || inputType == InputType::PassThrough) && !isEnabled(OutputType::SendClustersPerSector))); } + if ((isEnabled(OutputType::TPCTriggers) || caClusterer) && !isEnabled(OutputType::DisableWriter)) { + specs.push_back(o2::tpc::getTPCTriggerWriterSpec()); + } + if (zsOnTheFly) { specs.emplace_back(o2::tpc::getZSEncoderSpec(tpcSectors, outRaw, tpcSectorMask)); } @@ -445,6 +455,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto cfg.processMC = propagateMC; cfg.sendClustersPerSector = isEnabled(OutputType::SendClustersPerSector); cfg.askDISTSTF = askDISTSTF; + cfg.tpcTriggerHandling = isEnabled(OutputType::TPCTriggers) || cfg.caClusterer; Inputs ggInputs; auto ggRequest = std::make_shared(false, true, false, true, true, o2::base::GRPGeomRequest::Aligned, ggInputs, true); diff --git a/Detectors/TPC/workflow/src/TPCMergeTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCMergeTimeSeriesSpec.cxx new file mode 100644 index 0000000000000..9a3ef972cb175 --- /dev/null +++ b/Detectors/TPC/workflow/src/TPCMergeTimeSeriesSpec.cxx @@ -0,0 +1,190 @@ +// 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 TPCMergeTimeSeriesSpec.cxx +/// \brief device for merging the tpc time series in contiguous time intervals +/// \author Matthias Kleiner +/// \date Aug 20, 2023 + +#include "TPCWorkflow/TPCMergeTimeSeriesSpec.h" +#include "DetectorsCalibration/IntegratedClusterCalibrator.h" +#include "Framework/Task.h" +#include "Framework/ConfigParamRegistry.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsCalibration/Utils.h" +#include "DetectorsCommonDataFormats/FileMetaData.h" +#include "Framework/DataTakingContext.h" +#include "TPCBase/CDBInterface.h" +#include "TPCWorkflow/TPCTimeSeriesSpec.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tpc +{ + +class TPCMergeTimeSeries : public Task +{ + public: + /// \constructor + TPCMergeTimeSeries(std::shared_ptr req) : mCCDBRequest(req){}; + + void init(framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + mCalibrator = std::make_unique>(); + const auto slotLength = ic.options().get("tf-per-slot"); + const auto maxDelay = ic.options().get("max-delay"); + const auto debug = ic.options().get("debug"); + mCalibrator->setSlotLength(slotLength); + mCalibrator->setMaxSlotsDelay(maxDelay); + mCalibrator->setDebug(debug); + + mCalibFileDir = ic.options().get("output-dir"); + if (mCalibFileDir != "/dev/null") { + mCalibFileDir = o2::utils::Str::rectifyDirectory(mCalibFileDir); + } + mMetaFileDir = ic.options().get("meta-output-dir"); + if (mMetaFileDir != "/dev/null") { + mMetaFileDir = o2::utils::Str::rectifyDirectory(mMetaFileDir); + } + mDumpCalibData = ic.options().get("dump-calib-data"); + } + + void run(ProcessingContext& pc) final + { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); + + // set data taking context only once + if (mSetDataTakingCont) { + mDataTakingContext = pc.services().get(); + mSetDataTakingCont = false; + } + + // check slots to finalize at the beginning to avoid creation of a second slot and thus allocating new memory + mCalibrator->checkSlotsToFinalize(mCalibrator->getCurrentTFInfo().tfCounter, mCalibrator->getMaxSlotsDelay() * mCalibrator->getSlotLength()); + + LOGP(debug, "Created {} objects for {} slots with current TF {} and time stamp {}", mCalibrator->getTFinterval().size(), mCalibrator->getNSlots(), mCalibrator->getCurrentTFInfo().tfCounter, mCalibrator->getCurrentTFInfo().creation); + if (mCalibrator->hasCalibrationData()) { + sendOutput(pc.outputs()); + } + + const auto currents = pc.inputs().get(pc.inputs().get("timeseries")); + + // accumulate the currents + mCalibrator->process(mCalibrator->getCurrentTFInfo().tfCounter, *currents); + } + + void endOfStream(EndOfStreamContext& eos) final + { + LOGP(info, "Finalizing calibration. Dumping all objects"); + // just write everything out + for (int i = 0; i < mCalibrator->getNSlots(); ++i) { + mCalibrator->finalizeSlot(mCalibrator->getSlot(i)); + } + sendOutput(eos.outputs()); + } + + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); } + + static constexpr header::DataDescription getDataDescriptionCCDB() { return header::DataDescription{"TimeSeriesITSTPC"}; } + + private: + std::unique_ptr> mCalibrator; ///< calibrator object for creating the pad-by-pad gain map + std::shared_ptr mCCDBRequest; ///< info for CCDB request + std::string mMetaFileDir{}; ///< output dir for meta data + std::string mCalibFileDir{}; ///< output dir for calib objects + o2::framework::DataTakingContext mDataTakingContext{}; ///< run information for meta data + bool mSetDataTakingCont{true}; ///< flag for setting data taking context only once + bool mDumpCalibData{false}; ///< dump the calibration data as a calibration file + + void sendOutput(DataAllocator& output) + { + auto calibrations = std::move(*mCalibrator).getCalibs(); + const auto& intervals = mCalibrator->getTimeIntervals(); + assert(calibrations.size() == intervals.size()); + for (unsigned int i = 0; i < calibrations.size(); i++) { + auto& object = calibrations[i]; + o2::ccdb::CcdbObjectInfo info(CDBTypeMap.at(CDBType::CalTimeSeries), std::string{}, std::string{}, std::map{}, intervals[i].first, intervals[i].second); + + auto image = o2::ccdb::CcdbApi::createObjectImage(&object, &info); + LOGP(info, "Sending object {} / {} of size {} bytes, valid for {} : {} ", info.getPath(), info.getFileName(), image->size(), info.getStartValidityTimestamp(), info.getEndValidityTimestamp()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, getDataDescriptionCCDB(), i}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, getDataDescriptionCCDB(), i}, info); + + if (mDumpCalibData && mCalibFileDir != "/dev/null") { + std::string calibFName = fmt::format("tpc_timeseries_cal_data_{}_{}.root", info.getStartValidityTimestamp(), info.getEndValidityTimestamp()); + try { + std::ofstream calFile(fmt::format("{}{}", mCalibFileDir, calibFName), std::ios::out | std::ios::binary); + calFile.write(image->data(), image->size()); + calFile.close(); + } catch (std::exception const& e) { + LOG(error) << "Failed to store TimeSeriesITSTPC calibration data file " << calibFName << ", reason: " << e.what(); + } + + if (mMetaFileDir != "/dev/null") { + o2::dataformats::FileMetaData calMetaData; + calMetaData.fillFileData(calibFName); + calMetaData.setDataTakingContext(mDataTakingContext); + calMetaData.type = "calib"; + calMetaData.priority = "low"; + auto metaFileNameTmp = fmt::format("{}{}.tmp", mMetaFileDir, calibFName); + auto metaFileName = fmt::format("{}{}.done", mMetaFileDir, calibFName); + try { + std::ofstream metaFileOut(metaFileNameTmp); + metaFileOut << calMetaData; + metaFileOut.close(); + std::filesystem::rename(metaFileNameTmp, metaFileName); + } catch (std::exception const& e) { + LOG(error) << "Failed to store CTF meta data file " << metaFileName << ", reason: " << e.what(); + } + } + } + } + mCalibrator->initOutput(); // empty the outputs after they are send + } +}; + +o2::framework::DataProcessorSpec getTPCMergeTimeSeriesSpec() +{ + std::vector inputs; + inputs.emplace_back("timeseries", o2::header::gDataOriginTPC, getDataDescriptionTimeSeries(), 0, Lifetime::Sporadic); + auto ccdbRequest = std::make_shared(true, // orbitResetTime + true, // GRPECS=true for nHBF per TF + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs); + + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, TPCMergeTimeSeries::getDataDescriptionCCDB()}, Lifetime::Sporadic); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, TPCMergeTimeSeries::getDataDescriptionCCDB()}, Lifetime::Sporadic); + + return DataProcessorSpec{ + "tpc-merge-time-series", + inputs, + outputs, + AlgorithmSpec{adaptFromTask(ccdbRequest)}, + Options{ + {"debug", VariantType::Bool, false, {"Write debug output files"}}, + {"tf-per-slot", VariantType::UInt32, 1000u, {"number of TFs per calibration time slot"}}, + {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}, + {"output-dir", VariantType::String, "none", {"calibration files output directory, must exist"}}, + {"meta-output-dir", VariantType::String, "/dev/null", {"calibration metadata output directory, must exist (if not /dev/null)"}}, + {"dump-calib-data", VariantType::Bool, false, {"Dump TimeSeriesITSTPC calibration data to file"}}}}; +} + +} // end namespace tpc +} // end namespace o2 diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesReaderSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesReaderSpec.cxx new file mode 100644 index 0000000000000..9320b8c3a238d --- /dev/null +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesReaderSpec.cxx @@ -0,0 +1,160 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TPCTimeSeriesReaderSpec.cxx + +#include +#include + +#include "TPCWorkflow/TPCTimeSeriesReaderSpec.h" +#include "TPCWorkflow/TPCTimeSeriesSpec.h" +#include "DetectorsCalibration/IntegratedClusterCalibrator.h" +#include "Framework/Task.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "CommonUtils/NameConf.h" +#include "CommonDataFormat/TFIDInfo.h" +#include "Algorithm/RangeTokenizer.h" +#include "TChain.h" +#include "TGrid.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tpc +{ + +class TimeSeriesReader : public Task +{ + public: + TimeSeriesReader() = default; + ~TimeSeriesReader() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + + private: + void connectTrees(); + + int mChainEntry = 0; ///< processed entries in the chain + std::unique_ptr mChain; ///< input TChain + std::vector mFileNames; ///< input files + TimeSeriesITSTPC mTimeSeries, *mTimeSeriesPtr = &mTimeSeries; ///< branch time series values + o2::dataformats::TFIDInfo mTFinfo, *mTFinfoPtr = &mTFinfo; ///< branch TFIDInfo for injecting correct time + std::vector> mIndices; ///< firstTfOrbit, file, index +}; + +void TimeSeriesReader::init(InitContext& ic) +{ + const auto dontCheckFileAccess = ic.options().get("dont-check-file-access"); + auto fileList = o2::RangeTokenizer::tokenize(ic.options().get("tpc-time-series-infiles")); + + // check if only one input file (a txt file contaning a list of files is provided) + if (fileList.size() == 1) { + if (boost::algorithm::ends_with(fileList.front(), "txt")) { + LOGP(info, "Reading files from input file {}", fileList.front()); + std::ifstream is(fileList.front()); + std::istream_iterator start(is); + std::istream_iterator end; + std::vector fileNamesTmp(start, end); + fileList = fileNamesTmp; + } + } + + const std::string inpDir = o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")); + for (const auto& file : fileList) { + if ((file.find("alien://") == 0) && !gGrid && !TGrid::Connect("alien://")) { + LOG(fatal) << "Failed to open alien connection"; + } + const auto fileDir = o2::utils::Str::concat_string(inpDir, file); + if (!dontCheckFileAccess) { + std::unique_ptr filePtr(TFile::Open(fileDir.data())); + if (!filePtr || !filePtr->IsOpen() || filePtr->IsZombie()) { + LOGP(warning, "Could not open file {}", fileDir); + continue; + } + } + mFileNames.emplace_back(fileDir); + } + + if (mFileNames.size() == 0) { + LOGP(error, "No input files to process"); + } + connectTrees(); +} + +void TimeSeriesReader::run(ProcessingContext& pc) +{ + // check time order inside the TChain + if (mChainEntry == 0) { + mIndices.clear(); + mIndices.reserve(mChain->GetEntries()); + // disable all branches except the firstTForbit branch to significantly speed up the loop over the TTree + mChain->SetBranchStatus("*", 0); + mChain->SetBranchStatus("firstTForbit", 1); + for (unsigned long i = 0; i < mChain->GetEntries(); i++) { + mChain->GetEntry(i); + mIndices.emplace_back(std::make_pair(mTFinfo.firstTForbit, i)); + } + mChain->SetBranchStatus("*", 1); + std::sort(mIndices.begin(), mIndices.end()); + } + + LOGP(debug, "Processing entry {}", mIndices[mChainEntry].second); + mChain->GetEntry(mIndices[mChainEntry++].second); + + // inject correct timing informations + auto& timingInfo = pc.services().get(); + timingInfo.firstTForbit = mTFinfo.firstTForbit; + timingInfo.tfCounter = mTFinfo.tfCounter; + timingInfo.runNumber = mTFinfo.runNumber; + timingInfo.creation = mTFinfo.creation; + + pc.outputs().snapshot(Output{header::gDataOriginTPC, getDataDescriptionTimeSeries()}, mTimeSeries); + usleep(100); + + if (mChainEntry >= mChain->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void TimeSeriesReader::connectTrees() +{ + mChain.reset(new TChain("treeTimeSeries")); + for (const auto& file : mFileNames) { + LOGP(info, "Adding file to chain: {}", file); + mChain->AddFile(file.data()); + } + assert(mChain->GetEntries()); + mChain->SetBranchAddress("TimeSeries", &mTimeSeriesPtr); + mChain->SetBranchAddress("tfID", &mTFinfoPtr); +} + +DataProcessorSpec getTPCTimeSeriesReaderSpec() +{ + std::vector outputs; + outputs.emplace_back(o2::header::gDataOriginTPC, getDataDescriptionTimeSeries(), 0, Lifetime::Sporadic); + + return DataProcessorSpec{ + "tpc-time-series-reader", + Inputs{}, + outputs, + AlgorithmSpec{adaptFromTask()}, + Options{ + {"tpc-time-series-infiles", VariantType::String, "o2_timeseries_tpc.root", {"comma-separated list of input files or .txt file containing list of input files"}}, + {"input-dir", VariantType::String, "none", {"Input directory"}}, + {"dont-check-file-access", VariantType::Bool, false, {"Deactivate check if all files are accessible before adding them to the list of files"}}, + }}; +} + +} // namespace tpc +} // namespace o2 diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx new file mode 100644 index 0000000000000..93b19a0c99677 --- /dev/null +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -0,0 +1,1034 @@ +// 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 TPCTimeSeriesSpec.cxx +/// \brief device for time series +/// \author Matthias Kleiner +/// \date Aug 20, 2023 + +#include "Framework/Task.h" +#include "Framework/Logger.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/ControlService.h" +#include "DataFormatsTPC/WorkflowHelper.h" +#include "TPCWorkflow/ProcessingHelpers.h" +#include "TPCBase/Mapper.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "TPCWorkflow/TPCTimeSeriesSpec.h" +#include "DetectorsBase/TFIDInfoHelper.h" +#include "DetectorsBase/Propagator.h" +#include "TPCCalibration/RobustAverage.h" +#include "DetectorsCalibration/IntegratedClusterCalibrator.h" +#include "CommonUtils/DebugStreamer.h" +#include "ReconstructionDataFormats/TrackTPCITS.h" +#include "CommonDataFormat/AbstractRefAccessor.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "ReconstructionDataFormats/VtxTrackIndex.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "TPCBase/ParameterElectronics.h" +#include "TPCCalibration/VDriftHelper.h" +#include +#include + +using namespace o2::framework; + +namespace o2 +{ +namespace tpc +{ + +class TPCTimeSeries : public Task +{ + public: + /// \constructor + TPCTimeSeries(std::shared_ptr req, const bool disableWriter, const o2::base::Propagator::MatCorrType matType) : mCCDBRequest(req), mDisableWriter(disableWriter), mMatType(matType){}; + + void init(framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + mNMaxTracks = ic.options().get("max-tracks"); + mMinMom = ic.options().get("min-momentum"); + mMinNCl = ic.options().get("min-cluster"); + mMaxTgl = ic.options().get("max-tgl"); + mMaxQPtBin = ic.options().get("max-qPt-bin"); + mCoarseStep = ic.options().get("coarse-step"); + mFineStep = ic.options().get("fine-step"); + mCutDCA = ic.options().get("cut-DCA-median"); + mCutRMS = ic.options().get("cut-DCA-RMS"); + mRefXSec = ic.options().get("refX-for-sector"); + mTglBins = ic.options().get("tgl-bins"); + mPhiBins = ic.options().get("phi-bins"); + mQPtBins = ic.options().get("qPt-bins"); + mNThreads = ic.options().get("threads"); + maxITSTPCDCAr = ic.options().get("max-ITS-TPC-DCAr"); + maxITSTPCDCAz = ic.options().get("max-ITS-TPC-DCAz"); + maxITSTPCDCAr_comb = ic.options().get("max-ITS-TPC-DCAr_comb"); + maxITSTPCDCAz_comb = ic.options().get("max-ITS-TPC-DCAz_comb"); + mTimeWindowMUS = ic.options().get("time-window-mult-mus"); + mMIPdEdx = ic.options().get("MIP-dedx"); + mUseQMax = ic.options().get("use-qMax"); + mMaxSnp = ic.options().get("max-snp"); + mXCoarse = ic.options().get("mX-coarse"); + mSqrt = ic.options().get("sqrts"); + mBufferVals.resize(mNThreads); + mBufferDCA.setBinning(mPhiBins, mTglBins, mQPtBins); + } + + void run(ProcessingContext& pc) final + { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + if (mTPCVDriftHelper.isUpdated()) { + mTPCVDriftHelper.acknowledgeUpdate(); + mVDrift = mTPCVDriftHelper.getVDriftObject().getVDrift(); + LOGP(info, "Updated reference drift velocity to: {}", mVDrift); + } + + const int nBins = getNBins(); + + // init only once + if (mAvgADCAr.size() != nBins) { + mBufferDCA.resize(nBins); + mAvgADCAr.resize(nBins); + mAvgCDCAr.resize(nBins); + mAvgADCAz.resize(nBins); + mAvgCDCAz.resize(nBins); + mAvgMeffA.resize(nBins); + mAvgMeffC.resize(nBins); + mAvgChi2MatchA.resize(nBins); + mAvgChi2MatchC.resize(nBins); + mMIPdEdxRatioA.resize(nBins); + mMIPdEdxRatioC.resize(nBins); + mTPCChi2A.resize(nBins); + mTPCChi2C.resize(nBins); + mTPCNClA.resize(nBins); + mTPCNClC.resize(nBins); + } + + // getting tracks + auto tracksTPC = pc.inputs().get>("tracksTPC"); + auto tracksITSTPC = pc.inputs().get>("tracksITSTPC"); + + // getting the vertices + const auto vertices = pc.inputs().get>("pvtx"); + const auto primMatchedTracks = pc.inputs().get>("pvtx_trmtc"); + const auto primMatchedTracksRef = pc.inputs().get>("pvtx_tref"); + + LOGP(info, "Processing {} vertices, {} primary matched tracks, {} TPC tracks, {} ITS-TPC tracks", vertices.size(), primMatchedTracks.size(), tracksTPC.size(), tracksITSTPC.size()); + + // calculate mean vertex, RMS and count vertices + std::array avgVtx; + for (auto& avg : avgVtx) { + avg.reserve(vertices.size()); + } + for (const auto& vtx : vertices) { + avgVtx[0].addValue(vtx.getX()); + avgVtx[1].addValue(vtx.getY()); + avgVtx[2].addValue(vtx.getZ()); + avgVtx[3].addValue(vtx.getNContributors()); + } + mBufferDCA.vertexX_Median.front() = avgVtx[0].getMedian(); + mBufferDCA.vertexY_Median.front() = avgVtx[1].getMedian(); + mBufferDCA.vertexZ_Median.front() = avgVtx[2].getMedian(); + mBufferDCA.vertexX_RMS.front() = avgVtx[0].getStdDev(); + mBufferDCA.vertexY_RMS.front() = avgVtx[1].getStdDev(); + mBufferDCA.vertexZ_RMS.front() = avgVtx[2].getStdDev(); + mBufferDCA.nPrimVertices.front() = vertices.size(); + mBufferDCA.nVertexContributors_Median.front() = avgVtx[3].getMedian(); + mBufferDCA.nVertexContributors_RMS.front() = avgVtx[3].getStdDev(); + + // storing collision vertex to ITS-TPC track index + std::unordered_map indicesITSTPC_vtx; // ITS-TPC track index -> collision vertex ID + // loop over collisions + for (const auto& ref : primMatchedTracksRef) { + const auto source = o2::dataformats::VtxTrackIndex::Source::ITSTPC; + const int vID = ref.getVtxID(); // vertex ID + const int firstEntry = ref.getFirstEntryOfSource(source); + const int nEntries = ref.getEntriesOfSource(source); + // loop over all tracks belonging to the vertex + for (int i = 0; i < nEntries; ++i) { + // storing vertex ID for ITS-TPC track + const auto& matchedTrk = primMatchedTracks[i + firstEntry]; + if (matchedTrk.isTrackSource(source)) { + indicesITSTPC_vtx[matchedTrk] = vID; + } + } + } + + // storing indices to ITS-TPC tracks and vertex ID for tpc track + std::unordered_map> indicesITSTPC; // TPC track index -> ITS-TPC track index, vertex ID + // loop over all ITS-TPC tracks + for (int i = 0; i < tracksITSTPC.size(); ++i) { + auto it = indicesITSTPC_vtx.find(i); + // check if ITS-TPC track has attached vertex + const auto idxVtx = (it != indicesITSTPC_vtx.end()) ? (it->second) : -1; + // store TPC index and ITS-TPC+vertex index + indicesITSTPC[tracksITSTPC[i].getRefTPC().getIndex()] = {i, idxVtx}; + } + + // find nearest vertex of tracks which have no vertex assigned + findNearesVertex(tracksTPC, vertices); + + // getting cluster references for cluster bitmask + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamTimeSeries)) { + mTPCTrackClIdx = pc.inputs().get>("trackTPCClRefs"); + mFirstTFOrbit = processing_helpers::getFirstTForbit(pc); + // get local multiplicity - count neighbouring tracks + findNNeighbourTracks(tracksTPC); + }) + + // reset buffers + for (int i = 0; i < nBins; ++i) { + for (int type = 0; type < mAvgADCAr[i].size(); ++type) { + mAvgADCAr[i][type].clear(); + mAvgCDCAr[i][type].clear(); + mAvgADCAz[i][type].clear(); + mAvgCDCAz[i][type].clear(); + } + for (int type = 0; type < mMIPdEdxRatioA[i].size(); ++type) { + mMIPdEdxRatioA[i][type].clear(); + mMIPdEdxRatioC[i][type].clear(); + mTPCChi2A[i][type].clear(); + mTPCChi2C[i][type].clear(); + mTPCNClA[i][type].clear(); + mTPCNClC[i][type].clear(); + } + for (int j = 0; j < mAvgMeffA[i].size(); ++j) { + mAvgMeffA[i][j].clear(); + mAvgMeffC[i][j].clear(); + mAvgChi2MatchA[i][j].clear(); + mAvgChi2MatchC[i][j].clear(); + } + } + + for (int i = 0; i < mNThreads; ++i) { + mBufferVals[i].front().clear(); + mBufferVals[i].back().clear(); + } + + // define number of tracks which are used + const auto nTracks = tracksTPC.size(); + const size_t loopEnd = (mNMaxTracks < 0) ? nTracks : ((mNMaxTracks > nTracks) ? nTracks : size_t(mNMaxTracks)); + + // reserve memory + for (int i = 0; i < nBins; ++i) { + for (int type = 0; type < mAvgADCAr[i].size(); ++type) { + mAvgADCAr[i][type].reserve(loopEnd); + mAvgCDCAr[i][type].reserve(loopEnd); + mAvgADCAz[i][type].reserve(loopEnd); + mAvgCDCAz[i][type].reserve(loopEnd); + } + for (int type = 0; type < mMIPdEdxRatioA[i].size(); ++type) { + mMIPdEdxRatioA[i][type].reserve(loopEnd); + mMIPdEdxRatioC[i][type].reserve(loopEnd); + mTPCChi2A[i][type].reserve(loopEnd); + mTPCChi2C[i][type].reserve(loopEnd); + mTPCNClA[i][type].reserve(loopEnd); + mTPCNClC[i][type].reserve(loopEnd); + } + for (int j = 0; j < mAvgMeffA[i].size(); ++j) { + mAvgMeffA[i][j].reserve(loopEnd); + mAvgMeffC[i][j].reserve(loopEnd); + mAvgChi2MatchA[i][j].reserve(loopEnd); + mAvgChi2MatchC[i][j].reserve(loopEnd); + } + } + for (int iThread = 0; iThread < mNThreads; ++iThread) { + mBufferVals[iThread].front().reserve(loopEnd, 1); + mBufferVals[iThread].back().reserve(loopEnd, 0); + } + + using timer = std::chrono::high_resolution_clock; + auto startTotal = timer::now(); + + // loop over tracks and calculate DCAs + if (loopEnd < nTracks) { + // draw random tracks + std::vector ind(nTracks); + std::iota(ind.begin(), ind.end(), 0); + std::minstd_rand rng(std::time(nullptr)); + std::shuffle(ind.begin(), ind.end(), rng); + + auto myThread = [&](int iThread) { + for (size_t i = iThread; i < loopEnd; i += mNThreads) { + if (acceptTrack(tracksTPC[i])) { + fillDCA(tracksTPC, tracksITSTPC, vertices, i, iThread, indicesITSTPC); + } + } + }; + + std::vector threads(mNThreads); + for (int i = 0; i < mNThreads; i++) { + threads[i] = std::thread(myThread, i); + } + + for (auto& th : threads) { + th.join(); + } + } else { + auto myThread = [&](int iThread) { + for (size_t i = iThread; i < loopEnd; i += mNThreads) { + if (acceptTrack(tracksTPC[i])) { + fillDCA(tracksTPC, tracksITSTPC, vertices, i, iThread, indicesITSTPC); + } + } + }; + + std::vector threads(mNThreads); + for (int i = 0; i < mNThreads; i++) { + threads[i] = std::thread(myThread, i); + } + + for (auto& th : threads) { + th.join(); + } + } + + // fill DCA values from buffer + for (const auto& vals : mBufferVals) { + for (int type = 0; type < vals.size(); ++type) { + const auto& val = vals[type]; + const auto nPoints = val.side.size(); + for (int i = 0; i < nPoints; ++i) { + const auto tglBin = val.tglBin[i]; + const auto phiBin = val.phiBin[i]; + const auto qPtBin = val.qPtBin[i]; + const auto dcar = val.dcar[i]; + const auto dcaz = val.dcaz[i]; + const auto dcarW = val.dcarW[i]; + const int binInt = nBins - 1; + const bool fillCombDCA = ((type == 1) && (val.dcarcomb[i] != -1) && (val.dcazcomb[i] != -1)); + const std::array bins{tglBin, phiBin, qPtBin, binInt}; + // fill bins + for (auto bin : bins) { + if (val.side[i] == Side::C) { + mAvgCDCAr[bin][type].addValue(dcar, dcarW); + if (fillCombDCA) { + mAvgCDCAr[bin][2].addValue(val.dcarcomb[i], dcarW); + mAvgCDCAz[bin][2].addValue(val.dcazcomb[i], dcarW); + } + // fill only in case of valid value + if (dcaz != 0) { + mAvgCDCAz[bin][type].addValue(dcaz, dcarW); + } + } else { + mAvgADCAr[bin][type].addValue(dcar, dcarW); + if (fillCombDCA) { + mAvgADCAr[bin][2].addValue(val.dcarcomb[i], dcarW); + mAvgADCAz[bin][2].addValue(val.dcazcomb[i], dcarW); + } + // fill only in case of valid value + if (dcaz != 0) { + mAvgADCAz[bin][type].addValue(dcaz, dcarW); + } + } + } + } + } + } + + // calculate statistics and store values + // loop over TPC sides + for (int type = 0; type < 2; ++type) { + // loop over phi and tgl bins + for (int slice = 0; slice < nBins; ++slice) { + auto& bufferDCA = (type == 0) ? mBufferDCA.mTSTPC : mBufferDCA.mTSITSTPC; + + const auto dcaAr = mAvgADCAr[slice][type].filterPointsMedian(mCutDCA, mCutRMS); + bufferDCA.mDCAr_A_Median[slice] = std::get<0>(dcaAr); + bufferDCA.mDCAr_A_WeightedMean[slice] = std::get<1>(dcaAr); + bufferDCA.mDCAr_A_RMS[slice] = std::get<2>(dcaAr); + bufferDCA.mDCAr_A_NTracks[slice] = std::get<3>(dcaAr); + + const auto dcaAz = mAvgADCAz[slice][type].filterPointsMedian(mCutDCA, mCutRMS); + bufferDCA.mDCAz_A_Median[slice] = std::get<0>(dcaAz); + bufferDCA.mDCAz_A_WeightedMean[slice] = std::get<1>(dcaAz); + bufferDCA.mDCAz_A_RMS[slice] = std::get<2>(dcaAz); + bufferDCA.mDCAz_A_NTracks[slice] = std::get<3>(dcaAz); + + const auto dcaCr = mAvgCDCAr[slice][type].filterPointsMedian(mCutDCA, mCutRMS); + bufferDCA.mDCAr_C_Median[slice] = std::get<0>(dcaCr); + bufferDCA.mDCAr_C_WeightedMean[slice] = std::get<1>(dcaCr); + bufferDCA.mDCAr_C_RMS[slice] = std::get<2>(dcaCr); + bufferDCA.mDCAr_C_NTracks[slice] = std::get<3>(dcaCr); + + const auto dcaCz = mAvgCDCAz[slice][type].filterPointsMedian(mCutDCA, mCutRMS); + bufferDCA.mDCAz_C_Median[slice] = std::get<0>(dcaCz); + bufferDCA.mDCAz_C_WeightedMean[slice] = std::get<1>(dcaCz); + bufferDCA.mDCAz_C_RMS[slice] = std::get<2>(dcaCz); + bufferDCA.mDCAz_C_NTracks[slice] = std::get<3>(dcaCz); + // store combined ITS-TPC DCAs + if (type == 1) { + const auto dcaArComb = mAvgADCAr[slice][2].filterPointsMedian(mCutDCA, mCutRMS); + mBufferDCA.mDCAr_comb_A_Median[slice] = std::get<0>(dcaArComb); + mBufferDCA.mDCAr_comb_A_RMS[slice] = std::get<2>(dcaArComb); + + const auto dcaAzCom = mAvgADCAz[slice][2].filterPointsMedian(mCutDCA, mCutRMS); + mBufferDCA.mDCAz_comb_A_Median[slice] = std::get<0>(dcaAzCom); + mBufferDCA.mDCAz_comb_A_RMS[slice] = std::get<2>(dcaAzCom); + + const auto dcaCrComb = mAvgCDCAr[slice][2].filterPointsMedian(mCutDCA, mCutRMS); + mBufferDCA.mDCAr_comb_C_Median[slice] = std::get<0>(dcaCrComb); + mBufferDCA.mDCAr_comb_C_RMS[slice] = std::get<2>(dcaCrComb); + + const auto dcaCzComb = mAvgCDCAz[slice][2].filterPointsMedian(mCutDCA, mCutRMS); + mBufferDCA.mDCAz_comb_C_Median[slice] = std::get<0>(dcaCzComb); + mBufferDCA.mDCAz_comb_C_RMS[slice] = std::get<2>(dcaCzComb); + } + } + } + + // calculate matching eff + for (const auto& vals : mBufferVals) { + const auto& val = vals.front(); + const auto nPoints = val.side.size(); + for (int i = 0; i < nPoints; ++i) { + const auto tglBin = val.tglBin[i]; + const auto phiBin = val.phiBin[i]; + const auto qPtBin = val.qPtBin[i]; + const auto dcar = val.dcar[i]; + const auto dcaz = val.dcaz[i]; + const auto hasITS = val.hasITS[i]; + const auto chi2Match = val.chi2Match[i]; + const auto dedxRatio = val.dedxRatio[i]; + const auto sqrtChi2TPC = val.sqrtChi2TPC[i]; + const auto nClTPC = val.nClTPC[i]; + const int binInt = nBins - 1; + const Side side = val.side[i]; + const bool isCSide = (side == Side::C); + const auto& bufferDCARMSR = isCSide ? mBufferDCA.mTSTPC.mDCAr_C_RMS : mBufferDCA.mTSTPC.mDCAr_A_RMS; + const auto& bufferDCARMSZ = isCSide ? mBufferDCA.mTSTPC.mDCAz_C_RMS : mBufferDCA.mTSTPC.mDCAz_A_RMS; + const auto& bufferDCAMedR = isCSide ? mBufferDCA.mTSTPC.mDCAr_C_Median : mBufferDCA.mTSTPC.mDCAr_A_Median; + const auto& bufferDCAMedZ = isCSide ? mBufferDCA.mTSTPC.mDCAz_C_Median : mBufferDCA.mTSTPC.mDCAz_A_Median; + auto& mAvgEff = isCSide ? mAvgMeffC : mAvgMeffA; + auto& mAvgChi2Match = isCSide ? mAvgChi2MatchC : mAvgChi2MatchA; + auto& mAvgmMIPdEdxRatio = isCSide ? mMIPdEdxRatioC : mMIPdEdxRatioA; + auto& mAvgmTPCChi2 = isCSide ? mTPCChi2C : mTPCChi2A; + auto& mAvgmTPCNCl = isCSide ? mTPCNClC : mTPCNClA; + + const std::array bins{tglBin, phiBin, qPtBin, binInt}; + // fill bins + for (auto bin : bins) { + if ((std::abs(dcar - bufferDCAMedR[bin]) < (bufferDCARMSR[bin] * mCutRMS)) && (std::abs(dcaz - bufferDCAMedZ[bin]) < (bufferDCARMSZ[bin] * mCutRMS))) { + const auto gID = val.gID[i]; + mAvgEff[bin][0].addValue(hasITS); + // count tpc only tracks not matched + if (!hasITS) { + mAvgEff[bin][1].addValue(hasITS); + mAvgEff[bin][2].addValue(hasITS); + } + // count tracks from ITS standalone and afterburner + if (gID == o2::dataformats::GlobalTrackID::Source::ITS) { + mAvgEff[bin][1].addValue(hasITS); + } else if (gID == o2::dataformats::GlobalTrackID::Source::ITSAB) { + mAvgEff[bin][2].addValue(hasITS); + } + if (chi2Match > 0) { + mAvgChi2Match[bin][0].addValue(chi2Match); + if (gID == o2::dataformats::GlobalTrackID::Source::ITS) { + mAvgChi2Match[bin][1].addValue(chi2Match); + } else if (gID == o2::dataformats::GlobalTrackID::Source::ITSAB) { + mAvgChi2Match[bin][2].addValue(chi2Match); + } + } + if (dedxRatio > 0) { + mAvgmMIPdEdxRatio[bin][0].addValue(dedxRatio); + } + mAvgmTPCChi2[bin][0].addValue(sqrtChi2TPC); + mAvgmTPCNCl[bin][0].addValue(nClTPC); + if (hasITS) { + if (dedxRatio > 0) { + mAvgmMIPdEdxRatio[bin][1].addValue(dedxRatio); + } + mAvgmTPCChi2[bin][1].addValue(sqrtChi2TPC); + mAvgmTPCNCl[bin][1].addValue(nClTPC); + } + } + } + } + } + + // store matching eff + for (int slice = 0; slice < nBins; ++slice) { + for (int i = 0; i < mAvgMeffA[slice].size(); ++i) { + auto& itsBuf = (i == 0) ? mBufferDCA.mITSTPCAll : ((i == 1) ? mBufferDCA.mITSTPCStandalone : mBufferDCA.mITSTPCAfterburner); + itsBuf.mITSTPC_A_MatchEff[slice] = mAvgMeffA[slice][i].getMean(); + itsBuf.mITSTPC_C_MatchEff[slice] = mAvgMeffC[slice][i].getMean(); + itsBuf.mITSTPC_A_Chi2Match[slice] = mAvgChi2MatchA[slice][i].getMean(); + itsBuf.mITSTPC_C_Chi2Match[slice] = mAvgChi2MatchC[slice][i].getMean(); + } + + for (int i = 0; i < mMIPdEdxRatioC[slice].size(); ++i) { + auto& buff = (i == 0) ? mBufferDCA.mTSTPC : mBufferDCA.mTSITSTPC; + buff.mMIPdEdxRatioC[slice] = mMIPdEdxRatioC[slice][i].getMean(); + buff.mMIPdEdxRatioA[slice] = mMIPdEdxRatioA[slice][i].getMean(); + buff.mTPCChi2C[slice] = mTPCChi2C[slice][i].getMean(); + buff.mTPCChi2A[slice] = mTPCChi2A[slice][i].getMean(); + buff.mTPCNClC[slice] = mTPCNClC[slice][i].getMean(); + buff.mTPCNClA[slice] = mTPCNClA[slice][i].getMean(); + } + } + + auto stop = timer::now(); + std::chrono::duration time = stop - startTotal; + LOGP(detail, "DCA processing took {}", time.count()); + + // send data + sendOutput(pc); + } + + void endOfStream(EndOfStreamContext& eos) final + { + o2::utils::DebugStreamer::instance()->flush(); + eos.services().get().readyToQuit(QuitRequest::Me); + } + + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final + { + mTPCVDriftHelper.accountCCDBInputs(matcher, obj); + o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + } + + private: + /// buffer struct for multithreading + struct FillVals { + void reserve(int n, int type) + { + side.reserve(n); + tglBin.reserve(n); + phiBin.reserve(n); + qPtBin.reserve(n); + dcar.reserve(n); + dcaz.reserve(n); + dcarW.reserve(n); + dedxRatio.reserve(n); + sqrtChi2TPC.reserve(n); + nClTPC.reserve(n); + if (type == 1) { + hasITS.reserve(n); + chi2Match.reserve(n); + gID.reserve(n); + } else if (type == 0) { + dcarcomb.reserve(n); + dcazcomb.reserve(n); + } + } + + void clear() + { + side.clear(); + tglBin.clear(); + phiBin.clear(); + qPtBin.clear(); + dcar.clear(); + dcaz.clear(); + dcarW.clear(); + hasITS.clear(); + chi2Match.clear(); + dedxRatio.clear(); + sqrtChi2TPC.clear(); + nClTPC.clear(); + gID.clear(); + dcarcomb.clear(); + dcazcomb.clear(); + } + + void emplace_back(Side sideTmp, int tglBinTmp, int phiBinTmp, int qPtBinTmp, float dcarTmp, float dcazTmp, float dcarWTmp, float dedxRatioTmp, float sqrtChi2TPCTmp, float nClTPCTmp, o2::dataformats::GlobalTrackID::Source gIDTmp = o2::dataformats::GlobalTrackID::Source::NSources, float chi2MatchTmp = -2, int hasITSTmp = -1) + { + side.emplace_back(sideTmp); + tglBin.emplace_back(tglBinTmp); + phiBin.emplace_back(phiBinTmp); + qPtBin.emplace_back(qPtBinTmp); + dcar.emplace_back(dcarTmp); + dcaz.emplace_back(dcazTmp); + dcarW.emplace_back(dcarWTmp); + dedxRatio.emplace_back(dedxRatioTmp); + sqrtChi2TPC.emplace_back(sqrtChi2TPCTmp); + nClTPC.emplace_back(nClTPCTmp); + if (chi2MatchTmp != -2) { + chi2Match.emplace_back(chi2MatchTmp); + hasITS.emplace_back(hasITSTmp); + gID.emplace_back(gIDTmp); + } + } + + void emplace_back_ITSTPC(Side sideTmp, int tglBinTmp, int phiBinTmp, int qPtBinTmp, float dcarTmp, float dcazTmp, float dcarWTmp, float dedxRatioTmp, float sqrtChi2TPCTmp, float nClTPCTmp, float dcarCombTmp, float dcazCombTmp) + { + side.emplace_back(sideTmp); + tglBin.emplace_back(tglBinTmp); + phiBin.emplace_back(phiBinTmp); + qPtBin.emplace_back(qPtBinTmp); + dcar.emplace_back(dcarTmp); + dcaz.emplace_back(dcazTmp); + dcarW.emplace_back(dcarWTmp); + dedxRatio.emplace_back(dedxRatioTmp); + sqrtChi2TPC.emplace_back(sqrtChi2TPCTmp); + nClTPC.emplace_back(nClTPCTmp); + dcarcomb.emplace_back(dcarCombTmp); + dcazcomb.emplace_back(dcazCombTmp); + } + + std::vector side; + std::vector tglBin; + std::vector phiBin; + std::vector qPtBin; + std::vector dcar; + std::vector dcaz; + std::vector dcarW; + std::vector hasITS; + std::vector chi2Match; + std::vector dedxRatio; + std::vector sqrtChi2TPC; + std::vector nClTPC; + std::vector dcarcomb; + std::vector dcazcomb; + std::vector gID; + }; + std::shared_ptr mCCDBRequest; ///< info for CCDB request + const bool mDisableWriter{false}; ///< flag if no ROOT output will be written + o2::base::Propagator::MatCorrType mMatType; ///< material for propagation + int mPhiBins = SECTORSPERSIDE; ///< number of phi bins + int mTglBins{3}; ///< number of tgl bins + int mQPtBins{20}; ///< number of qPt bins + TimeSeriesITSTPC mBufferDCA; ///< buffer for integrate DCAs + std::vector> mAvgADCAr; ///< for averaging the DCAr for TPC and ITS-TPC tracks for A-side + std::vector> mAvgCDCAr; ///< for averaging the DCAr for TPC and ITS-TPC tracks for C-side + std::vector> mAvgADCAz; ///< for averaging the DCAz for TPC and ITS-TPC tracks for A-side + std::vector> mAvgCDCAz; ///< for averaging the DCAz for TPC and ITS-TPC tracks for C-side + std::vector> mMIPdEdxRatioA; ///< for averaging MIP/dEdx + std::vector> mMIPdEdxRatioC; ///< for averaging MIP/dEdx + std::vector> mTPCChi2A; ///< for averaging chi2 TPC A + std::vector> mTPCChi2C; ///< for averaging chi2 TPC C + std::vector> mTPCNClA; ///< for averaging number of cluster A + std::vector> mTPCNClC; ///< for averaging number of cluster C + std::vector> mAvgMeffA; ///< for matching efficiency ITS-TPC standalone + afterburner, standalone, afterburner + std::vector> mAvgMeffC; ///< for matching efficiency ITS-TPC standalone + afterburner, standalone, afterburner + std::vector> mAvgChi2MatchA; ///< for matching efficiency ITS-TPC standalone + afterburner, standalone, afterburner + std::vector> mAvgChi2MatchC; ///< for matching efficiency ITS-TPC standalone + afterburner, standalone, afterburner + int mNMaxTracks{-1}; ///< maximum number of tracks to process + float mMinMom{1}; ///< minimum accepted momentum + int mMinNCl{80}; ///< minimum accepted number of clusters per track + float mMaxTgl{1}; ///< maximum eta + float mMaxQPtBin{5}; ///< max qPt bin + float mCoarseStep{1}; ///< coarse step during track propagation + float mFineStep{0.005}; ///< fine step during track propagation + float mCutDCA{5}; ///< cut on the abs(DCA-median) + float mCutRMS{5}; ///< sigma cut for mean,median calculation + float mRefXSec{108.475}; ///< reference lx position for sector information (default centre of IROC) + int mNThreads{1}; ///< number of parallel threads + float maxITSTPCDCAr{0.2}; ///< maximum abs DCAr value for ITS-TPC tracks + float maxITSTPCDCAz{10}; ///< maximum abs DCAz value for ITS-TPC tracks + float maxITSTPCDCAr_comb{0.2}; ///< max abs DCA for ITS-TPC DCA to vertex + float maxITSTPCDCAz_comb{0.2}; ///< max abs DCA for ITS-TPC DCA to vertex + gsl::span mTPCTrackClIdx{}; ///< cluster refs for debugging + std::vector> mBufferVals; ///< buffer for multithreading + uint32_t mFirstTFOrbit{0}; ///< first TF orbit + float mTimeWindowMUS{50}; ///< time window in mus for local mult estimate + float mMIPdEdx{50}; ///< MIP dEdx position for MIP/dEdx monitoring + std::vector mNTracksWindow; ///< number of tracks in time window + std::vector mNearestVtxTPC; ///< nearest vertex for tpc tracks + o2::tpc::VDriftHelper mTPCVDriftHelper{}; ///< helper for v-drift + float mVDrift{2.64}; ///< v drift in mus + bool mUseQMax{false}; ///< use dedx qmax for MIP/dEdx monitoring + float mMaxSnp{0.85}; ///< max sinus phi for propagation + float mXCoarse{40}; ///< perform propagation with coarse steps up to this mx + float mSqrt{13600}; ///< centre of mass energy + + /// check if track passes coarse cuts + bool acceptTrack(const TrackTPC& track) const + { + if ((track.getP() < mMinMom) || (track.getNClusters() < mMinNCl) || std::abs(track.getTgl()) > mMaxTgl) { + return false; + } + return true; + } + + void fillDCA(const gsl::span tracksTPC, const gsl::span tracksITSTPC, const gsl::span vertices, const int iTrk, const int iThread, const std::unordered_map>& indicesITSTPC) + { + TrackTPC track = tracksTPC[iTrk]; + + // propagate track to the DCA and fill in slice + auto propagator = o2::base::Propagator::Instance(); + + // propagate track to DCA + o2::gpu::gpustd::array dca; + const o2::math_utils::Point3D refPoint{0, 0, 0}; + + // coarse propagation + if (!propagator->PropagateToXBxByBz(track, mXCoarse, mMaxSnp, mCoarseStep, mMatType)) { + return; + } + + // fine propagation with Bz only + if (!propagator->propagateToDCA(refPoint, track, propagator->getNominalBz(), mFineStep, mMatType, &dca)) { + return; + } + + TrackTPC trackTmp = tracksTPC[iTrk]; + + // coarse propagation to centre of IROC for phi bin + if (!propagator->propagateTo(trackTmp, mRefXSec, false, mMaxSnp, mCoarseStep, mMatType)) { + return; + } + + const int tglBin = mTglBins * std::abs(trackTmp.getTgl()) / mMaxTgl + mPhiBins; + const int phiBin = mPhiBins * trackTmp.getPhi() / o2::constants::math::TwoPI; + const int qPtBin = mPhiBins + mTglBins + mQPtBins * (trackTmp.getQ2Pt() + mMaxQPtBin) / (2 * mMaxQPtBin); + const int nBins = getNBins(); + + if ((phiBin < 0) || (phiBin > mPhiBins) || (tglBin < mPhiBins) || (tglBin > (mPhiBins + mTglBins)) || (qPtBin < (mPhiBins + mTglBins)) || (qPtBin > (mPhiBins + mTglBins + mQPtBins))) { + return; + } + + const int sector = o2::math_utils::angle2Sector(trackTmp.getPhiPos()); + if (sector < SECTORSPERSIDE) { + // find possible ITS-TPC track and vertex index + auto it = indicesITSTPC.find(iTrk); + const auto idxITSTPC = (it != indicesITSTPC.end()) ? (it->second) : std::array{-1, -1}; + + // get vertex (check if vertex ID is valid). In case no vertex is assigned return nearest vertex or else default vertex + const auto vertex = (idxITSTPC.back() != -1) ? vertices[idxITSTPC.back()] : ((mNearestVtxTPC[iTrk] != -1) ? vertices[mNearestVtxTPC[iTrk]] : o2::dataformats::PrimaryVertex{}); + + // calculate DCAz: (time TPC track - time vertex) * vDrift + sign_side * vertexZ + const float signSide = track.hasCSideClustersOnly() ? -1 : 1; // invert sign for C-side + const float dcaZFromDeltaTime = (vertex.getTimeStamp().getTimeStamp() == 0) ? 0 : (o2::tpc::ParameterElectronics::Instance().ZbinWidth * track.getTime0() - vertex.getTimeStamp().getTimeStamp()) * mVDrift + signSide * vertex.getZ(); + + // for weight of DCA + const float resCl = std::min(track.getNClusters(), static_cast(Mapper::PADROWS)) / static_cast(Mapper::PADROWS); + + const float div = (resCl * track.getPt()); + if (div == 0) { + return; + } + + const float fB = 0.2 / div; + const float fA = 0.15 + 0.15; // = 0.15 with additional misalignment error + const float dcarW = 1. / std::sqrt(fA * fA + fB * fB); // Weight of DCA: Rms2 ~ 0.15^2 + k/(L^2*pt) → 0.15**2 + (0.2/((NCl/152)*pt)^2); + + // store values for TPC DCA only for A- or C-side only tracks + const bool hasITSTPC = idxITSTPC.front() != -1; + + // get ratio of chi2 in case ITS-TPC track has been found + const float chi2 = hasITSTPC ? tracksITSTPC[idxITSTPC.front()].getChi2Match() : -1; + auto gID = o2::dataformats::GlobalTrackID::Source::TPC; // source + // check for source in case of ITS-TPC + if (hasITSTPC) { + const auto src = tracksITSTPC[idxITSTPC.front()].getRefITS().getSource(); + if (src == o2::dataformats::GlobalTrackID::ITS) { + gID = o2::dataformats::GlobalTrackID::Source::ITS; + } else if (src == o2::dataformats::GlobalTrackID::ITSAB) { + gID = o2::dataformats::GlobalTrackID::Source::ITSAB; + } + } + + const float chi2Match = (chi2 > 0) ? std::sqrt(chi2) : -1; + const float dedx = mUseQMax ? track.getdEdx().dEdxMaxTPC : track.getdEdx().dEdxTotTPC; + const float dedxRatio = (dedx > 0) ? (mMIPdEdx / dedx) : -1; + const float sqrtChi2TPC = (track.getChi2() > 0) ? std::sqrt(track.getChi2()) : 0; + const float nClTPC = track.getNClusters(); + + if (track.hasCSideClustersOnly()) { + mBufferVals[iThread].front().emplace_back(Side::C, tglBin, phiBin, qPtBin, dca[0], dcaZFromDeltaTime, dcarW, dedxRatio, sqrtChi2TPC, nClTPC, gID, chi2Match, hasITSTPC); + } else if (track.hasASideClustersOnly()) { + mBufferVals[iThread].front().emplace_back(Side::A, tglBin, phiBin, qPtBin, dca[0], dcaZFromDeltaTime, dcarW, dedxRatio, sqrtChi2TPC, nClTPC, gID, chi2Match, hasITSTPC); + } + + // check if the track was assigned to ITS track + o2::gpu::gpustd::array dcaITSTPC{0, 0}; + if (hasITSTPC) { + // propagate ITS-TPC track to (0,0) + auto trackITSTPCTmp = tracksITSTPC[idxITSTPC.front()]; + // fine propagation with Bz only + if (propagator->PropagateToXBxByBz(trackITSTPCTmp, mXCoarse, mMaxSnp, mCoarseStep, mMatType) && propagator->propagateToDCA(refPoint, trackITSTPCTmp, propagator->getNominalBz(), mFineStep, mMatType, &dcaITSTPC)) { + // make cut on abs(DCA) + if ((std::abs(dcaITSTPC[0]) < maxITSTPCDCAr) && (std::abs(dcaITSTPC[1]) < maxITSTPCDCAz)) { + // store TPC only DCAs + // propagate to vertex in case the track belongs to vertex + const bool contributeToVertex = (idxITSTPC.back() != -1); + o2::gpu::gpustd::array dcaITSTPCTmp{-1, -1}; + + if (contributeToVertex) { + propagator->propagateToDCA(vertex.getXYZ(), trackITSTPCTmp, propagator->getNominalBz(), mFineStep, mMatType, &dcaITSTPCTmp); + } + + // make cut around DCA to vertex due to gammas + if ((std::abs(dcaITSTPCTmp[0]) < maxITSTPCDCAr_comb) && (std::abs(dcaITSTPCTmp[1]) < maxITSTPCDCAz_comb)) { + dcaITSTPCTmp[0] = -1; + dcaITSTPCTmp[1] = -1; + } + + if (track.hasCSideClustersOnly()) { + mBufferVals[iThread].back().emplace_back_ITSTPC(Side::C, tglBin, phiBin, qPtBin, dca[0], dcaZFromDeltaTime, dcarW, dedxRatio, sqrtChi2TPC, nClTPC, dcaITSTPCTmp[0], dcaITSTPCTmp[1]); + } else if (track.hasASideClustersOnly()) { + mBufferVals[iThread].back().emplace_back_ITSTPC(Side::A, tglBin, phiBin, qPtBin, dca[0], dcaZFromDeltaTime, dcarW, dedxRatio, sqrtChi2TPC, nClTPC, dcaITSTPCTmp[0], dcaITSTPCTmp[1]); + } + } + } + } + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamTimeSeries)) { + const auto sampling = o2::utils::DebugStreamer::getSamplingTypeFrequency(o2::utils::StreamFlags::streamTimeSeries); + const float factorPt = sampling.second; + bool writeData = true; + float weight = 0; + if (sampling.first == o2::utils::SamplingTypes::sampleTsalis) { + writeData = o2::utils::DebugStreamer::downsampleTsalisCharged(tracksTPC[iTrk].getPt(), factorPt, mSqrt, weight, o2::utils::DebugStreamer::getRandom()); + } + if (writeData) { + auto clusterMask = makeClusterBitMask(track); + const auto& trkOrig = tracksTPC[iTrk]; + const bool isNearestVtx = (idxITSTPC.back() == -1); // is nearest vertex in case no vertex was found + const float mx_ITS = hasITSTPC ? tracksITSTPC[idxITSTPC.front()].getX() : -1; + int typeSide = 2; // A- and C-Side cluster + if (track.hasASideClustersOnly()) { + typeSide = 0; + } else if (track.hasCSideClustersOnly()) { + typeSide = 1; + } + + o2::utils::DebugStreamer::instance()->getStreamer("time_series", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("treeTimeSeries").data() + // DCAs + << "factorPt=" << factorPt + << "weight=" << weight + << "dcar_tpc=" << dca[0] + << "dcaz_tpc=" << dca[1] + << "dcar_itstpc=" << dcaITSTPC[0] + << "dcaz_itstpc=" << dcaITSTPC[1] + << "dcarW=" << dcarW + << "dcaZFromDeltaTime=" << dcaZFromDeltaTime + << "hasITSTPC=" << hasITSTPC + // vertex + << "vertex_x=" << vertex.getX() + << "vertex_y=" << vertex.getY() + << "vertex_z=" << vertex.getZ() + << "vertex_time=" << vertex.getTimeStamp().getTimeStamp() + << "vertex_nContributors=" << vertex.getNContributors() + << "isNearestVertex=" << isNearestVtx + // tpc track properties + << "pt=" << trkOrig.getPt() + << "tpc_timebin=" << trkOrig.getTime0() + << "qpt=" << trkOrig.getParam(4) + << "ncl=" << trkOrig.getNClusters() + << "tgl=" << trkOrig.getTgl() + << "side_type=" << typeSide + << "phi=" << trkOrig.getPhi() + << "clusterMask=" << clusterMask + << "dedxTotTPC=" << trkOrig.getdEdx().dEdxTotTPC + << "dedxTotIROC=" << trkOrig.getdEdx().dEdxTotIROC + << "dedxTotOROC1=" << trkOrig.getdEdx().dEdxTotOROC1 + << "dedxTotOROC2=" << trkOrig.getdEdx().dEdxTotOROC2 + << "dedxTotOROC3=" << trkOrig.getdEdx().dEdxTotOROC3 + << "chi2=" << trkOrig.getChi2() + << "mX=" << trkOrig.getX() + << "mX_ITS=" << mx_ITS + // meta + << "mult=" << mNTracksWindow[iTrk] + << "time_window_mult=" << mTimeWindowMUS + << "firstTFOrbit=" << mFirstTFOrbit + << "mVDrift=" << mVDrift + << "\n"; + } + }) + } + } + + void sendOutput(ProcessingContext& pc) + { + pc.outputs().snapshot(Output{header::gDataOriginTPC, getDataDescriptionTimeSeries()}, mBufferDCA); + // in case of ROOT output also store the TFinfo in the TTree + if (!mDisableWriter) { + o2::dataformats::TFIDInfo tfinfo; + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, tfinfo); + const long timeMS = o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS() + processing_helpers::getFirstTForbit(pc) * o2::constants::lhc::LHCOrbitMUS / 1000; + mBufferDCA.mTSTPC.setStartTime(timeMS); + mBufferDCA.mTSITSTPC.setStartTime(timeMS); + pc.outputs().snapshot(Output{header::gDataOriginTPC, getDataDescriptionTPCTimeSeriesTFId()}, tfinfo); + } + } + + /// find for tpc tracks the nearest vertex + void findNearesVertex(const gsl::span tracksTPC, const gsl::span vertices) + { + // create list of time bins of tracks + const int nVertices = vertices.size(); + + const int nTracks = tracksTPC.size(); + mNearestVtxTPC.clear(); + mNearestVtxTPC.resize(nTracks); + + // in case no vertices are found + if (!nVertices) { + std::fill(mNearestVtxTPC.begin(), mNearestVtxTPC.end(), -1); + return; + } + + // store timestamps of vertices. Assume vertices are already sorted in time! + std::vector times_vtx; + times_vtx.reserve(nVertices); + for (const auto& vtx : vertices) { + times_vtx.emplace_back(vtx.getTimeStamp().getTimeStamp()); + } + + // loop over tpc tracks and find nearest vertex + auto myThread = [&](int iThread) { + for (int i = iThread; i < nTracks; i += mNThreads) { + const float timeTrack = o2::tpc::ParameterElectronics::Instance().ZbinWidth * tracksTPC[i].getTime0(); + const auto lower = std::lower_bound(times_vtx.begin(), times_vtx.end(), timeTrack); + int closestVtx = std::distance(times_vtx.begin(), lower); + // if value is out of bounds use last value + if (closestVtx == nVertices) { + closestVtx -= 1; + } else if (closestVtx > 0) { + // if idx > 0 check preceeding value + double diff1 = std::abs(timeTrack - *lower); + double diff2 = std::abs(timeTrack - *(lower - 1)); + if (diff2 < diff1) { + closestVtx -= 1; + } + } + mNearestVtxTPC[i] = closestVtx; + } + }; + + std::vector threads(mNThreads); + for (int i = 0; i < mNThreads; i++) { + threads[i] = std::thread(myThread, i); + } + + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + } + + /// make bit mask of clusters + std::vector makeClusterBitMask(const TrackTPC& track) const + { + std::vector tpcClusterMask(Mapper::PADROWS, false); + const int nCl = track.getNClusterReferences(); + for (int j = 0; j < nCl; ++j) { + uint8_t sector, padrow; + uint32_t clusterIndexInRow; + track.getClusterReference(mTPCTrackClIdx, j, sector, padrow, clusterIndexInRow); + tpcClusterMask[padrow] = true; + } + return tpcClusterMask; + } + + /// find number of neighbouring tracks for mult estimate + void findNNeighbourTracks(const gsl::span tracksTPC) + { + const float tpcTBinMUS = o2::tpc::ParameterElectronics::Instance().ZbinWidth; // 0.199606f; time bin in MUS + const float windowTimeBins = mTimeWindowMUS / tpcTBinMUS; // number of neighbouring time bins to check + + // create list of time bins of tracks + std::vector times; + const int nTracks = tracksTPC.size(); + times.reserve(nTracks); + for (const auto& trk : tracksTPC) { + times.emplace_back(trk.getTime0()); + } + std::sort(times.begin(), times.end()); + + mNTracksWindow.clear(); + mNTracksWindow.resize(nTracks); + + // loop over tpc tracks and count number of neighouring tracks + auto myThread = [&](int iThread) { + for (int i = iThread; i < nTracks; i += mNThreads) { + const float t0 = tracksTPC[i].getTime0(); + const auto upperV0 = std::upper_bound(times.begin(), times.end(), t0 + windowTimeBins); + const auto lowerV0 = std::lower_bound(times.begin(), times.end(), t0 - windowTimeBins); + const int nMult = std::distance(times.begin(), upperV0) - std::distance(times.begin(), lowerV0); + mNTracksWindow[i] = nMult; + } + }; + + std::vector threads(mNThreads); + for (int i = 0; i < mNThreads; i++) { + threads[i] = std::thread(myThread, i); + } + + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + } + + /// \return returns total number of stored values per TF + int getNBins() const { return mBufferDCA.mTSTPC.getNBins(); } +}; + +o2::framework::DataProcessorSpec getTPCTimeSeriesSpec(const bool disableWriter, const o2::base::Propagator::MatCorrType matType) +{ + const bool enableAskMatLUT = matType == o2::base::Propagator::MatCorrType::USEMatCorrLUT; + std::vector inputs; + inputs.emplace_back("tracksITSTPC", "GLO", "TPCITS", 0, Lifetime::Timeframe); + inputs.emplace_back("tracksTPC", header::gDataOriginTPC, "TRACKS", 0, Lifetime::Timeframe); + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamTimeSeries)) { + // request tpc clusters only in case the debug streamer is used for the cluster bit mask + inputs.emplace_back("trackTPCClRefs", header::gDataOriginTPC, "CLUSREFS", 0, Lifetime::Timeframe); + }) + inputs.emplace_back("pvtx", "GLO", "PVTX", 0, Lifetime::Timeframe); + inputs.emplace_back("pvtx_trmtc", "GLO", "PVTX_TRMTC", 0, Lifetime::Timeframe); // global ids of associated tracks + inputs.emplace_back("pvtx_tref", "GLO", "PVTX_TRMTCREFS", 0, Lifetime::Timeframe); // vertex - trackID refs + + auto ccdbRequest = std::make_shared(!disableWriter, // orbitResetTime + false, // GRPECS=true for nHBF per TF + false, // GRPLHCIF + true, // GRPMagField + enableAskMatLUT, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true, + true); + + o2::tpc::VDriftHelper::requestCCDBInputs(inputs); + std::vector outputs; + outputs.emplace_back(o2::header::gDataOriginTPC, getDataDescriptionTimeSeries(), 0, Lifetime::Sporadic); + if (!disableWriter) { + outputs.emplace_back(o2::header::gDataOriginTPC, getDataDescriptionTPCTimeSeriesTFId(), 0, Lifetime::Sporadic); + } + + return DataProcessorSpec{ + "tpc-time-series", + inputs, + outputs, + AlgorithmSpec{adaptFromTask(ccdbRequest, disableWriter, matType)}, + Options{{"min-momentum", VariantType::Float, 0.2f, {"Minimum momentum of the tracks"}}, + {"min-cluster", VariantType::Int, 80, {"Minimum number of clusters of the tracks"}}, + {"max-tgl", VariantType::Float, 1.1f, {"Maximum accepted tgl of the tracks"}}, + {"max-qPt-bin", VariantType::Float, 5.f, {"Maximum abs(qPt) bin"}}, + {"max-snp", VariantType::Float, 0.85f, {"Maximum sinus(phi) for propagation"}}, + {"coarse-step", VariantType::Float, 5.f, {"Coarse step during track propagation"}}, + {"fine-step", VariantType::Float, 2.f, {"Fine step during track propagation"}}, + {"mX-coarse", VariantType::Float, 40.f, {"Perform coarse propagation up to this mx"}}, + {"max-tracks", VariantType::Int, -1, {"Number of maximum tracks to process"}}, + {"cut-DCA-median", VariantType::Float, 3.f, {"Cut on the DCA: abs(DCA-medianDCA) +#include "TPCWorkflow/TPCTimeSeriesWriterSpec.h" +#include "TPCWorkflow/TPCTimeSeriesSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "CommonDataFormat/TFIDInfo.h" + +#include "DetectorsCalibration/IntegratedClusterCalibrator.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tpc +{ +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; + +DataProcessorSpec getTPCTimeSeriesWriterSpec() +{ + return MakeRootTreeWriterSpec("tpc-time-series-writer", + "o2_timeseries_tpc.root", + "treeTimeSeries", + BranchDefinition{InputSpec{"timeseries", o2::header::gDataOriginTPC, getDataDescriptionTimeSeries(), 0}, "TimeSeries", 1}, + BranchDefinition{InputSpec{"itpctfid", o2::header::gDataOriginTPC, getDataDescriptionTPCTimeSeriesTFId(), 0}, "tfID", 1})(); +} + +} // namespace tpc +} // namespace o2 diff --git a/Detectors/TPC/workflow/src/TPCTriggerWriterSpec.cxx b/Detectors/TPC/workflow/src/TPCTriggerWriterSpec.cxx new file mode 100644 index 0000000000000..769185bfca2f0 --- /dev/null +++ b/Detectors/TPC/workflow/src/TPCTriggerWriterSpec.cxx @@ -0,0 +1,37 @@ +// 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 TPCIntegrateClusterWriterSpec.cxx + +#include +#include "TPCWorkflow/TPCTriggerWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsTPC/ZeroSuppression.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace tpc +{ +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; + +DataProcessorSpec getTPCTriggerWriterSpec() +{ + return MakeRootTreeWriterSpec("tpc-trigger-writer", + "tpctriggers.root", + "triggers", + BranchDefinition>{InputSpec{"trig", o2::header::gDataOriginTPC, "TRIGGERWORDS", 0}, "Triggers"})(); +} + +} // namespace tpc +} // namespace o2 diff --git a/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx b/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx new file mode 100644 index 0000000000000..c17b68e307328 --- /dev/null +++ b/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx @@ -0,0 +1,34 @@ +// 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 "TPCWorkflow/TPCMergeTimeSeriesSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + std::vector options{ + ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + }; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); + wf.emplace_back(o2::tpc::getTPCMergeTimeSeriesSpec()); + return wf; +} diff --git a/Detectors/TPC/workflow/src/time-series-reader.cxx b/Detectors/TPC/workflow/src/time-series-reader.cxx new file mode 100644 index 0000000000000..ccedbdf4f9599 --- /dev/null +++ b/Detectors/TPC/workflow/src/time-series-reader.cxx @@ -0,0 +1,25 @@ +// 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 "TPCWorkflow/TPCTimeSeriesReaderSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" + +using namespace o2::framework; + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) +{ + WorkflowSpec wf; + wf.emplace_back(o2::tpc::getTPCTimeSeriesReaderSpec()); + return wf; +} diff --git a/Detectors/TPC/workflow/src/tpc-calib-dEdx.cxx b/Detectors/TPC/workflow/src/tpc-calib-dEdx.cxx index 93e790f4cd414..ea25e04e14528 100644 --- a/Detectors/TPC/workflow/src/tpc-calib-dEdx.cxx +++ b/Detectors/TPC/workflow/src/tpc-calib-dEdx.cxx @@ -19,6 +19,7 @@ void customize(std::vector& workflowOptions) std::vector options{ {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"material-type", VariantType::Int, 2, {"Type for the material budget during track propagation: 0=None, 1=Geo, 2=LUT"}}, }; std::swap(workflowOptions, options); @@ -33,7 +34,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); o2::conf::ConfigurableParam::writeINI("o2tpccalibration_configuration.ini"); + auto materialType = static_cast(config.options().get("material-type")); using namespace o2::tpc; - return WorkflowSpec{getCalibdEdxSpec()}; + return WorkflowSpec{getCalibdEdxSpec(materialType)}; } diff --git a/Detectors/TPC/workflow/src/tpc-calib-laser-tracks.cxx b/Detectors/TPC/workflow/src/tpc-calib-laser-tracks.cxx index b69308e2bd109..68481f86cbc5d 100644 --- a/Detectors/TPC/workflow/src/tpc-calib-laser-tracks.cxx +++ b/Detectors/TPC/workflow/src/tpc-calib-laser-tracks.cxx @@ -13,6 +13,7 @@ #include "TPCWorkflow/CalibLaserTracksSpec.h" #include "Framework/CompletionPolicy.h" #include "Framework/CompletionPolicyHelpers.h" +#include "CommonUtils/ConfigurableParam.h" using namespace o2::framework; @@ -22,6 +23,8 @@ void customize(std::vector& workflowOptions) std::vector options{ {"input-spec", VariantType::String, "input:TPC/TRACKS", {"selection string input specs"}}, {"use-filtered-tracks", VariantType::Bool, false, {"use prefiltered laser tracks as input"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, }; std::swap(workflowOptions, options); @@ -33,6 +36,10 @@ void customize(std::vector& workflowOptions) WorkflowSpec defineDataProcessing(ConfigContext const& config) { + // set up configuration + o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + WorkflowSpec specs; std::string inputSpec = config.options().get("input-spec"); if (config.options().get("use-filtered-tracks")) { diff --git a/Detectors/TPC/workflow/src/tpc-calibrator-dEdx.cxx b/Detectors/TPC/workflow/src/tpc-calibrator-dEdx.cxx index 5ff36f695bba9..54fcc4621bef3 100644 --- a/Detectors/TPC/workflow/src/tpc-calibrator-dEdx.cxx +++ b/Detectors/TPC/workflow/src/tpc-calibrator-dEdx.cxx @@ -19,6 +19,7 @@ void customize(std::vector& workflowOptions) std::vector options{ {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"material-type", VariantType::Int, 2, {"Type for the material budget during track propagation: 0=None, 1=Geo, 2=LUT"}}, }; std::swap(workflowOptions, options); @@ -33,7 +34,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); o2::conf::ConfigurableParam::writeINI("o2tpccalibration_configuration.ini"); + auto materialType = static_cast(config.options().get("material-type")); using namespace o2::tpc; - return WorkflowSpec{getCalibratordEdxSpec()}; + return WorkflowSpec{getCalibratordEdxSpec(materialType)}; } diff --git a/Detectors/TPC/workflow/src/tpc-laser-track-filter.cxx b/Detectors/TPC/workflow/src/tpc-laser-track-filter.cxx index a1f1ef75ff3ba..7b31bf80b8874 100644 --- a/Detectors/TPC/workflow/src/tpc-laser-track-filter.cxx +++ b/Detectors/TPC/workflow/src/tpc-laser-track-filter.cxx @@ -12,8 +12,6 @@ #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamSpec.h" -#include "Framework/CompletionPolicy.h" -#include "Framework/CompletionPolicyHelpers.h" #include "TPCWorkflow/LaserTrackFilterSpec.h" #include "DataFormatsTPC/TrackTPC.h" @@ -47,7 +45,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) const char* defaultFileName = "tpc-laser-tracks.root"; const char* defaultTreeName = "tpcrec"; - //branch definitions for RootTreeWriter spec + // branch definitions for RootTreeWriter spec using TrackOutputType = std::vector; // a spectator callback which will be invoked by the tree writer with the extracted object diff --git a/Detectors/TPC/workflow/src/tpc-occupancy-filter.cxx b/Detectors/TPC/workflow/src/tpc-occupancy-filter.cxx new file mode 100644 index 0000000000000..f71d0b837f783 --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-occupancy-filter.cxx @@ -0,0 +1,220 @@ +// 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 +#include +#include +#include + +#include "Algorithm/RangeTokenizer.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "CommonUtils/ConfigurableParam.h" + +#include "CCDB/BasicCCDBManager.h" +#include "DataFormatsTPC/TPCSectorHeader.h" +#include "DataFormatsTPC/Digit.h" +#include "TPCBase/Sector.h" +#include "TPCWorkflow/OccupancyFilterSpec.h" +#include "TPCWorkflow/FileWriterSpec.h" +#include "TPCReaderWorkflow/TPCSectorCompletionPolicy.h" + +using namespace o2::framework; +using namespace o2::tpc; + +// Global variable used to transport data to the completion policy +std::vector gPolicyData; +unsigned long gTpcSectorMask = 0xFFFFFFFFF; + +// customize the completion policy +void customize(std::vector& policies) +{ + using o2::framework::CompletionPolicy; + // policies.push_back(CompletionPolicyHelpers::defineByName("tpc-krypton-raw-filter.*", CompletionPolicy::CompletionOp::Consume)); + // policies.push_back(CompletionPolicyHelpers::defineByName("file-writer", CompletionPolicy::CompletionOp::Consume)); + + // we customize the pipeline processors to consume data as it comes + // + // the custom completion policy for the tracker + policies.push_back(o2::tpc::TPCSectorCompletionPolicy("occupancy-file-writer", o2::tpc::TPCSectorCompletionPolicy::Config::RequireAll, &gPolicyData, &gTpcSectorMask)()); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::string sectorDefault = "0-" + std::to_string(o2::tpc::Sector::MAXSECTOR - 1); + int defaultlanes = std::max(1u, std::thread::hardware_concurrency() / 2); + + std::vector options{ + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"outputFile", VariantType::String, "./occupancy-filtered-digits.root", {"output file name for the filtered krypton file"}}, + {"lanes", VariantType::Int, defaultlanes, {"Number of parallel processing lanes."}}, + {"sectors", VariantType::String, sectorDefault.c_str(), {"List of TPC sectors, comma separated ranges, e.g. 0-3,7,9-15"}}, + {"writer-type", VariantType::String, "local", {"Writer type (local, EPN, none)"}}, + {"ccdb-path", VariantType::String, "http://ccdb-test.cern.ch:8080", {"Path to CCDB"}}, + }; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; + +enum class WriterType { + Local, + EPN, + None, +}; + +const std::unordered_map WriterMap{ + {"local", WriterType::Local}, + {"EPN", WriterType::EPN}, + {"none", WriterType::None}, +}; + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + + using namespace o2::tpc; + + // set up configuration + o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2tpccalibration_configuration.ini"); + + const std::string outputFile = config.options().get("outputFile"); + + const auto tpcSectors = o2::RangeTokenizer::tokenize(config.options().get("sectors")); + const auto nSectors = (int)tpcSectors.size(); + const auto nLanes = std::min(config.options().get("lanes"), nSectors); + + WriterType writerType; + try { + writerType = WriterMap.at(config.options().get("writer-type")); + } catch (std::out_of_range&) { + throw std::invalid_argument(std::string("invalid writer-type type: ") + config.options().get("writer-type")); + } + + WorkflowSpec workflow; + + if (nLanes <= 0) { + return workflow; + } + + std::vector laneConfiguration = tpcSectors; // Currently just a copy of the tpcSectors, why? + + gTpcSectorMask = 0; + for (auto s : tpcSectors) { + gTpcSectorMask |= (1ul << s); + } + gPolicyData.emplace_back(o2::framework::InputSpec{"data", o2::framework::ConcreteDataTypeMatcher{"TPC", "FILTERDIG"}}); + + WorkflowSpec parallelProcessors; + parallelProcessors.emplace_back(getOccupancyFilterSpec()); + + parallelProcessors = parallelPipeline( + parallelProcessors, nLanes, + [&laneConfiguration]() { return laneConfiguration.size(); }, + [&laneConfiguration](size_t index) { return laneConfiguration[index]; }); + workflow.insert(workflow.end(), parallelProcessors.begin(), parallelProcessors.end()); + + if (writerType == WriterType::Local) { + ////////////////////////////////////////////////////////////////////////////////////////////// + // + // generation of processor specs for various types of outputs + // based on generic RootTreeWriter and MakeRootTreeWriterSpec generator + // + // ------------------------------------------------------------------------------------------- + // the callbacks for the RootTreeWriter + // + // The generic writer needs a way to associate incoming data with the individual branches for + // the TPC sectors. The sector number is transmitted as part of the sector header, the callback + // finds the corresponding index in the vector of configured sectors + auto getIndex = [tpcSectors](o2::framework::DataRef const& ref) { + auto const* tpcSectorHeader = o2::framework::DataRefUtils::getHeader(ref); + if (!tpcSectorHeader) { + throw std::runtime_error("TPC sector header missing in header stack"); + } + if (tpcSectorHeader->sector() < 0) { + // special data sets, don't write + return ~(size_t)0; + } + size_t index = 0; + for (auto const& sector : tpcSectors) { + if (sector == tpcSectorHeader->sector()) { + return index; + } + ++index; + } + throw std::runtime_error("sector " + std::to_string(tpcSectorHeader->sector()) + " not configured for writing"); + }; + auto getName = [tpcSectors](std::string base, size_t index) { + return base + "_" + std::to_string(tpcSectors.at(index)); + }; + + auto makeWriterSpec = [tpcSectors, laneConfiguration, getIndex, getName](const char* processName, + const char* defaultFileName, + const char* defaultTreeName, + auto&& databranch, + bool singleBranch = false) { + if (tpcSectors.size() == 0) { + throw std::invalid_argument(std::string("writer process configuration needs list of TPC sectors")); + } + + auto amendInput = [tpcSectors, laneConfiguration](InputSpec& input, size_t index) { + input.binding += std::to_string(laneConfiguration[index]); + DataSpecUtils::updateMatchingSubspec(input, laneConfiguration[index]); + }; + auto amendBranchDef = [laneConfiguration, amendInput, tpcSectors, getIndex, getName, singleBranch](auto&& def, bool enableMC = true) { + if (!singleBranch) { + def.keys = mergeInputs(def.keys, laneConfiguration.size(), amendInput); + // the branch is disabled if set to 0 + def.nofBranches = enableMC ? tpcSectors.size() : 0; + def.getIndex = getIndex; + def.getName = getName; + } else { + // instead of the separate sector branches only one is going to be written + def.nofBranches = enableMC ? 1 : 0; + } + return std::move(def); + }; + + return std::move(MakeRootTreeWriterSpec(processName, defaultFileName, defaultTreeName, + std::move(amendBranchDef(databranch)))()); + }; + + ////////////////////////////////////////////////////////////////////////////////////////////// + // + // a writer process for digits + // + // selected by output type 'difits' + using OutputType = std::vector; + workflow.push_back(makeWriterSpec("tpc-occupancy-writer", + outputFile.data(), + "o2sim", + BranchDefinition{InputSpec{"data", "TPC", "FILTERDIG", 0}, + "TPCDigit", + "digit-branch-name"})); + } else if (writerType == WriterType::EPN) { + workflow.push_back(getFileWriterSpec("data:TPC/FILTERDIG", BranchType::Digits)); + } + + return workflow; +} diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 89bd5151faa64..f72405cbebf7f 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -56,7 +56,7 @@ void customize(std::vector& workflowOptions) std::vector options{ {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, clustershw, clusters, compressed-clusters, compressed-clusters-ctf, pass-through"}}, - {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clusters, tracks, compressed-clusters, encoded-clusters, disable-writer, send-clusters-per-sector, qa, no-shared-cluster-map"}}, + {"output-type", VariantType::String, "tracks", {"digits, zsraw, clustershw, clusters, tracks, compressed-clusters, encoded-clusters, disable-writer, send-clusters-per-sector, qa, no-shared-cluster-map, tpc-triggers"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input reader"}}, {"no-ca-clusterer", VariantType::Bool, false, {"Use HardwareClusterer instead of clusterer of GPUCATracking"}}, {"disable-mc", VariantType::Bool, false, {"disable sending of MC information"}}, diff --git a/Detectors/TPC/workflow/src/tpc-time-series.cxx b/Detectors/TPC/workflow/src/tpc-time-series.cxx new file mode 100644 index 0000000000000..e6ce77a1227cc --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-time-series.cxx @@ -0,0 +1,48 @@ +// 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 tpc-time-series.cxx +/// \author Matthias Kleiner, mkleiner@ikf.uni-frankfurt.de + +#include "TPCWorkflow/TPCTimeSeriesSpec.h" +#include "TPCWorkflow/TPCTimeSeriesWriterSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "TPCReaderWorkflow/TPCSectorCompletionPolicy.h" +#include "Framework/ConfigParamSpec.h" +#include "GPUDebugStreamer.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"disable-root-output", VariantType::Bool, false, {"disable root-files output writers"}}, + {"material-type", VariantType::Int, 2, {"Type for the material budget during track propagation: 0=None, 1=Geo, 2=LUT"}}}; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + WorkflowSpec workflow; + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + const bool disableWriter = config.options().get("disable-root-output"); + auto materialType = static_cast(config.options().get("material-type")); + workflow.emplace_back(o2::tpc::getTPCTimeSeriesSpec(disableWriter, materialType)); + if (!disableWriter) { + workflow.emplace_back(o2::tpc::getTPCTimeSeriesWriterSpec()); + } + return workflow; +} diff --git a/Detectors/TPC/workflow/src/tpc-vdrift-tgl-calibration-workflow.cxx b/Detectors/TPC/workflow/src/tpc-vdrift-tgl-calibration-workflow.cxx index e7d969445a9d4..26e5278c479ad 100644 --- a/Detectors/TPC/workflow/src/tpc-vdrift-tgl-calibration-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-vdrift-tgl-calibration-workflow.cxx @@ -24,7 +24,7 @@ void customize(std::vector& workflowOptions) {"max-tgl-its", o2::framework::VariantType::Float, 2.f, {"max range for tgL of ITS tracks"}}, {"nbins-dtgl", o2::framework::VariantType::Int, 50, {"number of bins in tgL_ITS - tgl_TPC"}}, {"max-dtgl-itstpc", o2::framework::VariantType::Float, 0.15f, {"max range for tgL_ITS - tgl_TPC"}}, - {"min-entries-per-slot", o2::framework::VariantType::Int, 1000, {"mininal number of entries per slot"}}, + {"min-entries-per-slot", o2::framework::VariantType::Int, 10000, {"mininal number of entries per slot"}}, {"time-slot-seconds", o2::framework::VariantType::Int, 600, {"time slot length in seconds"}}, {"max-slots-delay", o2::framework::VariantType::Float, 0.1f, {"difference in slot units between the current TF and oldest slot (end TF) to account for the TF"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; diff --git a/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h b/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h index 5b5323ada0e9a..c7620ae6944c3 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/DCSProcessor.h @@ -64,10 +64,18 @@ class DCSProcessor bool updateCurrentsDPsCCDB(); bool updateEnvDPsCCDB(); bool updateRunDPsCCDB(); + // LB: new DPs for Fed and Cavern + bool updateFedChamberStatusDPsCCDB(); + bool updateFedCFGtagDPsCCDB(); + bool updateFedEnvTempDPsCCDB(); + bool updateCavernDPsCCDB(); // signal that the CCDB object for the voltages should be updated due to change exceeding threshold bool shouldUpdateVoltages() const { return mShouldUpdateVoltages; } bool shouldUpdateRun() const { return mShouldUpdateRun; } + // LB: Only update ChamberStatus/CFGtag if both conditions are met (complete DPs and new run) + bool shouldUpdateFedChamberStatus() const { return mFedChamberStatusCompleteDPs && mFirstRunEntryForFedChamberStatusUpdate; } + bool shouldUpdateFedCFGtag() const { return mFedCFGtagCompleteDPs && mFirstRunEntryForFedCFGtagUpdate; } // allow access to the CCDB objects from DPL processor CcdbObjectInfo& getccdbGasDPsInfo() { return mCcdbGasDPsInfo; } @@ -75,15 +83,25 @@ class DCSProcessor CcdbObjectInfo& getccdbCurrentsDPsInfo() { return mCcdbCurrentsDPsInfo; } CcdbObjectInfo& getccdbEnvDPsInfo() { return mCcdbEnvDPsInfo; } CcdbObjectInfo& getccdbRunDPsInfo() { return mCcdbRunDPsInfo; } + CcdbObjectInfo& getccdbFedChamberStatusDPsInfo() { return mCcdbFedChamberStatusDPsInfo; } + CcdbObjectInfo& getccdbFedCFGtagDPsInfo() { return mCcdbFedCFGtagDPsInfo; } + CcdbObjectInfo& getccdbFedEnvTempDPsInfo() { return mCcdbFedEnvTempDPsInfo; } + CcdbObjectInfo& getccdbCavernDPsInfo() { return mCcdbCavernDPsInfo; } const std::unordered_map& getTRDGasDPsInfo() const { return mTRDDCSGas; } const std::unordered_map& getTRDVoltagesDPsInfo() const { return mTRDDCSVoltages; } const std::unordered_map& getTRDCurrentsDPsInfo() const { return mTRDDCSCurrents; } const std::unordered_map& getTRDEnvDPsInfo() const { return mTRDDCSEnv; } const std::unordered_map& getTRDRunDPsInfo() const { return mTRDDCSRun; } + const std::unordered_map& getTRDFedChamberStatusDPsInfo() const { return mTRDDCSFedChamberStatus; } + const std::unordered_map& getTRDFedCFGtagDPsInfo() const { return mTRDDCSFedCFGtag; } + const std::unordered_map& getTRDFedEnvTempDPsInfo() const { return mTRDDCSFedEnvTemp; } + const std::unordered_map& getTRDCavernDPsInfo() const { return mTRDDCSCavern; } // settings void setCurrentTS(TFType tf) { mCurrentTS = tf; } void setVerbosity(int v) { mVerbosity = v; } + void setMaxCounterAlarmFed(int alarmfed) { mFedAlarmCounterMax = alarmfed; } + void setFedMinimunDPsForUpdate(int minupdatefed) { mFedMinimunDPsForUpdate = minupdatefed; } // reset methods void clearGasDPsInfo(); @@ -91,6 +109,11 @@ class DCSProcessor void clearCurrentsDPsInfo(); void clearEnvDPsInfo(); void clearRunDPsInfo(); + // LB: new DPs for Fed and Cavern + void clearFedChamberStatusDPsInfo(); + void clearFedCFGtagDPsInfo(); + void clearFedEnvTempDPsInfo(); + void clearCavernDPsInfo(); // helper functions int getChamberIdFromAlias(const char* alias) const; @@ -102,12 +125,11 @@ class DCSProcessor std::unordered_map mTRDDCSVoltages; ///< anode and drift voltages std::unordered_map mTRDDCSEnv; ///< environment parameters (temperatures, pressures) std::unordered_map mTRDDCSRun; ///< run number and run type - // TODO - // Possibly add CFG tag and chamber status here? - // Or send errors to the InfoLogger in case CFG tag mismatches are detected for chamber which have the same FSM state? - // For this I need more information on the chamber status - which status indicates all good and included in data taking? - // not TODO - // I don't think the FED ENV temperature is needed at analysis level at any point in time so I am leaving it out for now + // LB: new DPs for Fed and Cavern + std::unordered_map mTRDDCSFedChamberStatus; ///< fed chamber status + std::unordered_map mTRDDCSFedCFGtag; ///< fed config tag + std::unordered_map mTRDDCSFedEnvTemp; ///< fed env temperature + std::unordered_map mTRDDCSCavern; ///< cavern humidity // helper variables std::unordered_map mPids; ///< flag for each DP whether it has been processed at least once @@ -117,24 +139,52 @@ class DCSProcessor CcdbObjectInfo mCcdbCurrentsDPsInfo; CcdbObjectInfo mCcdbEnvDPsInfo; CcdbObjectInfo mCcdbRunDPsInfo; + // LB: new DPs for Fed and Cavern + CcdbObjectInfo mCcdbFedChamberStatusDPsInfo; + CcdbObjectInfo mCcdbFedCFGtagDPsInfo; + CcdbObjectInfo mCcdbFedEnvTempDPsInfo; + CcdbObjectInfo mCcdbCavernDPsInfo; + TFType mGasStartTS; ///< the time stamp of the first TF which was processesd for the current GAS CCDB object TFType mVoltagesStartTS; ///< the time stamp of the first TF which was processesd for the current voltages CCDB object TFType mCurrentsStartTS; ///< the time stamp of the first TF which was processesd for the current voltages CCDB object TFType mEnvStartTS; TFType mRunStartTS; TFType mRunEndTS; + // LB: new DPs for Fed and Cavern + TFType mFedChamberStatusStartTS; + TFType mFedCFGtagStartTS; + TFType mFedEnvTempStartTS; + TFType mCavernStartTS; TFType mCurrentTS{0}; ///< the time stamp of the TF currently being processed bool mGasStartTSset{false}; bool mVoltagesStartTSSet{false}; bool mCurrentsStartTSSet{false}; bool mEnvStartTSSet{false}; bool mRunStartTSSet{false}; + // LB: new DPs for Fed and Cavern + bool mFedChamberStatusStartTSSet{false}; + bool mFedCFGtagStartTSSet{false}; + bool mFedEnvTempStartTSSet{false}; + bool mCavernStartTSSet{false}; std::bitset mVoltageSet{}; bool mShouldUpdateVoltages{false}; bool mShouldUpdateRun{false}; + // LB: FedChamberStatus and FedCFGtag logic + bool mFedChamberStatusCompleteDPs{false}; + bool mFedCFGtagCompleteDPs{false}; + bool mFirstRunEntryForFedChamberStatusUpdate{false}; + bool mFirstRunEntryForFedCFGtagUpdate{false}; + int mCurrentRunNumber{-1}; + int mFedChamberStatusAlarmCounter{0}; + int mFedCFGtagAlarmCounter{0}; + // LB: for testing runNo object, turned off for now + // int mFinishedRunNumber; // settings int mVerbosity{0}; + int mFedAlarmCounterMax{1}; + int mFedMinimunDPsForUpdate{540}; ClassDefNV(DCSProcessor, 0); }; diff --git a/Detectors/TRD/calibration/macros/makeTRDCCDBEntryForDCS.C b/Detectors/TRD/calibration/macros/makeTRDCCDBEntryForDCS.C index 55de2e0759aa1..f99f0a3822f49 100644 --- a/Detectors/TRD/calibration/macros/makeTRDCCDBEntryForDCS.C +++ b/Detectors/TRD/calibration/macros/makeTRDCCDBEntryForDCS.C @@ -36,9 +36,11 @@ int makeTRDCCDBEntryForDCS(const std::string url = "http://localhost:8080") aliasesFloat.insert(aliasesFloat.end(), {"trd_aliEnvTempCavern", "trd_aliEnvTempP2"}); aliasesFloat.insert(aliasesFloat.end(), {"trd_aliEnvPressure00", "trd_aliEnvPressure01", "trd_aliEnvPressure02"}); aliasesInt.insert(aliasesInt.end(), {"trd_runNo", "trd_runType"}); - // aliasesFloat.insert(aliasesFloat.end(), {"trd_cavernHumidity", "trd_fedEnvTemp[00..539]"}); - // aliasesInt.insert(aliasesInt.end(), {"trd_fedChamberStatus[00..539]"}); - // aliasesString.insert(aliasesString.end(), {"trd_fedCFGtag[00..539]"}); + + // New DPs + aliasesFloat.insert(aliasesFloat.end(), {"trd_cavernHumidity", "trd_fedEnvTemp[00..539]"}); + aliasesInt.insert(aliasesInt.end(), {"trd_fedChamberStatus[00..539]"}); + aliasesString.insert(aliasesString.end(), {"trd_fedCFGtag[00..539]"}); DPID dpidTmp; for (const auto& ali : o2::dcs::expandAliases(aliasesFloat)) { diff --git a/Detectors/TRD/calibration/macros/readTRDDCSentries.C b/Detectors/TRD/calibration/macros/readTRDDCSentries.C index 76bc14b340a1b..2942b9bf14443 100644 --- a/Detectors/TRD/calibration/macros/readTRDDCSentries.C +++ b/Detectors/TRD/calibration/macros/readTRDDCSentries.C @@ -31,6 +31,7 @@ void readTRDDCSentries(std::string ccdb = "http://localhost:8080", long ts = -1) ccdbmgr.setURL(ccdb.c_str()); // comment out this line to read from production CCDB instead of a local one, or adapt ccdb string if (ts < 0) { ts = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + std::cout << "Timestamp: " << ts << std::endl; } ccdbmgr.setTimestamp(ts); @@ -60,5 +61,45 @@ void readTRDDCSentries(std::string ccdb = "http://localhost:8080", long ts = -1) } std::cout << std::endl; + // LB: also read FedChamberStatus and FedCFGtag for testing + // There seems to be an issue with the validity timestamp of Run DPs, ignoring them at the moment + // Access FedChamberStatus DPs + auto calchamberstatus = ccdbmgr.get>("TRD/Calib/DCSDPsFedChamberStatus"); + + std::cout << "Print all objects from the map (DCSDPsFedChamberStatus), together with their DataPointIdentifier:" << std::endl; + for (const auto& entry : *calchamberstatus) { + std::cout << "id = " << entry.first << ",\tvalue = " << entry.second << std::endl; + } + std::cout << std::endl; + + // Access FedCFGtag DPs + auto calcfgtag = ccdbmgr.get>("TRD/Calib/DCSDPsFedCFGtag"); + + std::cout << "Print all objects from the map (DCSDPsFedCFGtag), together with their DataPointIdentifier:" << std::endl; + for (const auto& entry : *calcfgtag) { + std::cout << "id = " << entry.first << ",\tvalue = " << entry.second << std::endl; + } + std::cout << std::endl; + + // Access FedEnvTemp DPs + auto calfedenvtemp = ccdbmgr.get>("TRD/Calib/DCSDPsFedEnvTemp"); + + for (const auto& entry : *calfedenvtemp) { + std::cout << entry.first << std::endl; + entry.second.print(); + std::cout << std::endl; + } + + // Access Cavern DPs + auto calcavern = ccdbmgr.get>("TRD/Calib/DCSDPsCavern"); + + for (const auto& entry : *calcavern) { + std::cout << entry.first << std::endl; + entry.second.print(); + std::cout << std::endl; + } + + std::cout << std::endl; + return; } diff --git a/Detectors/TRD/calibration/src/DCSProcessor.cxx b/Detectors/TRD/calibration/src/DCSProcessor.cxx index 2ef3ca1dd673d..12097a8e20c49 100644 --- a/Detectors/TRD/calibration/src/DCSProcessor.cxx +++ b/Detectors/TRD/calibration/src/DCSProcessor.cxx @@ -43,12 +43,41 @@ int DCSProcessor::process(const gsl::span dps) LOG(info) << "\n\n\nProcessing new TF\n-----------------"; } - if (mVerbosity > 1) { - std::unordered_map mapin; - for (auto& it : dps) { - mapin[it.id] = it.data; + // LB: setup counters for ChamberStatus/CFGtag logic + int ChamberStatusDPsCounter = 0; + int CFGtagDPsCounter = 0; + + std::unordered_map mapin; + for (auto& it : dps) { + mapin[it.id] = it.data; + + // LB: check if all ChamberStatus/CFGtag DPs were sent in dps + // if counter is equal to 540 => all DPs were sent + if (std::strstr(it.id.get_alias(), "trd_fedChamberStatus") != nullptr) { + ChamberStatusDPsCounter++; + } else if (std::strstr(it.id.get_alias(), "trd_fedCFGtag") != nullptr) { + CFGtagDPsCounter++; + } + } + + if (ChamberStatusDPsCounter >= mFedMinimunDPsForUpdate) { + mFedChamberStatusCompleteDPs = true; + if (mVerbosity > 1) { + LOG(info) << "Minimum number of required DPs (" << mFedMinimunDPsForUpdate << ") for ChamberStatus update were found."; } + } + if (CFGtagDPsCounter >= mFedMinimunDPsForUpdate) { + mFedCFGtagCompleteDPs = true; + if (mVerbosity > 1) { + LOG(info) << "Minimum number of required DPs (" << mFedMinimunDPsForUpdate << ") for CFGtag update were found."; + } + } + if (mVerbosity > 1) { + LOG(info) << "Number of ChamberStatus DPs = " << ChamberStatusDPsCounter; + LOG(info) << "Number of CFGtag DPs = " << CFGtagDPsCounter; + } + if (mVerbosity > 1) { for (auto& it : mPids) { const auto& el = mapin.find(it.first); if (el == mapin.end()) { @@ -96,8 +125,10 @@ int DCSProcessor::processDP(const DPCOM& dpcom) } auto flags = dpcom.data.get_flags(); if (processFlags(flags, dpid.get_alias()) == 0) { + auto etime = dpcom.data.get_epoch_time(); + + // DPs are sorted by type variable if (type == DPVAL_DOUBLE) { - auto etime = dpcom.data.get_epoch_time(); // check if DP is one of the gas values if (std::strstr(dpid.get_alias(), "trd_gas") != nullptr) { @@ -163,42 +194,116 @@ int DCSProcessor::processDP(const DPCOM& dpcom) mLastDPTimeStamps[dpid] = etime; } } + + if (std::strstr(dpid.get_alias(), "trd_fedEnvTemp") != nullptr) { // DP is trd_fedEnvTemp + if (!mFedEnvTempStartTSSet) { + mFedEnvTempStartTS = mCurrentTS; + mFedEnvTempStartTSSet = true; + } + auto& dpInfoFedEnvTemp = mTRDDCSFedEnvTemp[dpid]; + if (dpInfoFedEnvTemp.nPoints == 0 || etime != mLastDPTimeStamps[dpid]) { + // only add data point in case last one was not already read before + dpInfoFedEnvTemp.addPoint(o2::dcs::getValue(dpcom), etime); + mLastDPTimeStamps[dpid] = etime; + } + } + + if (std::strstr(dpid.get_alias(), "trd_cavernHumidity") != nullptr) { // DP is trd_cavernHumidity + if (!mCavernStartTSSet) { + mCavernStartTS = mCurrentTS; + mCavernStartTSSet = true; + } + auto& dpInfoCavern = mTRDDCSCavern[dpid]; + if (dpInfoCavern.nPoints == 0 || etime != mLastDPTimeStamps[dpid]) { + // only add data point in case last one was not already read before + dpInfoCavern.addPoint(o2::dcs::getValue(dpcom), etime); + mLastDPTimeStamps[dpid] = etime; + } + } } + if (type == DPVAL_INT) { if (std::strstr(dpid.get_alias(), "trd_runNo") != nullptr) { // DP is trd_runNo if (!mRunStartTSSet) { mRunStartTS = mCurrentTS; mRunStartTSSet = true; } + auto& runNumber = mTRDDCSRun[dpid]; - if (mPids[dpid] && runNumber != o2::dcs::getValue(dpcom)) { - LOGF(info, "Run number has already been processed and the new one %i differs from the old one %i", runNumber, o2::dcs::getValue(dpcom)); - mShouldUpdateRun = true; - mRunEndTS = mCurrentTS; - } else { - runNumber = o2::dcs::getValue(dpcom); - } - } else if (std::strstr(dpid.get_alias(), "trd_runType") != nullptr) { // DP is trd_runType - if (!mRunStartTSSet) { - mRunStartTS = mCurrentTS; - mRunStartTSSet = true; + + // LB: Check if new value is a valid run number (0 = cleared variable) + if (o2::dcs::getValue(dpcom) > 0) { + // If value has changed from previous one, new run has begun and update + if (o2::dcs::getValue(dpcom) != mCurrentRunNumber) { + LOG(info) << "New run number " << o2::dcs::getValue(dpcom) << " differs from the old one " << mCurrentRunNumber; + mShouldUpdateRun = true; + // LB: two different flags as they reset separately, after upload of CCDB, for each object + mFirstRunEntryForFedChamberStatusUpdate = true; + mFirstRunEntryForFedCFGtagUpdate = true; + mRunEndTS = mCurrentTS; + } + + // LB: Save current run number + mCurrentRunNumber = o2::dcs::getValue(dpcom); + // Save to mTRDDCSRun + runNumber = mCurrentRunNumber; + } + + if (mVerbosity > 2) { + LOG(info) << "Current Run Number: " << mCurrentRunNumber; + } + + } else if (std::strstr(dpid.get_alias(), "trd_fedChamberStatus") != nullptr) { // DP is trd_fedChamberStatus + if (!mFedChamberStatusStartTSSet) { + mFedChamberStatusStartTS = mCurrentTS; + mFedChamberStatusStartTSSet = true; } - auto& runType = mTRDDCSRun[dpid]; - if (mPids[dpid] && runType != o2::dcs::getValue(dpcom)) { - LOGF(info, "Run type has already been processed and the new one %i differs from the old one %i", runType, o2::dcs::getValue(dpcom)); - mShouldUpdateRun = true; - mRunEndTS = mCurrentTS; - } else { - runType = o2::dcs::getValue(dpcom); + + auto& dpInfoFedChamberStatus = mTRDDCSFedChamberStatus[dpid]; + if (etime != mLastDPTimeStamps[dpid]) { + if (dpInfoFedChamberStatus != o2::dcs::getValue(dpcom)) { + // If value changes after processing and DPs should not be updated, log change as warning (for now) + if (mPids[dpid] && !(mFedChamberStatusCompleteDPs && mFirstRunEntryForFedChamberStatusUpdate)) { + // Issue an alarm if counter is lower than maximum, warning otherwise + if (mFedChamberStatusAlarmCounter < mFedAlarmCounterMax) { + LOG(alarm) << "ChamberStatus change " << dpid.get_alias() << " : " << dpInfoFedChamberStatus << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; + mFedChamberStatusAlarmCounter++; + } else if (mVerbosity > 0) { + LOG(warn) << "ChamberStatus change " << dpid.get_alias() << " : " << dpInfoFedChamberStatus << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; + } + } + } + + dpInfoFedChamberStatus = o2::dcs::getValue(dpcom); + mLastDPTimeStamps[dpid] = etime; } } } if (type == DPVAL_STRING) { if (std::strstr(dpid.get_alias(), "trd_fedCFGtag") != nullptr) { // DP is trd_fedCFGtag - auto cfgTag = o2::dcs::getValue(dpcom); - if (mVerbosity > 1) { - LOG(info) << "CFG tag " << dpid.get_alias() << " is " << cfgTag; + if (!mFedCFGtagStartTSSet) { + mFedCFGtagStartTS = mCurrentTS; + mFedCFGtagStartTSSet = true; + } + + auto& dpInfoFedCFGtag = mTRDDCSFedCFGtag[dpid]; + if (etime != mLastDPTimeStamps[dpid]) { + if (dpInfoFedCFGtag != o2::dcs::getValue(dpcom)) { + // If value changes after processing and DPs should not be updated, log change as warning (for now) + if (mPids[dpid] && !(mFedCFGtagCompleteDPs && mFirstRunEntryForFedCFGtagUpdate)) { + // Issue an alarm if counter is lower than maximum, warning otherwise + if (mFedCFGtagAlarmCounter < mFedAlarmCounterMax) { + LOG(alarm) << "CFGtag change " << dpid.get_alias() << " : " << dpInfoFedCFGtag << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; + mFedCFGtagAlarmCounter++; + } else if (mVerbosity > 0) { + LOG(warn) << "CFGtag change " << dpid.get_alias() << " : " << dpInfoFedCFGtag << " -> " << o2::dcs::getValue(dpcom) << ", run = " << mCurrentRunNumber; + } + } + } + + dpInfoFedCFGtag = o2::dcs::getValue(dpcom); + mLastDPTimeStamps[dpid] = etime; } } } @@ -349,7 +454,7 @@ bool DCSProcessor::updateVoltagesDPsCCDB() retVal = true; } if (mVerbosity > 1) { - LOG(info) << "PID = " << it.first.get_alias() << " Value = " << mTRDDCSVoltages[it.first]; + LOG(info) << "PID = " << it.first.get_alias() << ". Value = " << mTRDDCSVoltages[it.first]; } } } @@ -375,7 +480,7 @@ bool DCSProcessor::updateEnvDPsCCDB() if (it.second == true) { // we processed the DP at least 1x retVal = true; } - if (mVerbosity > 0) { + if (mVerbosity > 1) { LOG(info) << "PID = " << it.first.get_alias(); mTRDDCSEnv[it.first].print(); } @@ -398,7 +503,7 @@ bool DCSProcessor::updateRunDPsCCDB() for (const auto& it : mPids) { const auto& type = it.first.get_type(); - if (type == o2::dcs::DPVAL_DOUBLE) { + if (type == o2::dcs::DPVAL_INT) { if (std::strstr(it.first.get_alias(), "trd_run") != nullptr) { if (it.second == true) { // we processed the DP at least 1x retVal = true; @@ -410,9 +515,135 @@ bool DCSProcessor::updateRunDPsCCDB() } } std::map md; - md["responsible"] = "Ole Schmidt"; + md["responsible"] = "Leonardo Barreto"; + // Redundancy for testing, this object is updated after run ended, so need to write old run number, not current + // md["runNumber"] = std::to_string(mFinishedRunNumber); o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSRun, mCcdbRunDPsInfo, "TRD/Calib/DCSDPsRun", md, mRunStartTS, mRunEndTS); + // LB: Deactivated upload of Run DPs to CCDB even if processed + // To turn it back on just comment the next line + retVal = false; + return retVal; +} + +bool DCSProcessor::updateFedChamberStatusDPsCCDB() +{ + // here we create the object containing the fedChamberStatus data points to then be sent to CCDB + LOG(info) << "Preparing CCDB object for TRD fedChamberStatus DPs"; + + bool retVal = false; // set to 'true' in case at least one DP for run has been processed + + for (const auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_INT) { + if (std::strstr(it.first.get_alias(), "trd_fedChamberStatus") != nullptr) { + if (it.second == true) { // we processed the DP at least 1x + retVal = true; + } + if (mVerbosity > 1) { + LOG(info) << "PID = " << it.first.get_alias() << ". Value = " << mTRDDCSFedChamberStatus[it.first]; + } + } + } + } + + std::map md; + md["responsible"] = "Leonardo Barreto"; + md["runNumber"] = std::to_string(mCurrentRunNumber); + // TODO: define mFedStartTS and mFedEndTS, use same setup as env for now + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSFedChamberStatus, mCcdbFedChamberStatusDPsInfo, "TRD/Calib/DCSDPsFedChamberStatus", md, mFedChamberStatusStartTS, mFedChamberStatusStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + + return retVal; +} + +bool DCSProcessor::updateFedCFGtagDPsCCDB() +{ + // here we create the object containing the fedCFGtag data points to then be sent to CCDB + LOG(info) << "Preparing CCDB object for TRD fedCFGtag DPs"; + + bool retVal = false; // set to 'true' in case at least one DP for run has been processed + + for (const auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_STRING) { + if (std::strstr(it.first.get_alias(), "trd_fedCFGtag") != nullptr) { + if (it.second == true) { // we processed the DP at least 1x + retVal = true; + } + if (mVerbosity > 1) { + LOG(info) << "PID = " << it.first.get_alias() << ". Value = " << mTRDDCSFedCFGtag[it.first]; + } + } + } + } + + std::map md; + md["responsible"] = "Leonardo Barreto"; + md["runNumber"] = std::to_string(mCurrentRunNumber); + // TODO: define mFedStartTS and mFedEndTS, use same setup as env for now + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSFedCFGtag, mCcdbFedCFGtagDPsInfo, + "TRD/Calib/DCSDPsFedCFGtag", md, mFedCFGtagStartTS, mFedCFGtagStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + + return retVal; +} + +bool DCSProcessor::updateFedEnvTempDPsCCDB() +{ + // here we create the object containing the fedEnvTemp data points to then be sent to CCDB + LOG(info) << "Preparing CCDB object for TRD fedEnvTemp DPs"; + + bool retVal = false; // set to 'true' in case at least one DP for run has been processed + + for (const auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_DOUBLE) { + if (std::strstr(it.first.get_alias(), "trd_fedEnvTemp") != nullptr) { + if (it.second == true) { // we processed the DP at least 1x + retVal = true; + } + if (mVerbosity > 1) { + LOG(info) << "PID = " << it.first.get_alias(); + mTRDDCSFedEnvTemp[it.first].print(); + } + } + } + } + + std::map md; + md["responsible"] = "Leonardo Barreto"; + // TODO: define mFedStartTS and mFedEndTS, use same setup as env for now + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSFedEnvTemp, mCcdbFedEnvTempDPsInfo, + "TRD/Calib/DCSDPsFedEnvTemp", md, mFedEnvTempStartTS, mFedEnvTempStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + + return retVal; +} + +bool DCSProcessor::updateCavernDPsCCDB() +{ + // here we create the object containing the cavern data points to then be sent to CCDB + LOG(info) << "Preparing CCDB object for TRD cavern DPs"; + + bool retVal = false; // set to 'true' in case at least one DP for env has been processed + + for (const auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_DOUBLE) { + if (std::strstr(it.first.get_alias(), "trd_cavern") != nullptr) { + if (it.second == true) { // we processed the DP at least 1x + retVal = true; + } + if (mVerbosity > 1) { + LOG(info) << "PID = " << it.first.get_alias(); + mTRDDCSCavern[it.first].print(); + } + } + } + } + std::map md; + md["responsible"] = "Leonardo Barreto"; + // TODO: define mCavernStartTS and mCavernEndTS, use same setup as env for now + o2::calibration::Utils::prepareCCDBobjectInfo(mTRDDCSCavern, mCcdbCavernDPsInfo, "TRD/Calib/DCSDPsCavern", md, mCavernStartTS, mCavernStartTS + 3 * o2::ccdb::CcdbObjectInfo::DAY); + return retVal; } @@ -453,7 +684,6 @@ void DCSProcessor::clearGasDPsInfo() // reset the data and the gas CCDB object itself mTRDDCSGas.clear(); mGasStartTSset = false; // the next object will be valid from the first processed time stamp - // reset the 'processed' flags for the gas DPs for (auto& it : mPids) { const auto& type = it.first.get_type(); @@ -469,7 +699,7 @@ void DCSProcessor::clearEnvDPsInfo() { mTRDDCSEnv.clear(); mEnvStartTSSet = false; - // reset the 'processed' flags for the gas DPs + // reset the 'processed' flags for the env DPs for (auto& it : mPids) { const auto& type = it.first.get_type(); if (type == o2::dcs::DPVAL_DOUBLE) { @@ -485,13 +715,79 @@ void DCSProcessor::clearRunDPsInfo() mTRDDCSRun.clear(); mRunStartTSSet = false; mShouldUpdateRun = false; - // reset the 'processed' flags for the gas DPs + // reset the 'processed' flags for the run DPs for (auto& it : mPids) { const auto& type = it.first.get_type(); - if (type == o2::dcs::DPVAL_DOUBLE) { + if (type == o2::dcs::DPVAL_INT) { if (std::strstr(it.first.get_alias(), "trd_run") != nullptr) { it.second = false; } } } } + +void DCSProcessor::clearFedChamberStatusDPsInfo() +{ + mTRDDCSFedChamberStatus.clear(); + mFedChamberStatusStartTSSet = false; + mFedChamberStatusCompleteDPs = false; + mFirstRunEntryForFedChamberStatusUpdate = false; + mFedChamberStatusAlarmCounter = 0; + // reset the 'processed' flags for the fed DPs + for (auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_INT) { + if (std::strstr(it.first.get_alias(), "trd_fedChamberStatus") != nullptr) { + it.second = false; + } + } + } +} + +void DCSProcessor::clearFedCFGtagDPsInfo() +{ + mTRDDCSFedCFGtag.clear(); + mFedCFGtagStartTSSet = false; + mFedCFGtagCompleteDPs = false; + mFirstRunEntryForFedCFGtagUpdate = false; + mFedCFGtagAlarmCounter = 0; + // reset the 'processed' flags for the fed DPs + for (auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_STRING) { + if (std::strstr(it.first.get_alias(), "trd_fedCFGtag") != nullptr) { + it.second = false; + } + } + } +} + +void DCSProcessor::clearFedEnvTempDPsInfo() +{ + mTRDDCSFedEnvTemp.clear(); + mFedEnvTempStartTSSet = false; + // reset the 'processed' flags for the fed DPs + for (auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_DOUBLE) { + if (std::strstr(it.first.get_alias(), "trd_fedEnvTemp") != nullptr) { + it.second = false; + } + } + } +} + +void DCSProcessor::clearCavernDPsInfo() +{ + mTRDDCSCavern.clear(); + mCavernStartTSSet = false; + // reset the 'processed' flags for the fed DPs + for (auto& it : mPids) { + const auto& type = it.first.get_type(); + if (type == o2::dcs::DPVAL_DOUBLE) { + if (std::strstr(it.first.get_alias(), "trd_cavern") != nullptr) { + it.second = false; + } + } + } +} diff --git a/Detectors/TRD/calibration/src/PulseHeight.cxx b/Detectors/TRD/calibration/src/PulseHeight.cxx index 851637a88a901..13dee1ab9cfee 100644 --- a/Detectors/TRD/calibration/src/PulseHeight.cxx +++ b/Detectors/TRD/calibration/src/PulseHeight.cxx @@ -92,16 +92,19 @@ void PulseHeight::process() const auto& trigTrack = (*trackTriggers)[iTrigTrack]; if (trigTrack.getBCData().differenceInBC(trig.getBCData()) > 0) { // aborting, since track trigger is later than digit trigger"; + LOGP(debug, "Aborting, track trigger is too late"); break; } if (trigTrack.getBCData() != trig.getBCData()) { // skipping, since track trigger earlier than digit trigger"; + LOGP(debug, "Skipping, track trigger is too late"); ++lastTrkTrig[iTrackType]; continue; } if ((*tracks)[trigTrack.getFirstTrack()].hasPileUpInfo() && (*tracks)[trigTrack.getFirstTrack()].getPileUpTimeShiftMUS() < mParams.pileupCut) { // rejecting triggers which are close to other collisions (avoid pile-up) ++lastTrkTrig[iTrackType]; + LOGP(debug, "Rececting trigger due to pileup of {}", (*tracks)[trigTrack.getFirstTrack()].getPileUpTimeShiftMUS()); break; } for (int iTrk = trigTrack.getFirstTrack(); iTrk < trigTrack.getFirstTrack() + trigTrack.getNumberOfTracks(); iTrk++) { @@ -125,8 +128,13 @@ void PulseHeight::process() void PulseHeight::findDigitsForTracklet(const Tracklet64& trklt, const TriggerRecord& trig, int type) { auto trkltDet = trklt.getDetector(); - for (int iDigit = trig.getFirstDigit() + 1; iDigit < trig.getFirstDigit() + trig.getNumberOfDigits() - 1; ++iDigit) { + int iDigitFirst = trig.getFirstDigit(); + int iDigitLast = trig.getFirstDigit() + trig.getNumberOfDigits(); + for (int iDigit = iDigitFirst + 1; iDigit < iDigitLast - 1; ++iDigit) { const auto& digit = (*mDigits)[iDigit]; + if (digit.isSharedDigit()) { + continue; // avoid double-counting of the same digit + } if (digit.getDetector() != trkltDet || digit.getPadRow() != trklt.getPadRow() || digit.getPadCol() != trklt.getPadCol()) { // for now we loose charge information from padrow-crossing tracklets (~15% of all tracklets) continue; @@ -134,38 +142,47 @@ void PulseHeight::findDigitsForTracklet(const Tracklet64& trklt, const TriggerRe int nNeighbours = 0; bool left = false; bool right = false; - const auto& digitLeft = (*mDigits)[iDigit - 1]; - const auto& digitRight = (*mDigits)[iDigit + 1]; + const auto* digitLeft = &(*mDigits)[iDigit + 1]; + const auto* digitRight = &(*mDigits)[iDigit - 1]; + // due to shared digits the neighbouring element might still be in the same pad column + if (digitLeft->getPadCol() == digit.getPadCol() && iDigit < iDigitLast - 2) { + digitLeft = &(*mDigits)[iDigit + 2]; + } + if (digitRight->getPadCol() == digit.getPadCol() && iDigit > iDigitFirst + 2) { + digitRight = &(*mDigits)[iDigit - 2]; + } LOG(debug) << "Central digit: " << digit; - LOG(debug) << "Left digit: " << digitLeft; - LOG(debug) << "Right digit: " << digitRight; - if (digitLeft.isNeighbour(digit) && digitLeft.getChannel() < digit.getChannel()) { + LOG(debug) << "Left digit: " << *digitLeft; + LOG(debug) << "Right digit: " << *digitRight; + int direction = 0; + if (digitLeft->isNeighbour(digit)) { ++nNeighbours; left = true; + direction = digit.getPadCol() - digitLeft->getPadCol(); } - if (digitRight.isNeighbour(digit) && digitRight.getChannel() > digit.getChannel()) { + if (digitRight->isNeighbour(digit) && digit.getPadCol() - digitRight->getPadCol() != direction) { ++nNeighbours; right = true; } if (nNeighbours > 0) { int digitTrackletDistance = 0; auto adcSumMax = digit.getADCsum(); - if (left && digitLeft.getADCsum() > adcSumMax) { - adcSumMax = digitLeft.getADCsum(); + if (left && digitLeft->getADCsum() > adcSumMax) { + adcSumMax = digitLeft->getADCsum(); digitTrackletDistance = 1; } - if (right && digitRight.getADCsum() > adcSumMax) { - adcSumMax = digitRight.getADCsum(); + if (right && digitRight->getADCsum() > adcSumMax) { + adcSumMax = digitRight->getADCsum(); digitTrackletDistance = -1; } mDistances.push_back(digitTrackletDistance); for (int iTb = 0; iTb < TIMEBINS; ++iTb) { uint16_t phVal = digit.getADC()[iTb]; if (left) { - phVal += digitLeft.getADC()[iTb]; + phVal += digitLeft->getADC()[iTb]; } if (right) { - phVal += digitRight.getADC()[iTb]; + phVal += digitRight->getADC()[iTb]; } mPHValues.emplace_back(phVal, trkltDet, iTb, nNeighbours, type); } diff --git a/Detectors/TRD/calibration/workflow/TRDDCSDataProcessorSpec.h b/Detectors/TRD/calibration/workflow/TRDDCSDataProcessorSpec.h index 456d1af4328e2..7a7af3c5e889d 100644 --- a/Detectors/TRD/calibration/workflow/TRDDCSDataProcessorSpec.h +++ b/Detectors/TRD/calibration/workflow/TRDDCSDataProcessorSpec.h @@ -73,6 +73,18 @@ class TRDDCSDataProcessor : public o2::framework::Task LOG(error) << "TRD DPs update interval set to zero seconds --> changed to 1800s"; mEnvDPsUpdateInterval = 1800; } + // LB: FedEnvTemp DPs, only update every 30 minutes + mFedEnvTempDPsUpdateInterval = ic.options().get("DPs-update-interval-fedenv"); + if (mFedEnvTempDPsUpdateInterval == 0) { + LOG(error) << "TRD DPs update interval set to zero seconds --> changed to 1800s"; + mFedEnvTempDPsUpdateInterval = 1800; + } + // LB: Cavern DPs, only update every 2 hours + mCavernDPsUpdateInterval = ic.options().get("DPs-update-interval-cavern"); + if (mCavernDPsUpdateInterval == 0) { + LOG(error) << "TRD DPs update interval set to zero seconds --> changed to 7200s"; + mCavernDPsUpdateInterval = 7200; + } bool useCCDBtoConfigure = ic.options().get("use-ccdb-to-configure"); if (useCCDBtoConfigure) { LOG(info) << "Configuring via CCDB"; @@ -94,10 +106,10 @@ class TRDDCSDataProcessor : public o2::framework::Task aliasesFloat.insert(aliasesFloat.end(), {"trd_hvAnodeImon[00..539]", "trd_hvAnodeUmon[00..539]", "trd_hvDriftImon[00..539]", "trd_hvDriftUmon[00..539]"}); aliasesFloat.insert(aliasesFloat.end(), {"trd_aliEnvTempCavern", "trd_aliEnvTempP2"}); aliasesFloat.insert(aliasesFloat.end(), {"trd_aliEnvPressure00", "trd_aliEnvPressure01", "trd_aliEnvPressure02"}); - // aliasesFloat.insert(aliasesFloat.end(), {"trd_cavernHumidity", "trd_fedEnvTemp[00..539]"}); + aliasesFloat.insert(aliasesFloat.end(), {"trd_cavernHumidity", "trd_fedEnvTemp[00..539]"}); aliasesInt.insert(aliasesInt.end(), {"trd_runNo", "trd_runType"}); - // aliasesInt.insert(aliasesInt.end(), {"trd_fedChamberStatus[00..539]"}); - // aliasesString.insert(aliasesString.end(), {"trd_fedCFGtag[00..539]"}); + aliasesInt.insert(aliasesInt.end(), {"trd_fedChamberStatus[00..539]"}); + aliasesString.insert(aliasesString.end(), {"trd_fedCFGtag[00..539]"}); for (const auto& i : o2::dcs::expandAliases(aliasesFloat)) { vect.emplace_back(i, o2::dcs::DPVAL_DOUBLE); @@ -116,16 +128,40 @@ class TRDDCSDataProcessor : public o2::framework::Task } mProcessor = std::make_unique(); - int verbosity = ic.options().get("processor-verbosity"); + int verbosity = ic.options().get("processor-verbosity"); if (verbosity > 0) { LOG(info) << "Using verbose mode for TRD DCS processor"; mProcessor->setVerbosity(verbosity); } + + // LB: set maximum number of alarms in change in FedChamberStatus and FedCFGtag + int alarmfed = ic.options().get("DPs-max-counter-alarm-fed"); + if (alarmfed >= 0) { + LOG(info) << "Setting max number of alarms in FED objects changes to " << alarmfed; + mProcessor->setMaxCounterAlarmFed(alarmfed); + } else { + LOG(info) << "Invalid max number of alarms in FED objects changes " << alarmfed << ", using default value of 1"; + } + + // LB: set minimum number of DPs in DCS Processor to update ChamberStatus/CFGtag + int minupdatefed = ic.options().get("DPs-min-counter-update-fed"); + if (minupdatefed >= 0 && minupdatefed <= 540) { + LOG(info) << "Setting min number of DPs to update ChamberStatus/CFGtag to " << minupdatefed; + mProcessor->setFedMinimunDPsForUpdate(minupdatefed); + } else { + LOG(info) << "Invalid min number of DPs to update ChamberStatus/CFGtag " << alarmfed << ", using default value of 540"; + } + mProcessor->init(vect); mTimerGas = std::chrono::high_resolution_clock::now(); mTimerVoltages = mTimerGas; mTimerCurrents = mTimerGas; mTimerEnv = mTimerGas; + // LB: new DPs for Fed and Cavern + mTimerFedChamberStatus = mTimerGas; + mTimerFedCFGtag = mTimerGas; + mTimerFedEnvTemp = mTimerGas; + mTimerCavern = mTimerGas; mReportTiming = ic.options().get("report-timing") || verbosity > 0; } @@ -169,6 +205,29 @@ class TRDDCSDataProcessor : public o2::framework::Task mTimerEnv = timeNow; } + // LB: processing logic for FedChamberStatus and FedCFGtag + if (mProcessor->shouldUpdateFedChamberStatus()) { + sendDPsoutputFedChamberStatus(pc.outputs()); + } + + if (mProcessor->shouldUpdateFedCFGtag()) { + sendDPsoutputFedCFGtag(pc.outputs()); + } + + // LB: new DP for FedEnvTemp + auto elapsedTimeFedEnvTemp = timeNow - mTimerFedEnvTemp; // in ns + if (elapsedTimeFedEnvTemp.count() * 1e-9 >= mFedEnvTempDPsUpdateInterval) { + sendDPsoutputFedEnvTemp(pc.outputs()); + mTimerFedEnvTemp = timeNow; + } + + // LB: new DP for Cavern + auto elapsedTimeCavern = timeNow - mTimerCavern; // in ns + if (elapsedTimeCavern.count() * 1e-9 >= mCavernDPsUpdateInterval) { + sendDPsoutputCavern(pc.outputs()); + mTimerCavern = timeNow; + } + if (mProcessor->shouldUpdateRun()) { sendDPsoutputRun(pc.outputs()); } @@ -185,6 +244,11 @@ class TRDDCSDataProcessor : public o2::framework::Task sendDPsoutputCurrents(ec.outputs()); sendDPsoutputEnv(ec.outputs()); sendDPsoutputRun(ec.outputs()); + // LB: new DPs for Fed and Cavern + sendDPsoutputFedChamberStatus(ec.outputs()); + sendDPsoutputFedCFGtag(ec.outputs()); + sendDPsoutputFedEnvTemp(ec.outputs()); + sendDPsoutputCavern(ec.outputs()); } private: @@ -194,11 +258,22 @@ class TRDDCSDataProcessor : public o2::framework::Task std::chrono::high_resolution_clock::time_point mTimerVoltages; std::chrono::high_resolution_clock::time_point mTimerCurrents; std::chrono::high_resolution_clock::time_point mTimerEnv; + // LB: new DPs for Fed and Cavern + std::chrono::high_resolution_clock::time_point mTimerFedChamberStatus; + std::chrono::high_resolution_clock::time_point mTimerFedCFGtag; + std::chrono::high_resolution_clock::time_point mTimerFedEnvTemp; + std::chrono::high_resolution_clock::time_point mTimerCavern; + int64_t mGasDPsUpdateInterval; int64_t mVoltagesDPsUpdateInterval; int64_t mCurrentsDPsUpdateInterval; int64_t mMinUpdateIntervalU; int64_t mEnvDPsUpdateInterval; + // LB: new DPs for Fed and Cavern + int64_t mFedChamberStatusDPsUpdateInterval; + int64_t mFedCFGtagDPsUpdateInterval; + int64_t mFedEnvTempDPsUpdateInterval; + int64_t mCavernDPsUpdateInterval; void sendDPsoutputVoltages(DataAllocator& output) { @@ -289,6 +364,87 @@ class TRDDCSDataProcessor : public o2::framework::Task mProcessor->clearRunDPsInfo(); } else { auto& info = mProcessor->getccdbRunDPsInfo(); + // LOG(info) << "Not sending object " << info.getPath() << "/" << info.getFileName() << " since no DPs were processed for it"; + LOG(info) << "Not sending object " << info.getPath() << "/" << info.getFileName() << " as upload of Run DPs was deactivated"; + } + } + + // LB: new DP for FedChamberStatus + //________________________________________________________________ + void sendDPsoutputFedChamberStatus(DataAllocator& output) + { + // extract CCDB infos and calibration object for DPs + if (mProcessor->updateFedChamberStatusDPsCCDB()) { + const auto& payload = mProcessor->getTRDFedChamberStatusDPsInfo(); + auto& info = mProcessor->getccdbFedChamberStatusDPsInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_ChamberStat", 0}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_ChamberStat", 0}, info); + mProcessor->clearFedChamberStatusDPsInfo(); + } else { + auto& info = mProcessor->getccdbFedChamberStatusDPsInfo(); + LOG(info) << "Not sending object " << info.getPath() << "/" << info.getFileName() << " since no DPs were processed for it"; + } + } + + // LB: new DP for FedCFGtag + //________________________________________________________________ + void sendDPsoutputFedCFGtag(DataAllocator& output) + { + // extract CCDB infos and calibration object for DPs + if (mProcessor->updateFedCFGtagDPsCCDB()) { + const auto& payload = mProcessor->getTRDFedCFGtagDPsInfo(); + auto& info = mProcessor->getccdbFedCFGtagDPsInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_CFGtag", 0}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_CFGtag", 0}, info); + mProcessor->clearFedCFGtagDPsInfo(); + } else { + auto& info = mProcessor->getccdbFedCFGtagDPsInfo(); + LOG(info) << "Not sending object " << info.getPath() << "/" << info.getFileName() << " since no DPs were processed for it"; + } + } + + // LB: new DP for FedEnvTemp + //________________________________________________________________ + void sendDPsoutputFedEnvTemp(DataAllocator& output) + { + // extract CCDB infos and calibration object for DPs + if (mProcessor->updateFedEnvTempDPsCCDB()) { + const auto& payload = mProcessor->getTRDFedEnvTempDPsInfo(); + auto& info = mProcessor->getccdbFedEnvTempDPsInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_FedTemp", 0}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_FedTemp", 0}, info); + mProcessor->clearFedEnvTempDPsInfo(); + } else { + auto& info = mProcessor->getccdbFedEnvTempDPsInfo(); + LOG(info) << "Not sending object " << info.getPath() << "/" << info.getFileName() << " since no DPs were processed for it"; + } + } + + // LB: new DP for Cavern + //________________________________________________________________ + void sendDPsoutputCavern(DataAllocator& output) + { + // extract CCDB infos and calibration object for DPs + if (mProcessor->updateCavernDPsCCDB()) { + const auto& payload = mProcessor->getTRDCavernDPsInfo(); + auto& info = mProcessor->getccdbCavernDPsInfo(); + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, &info); + LOG(info) << "Sending object " << info.getPath() << "/" << info.getFileName() << " of size " << image->size() + << " bytes, valid for " << info.getStartValidityTimestamp() << " : " << info.getEndValidityTimestamp(); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_DCSCavernDPs", 0}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_DCSCavernDPs", 0}, info); + mProcessor->clearCavernDPsInfo(); + } else { + auto& info = mProcessor->getccdbCavernDPsInfo(); LOG(info) << "Not sending object " << info.getPath() << "/" << info.getFileName() << " since no DPs were processed for it"; } } @@ -314,6 +470,16 @@ DataProcessorSpec getTRDDCSDataProcessorSpec() outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_DCSRunDPs"}); outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_DCSEnvDPs"}); outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_DCSEnvDPs"}); + // LB: new DPs for Fed and Cavern + // Must use reduced names due to initializer string cannot exceed descriptor size in Data Format + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_ChamberStat"}); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_ChamberStat"}); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_CFGtag"}); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_CFGtag"}); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_FedTemp"}); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_FedTemp"}); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "TRD_DCSCavernDPs"}); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "TRD_DCSCavernDPs"}); return DataProcessorSpec{ "trd-dcs-data-processor", @@ -328,7 +494,11 @@ DataProcessorSpec getTRDDCSDataProcessorSpec() {"DPs-update-interval-voltages", VariantType::Int64, 600ll, {"Interval (in s) after which to update the DPs CCDB entry for voltage parameters"}}, {"DPs-update-interval-env", VariantType::Int64, 1800ll, {"Interval (in s) after which to update the DPs CCDB entry for environment parameters"}}, {"DPs-min-update-interval-voltages", VariantType::Int64, 120ll, {"Minimum range to be covered by voltage CCDB object"}}, - {"DPs-update-interval-gas", VariantType::Int64, 900ll, {"Interval (in s) after which to update the DPs CCDB entry for gas parameters"}}}}; + {"DPs-update-interval-gas", VariantType::Int64, 900ll, {"Interval (in s) after which to update the DPs CCDB entry for gas parameters"}}, + {"DPs-update-interval-fedenv", VariantType::Int64, 1800ll, {"Interval (in s) after which to update the DPs CCDB entry for front end device environment parameters"}}, + {"DPs-update-interval-cavern", VariantType::Int64, 7200ll, {"Interval (in s) after which to update the DPs CCDB entry for cavern parameters"}}, + {"DPs-max-counter-alarm-fed", VariantType::Int, 1, {"Maximum number of alarms after FedChamberStatus and FedCFGtag changes, following changes are logged as warnings"}}, + {"DPs-min-counter-update-fed", VariantType::Int, 540, {"Minimum number of DPs to update FedChamberStatus and FedCFGtag objects"}}}}; } } // namespace framework diff --git a/Detectors/TRD/calibration/workflow/trd-dcs-sim-workflow.cxx b/Detectors/TRD/calibration/workflow/trd-dcs-sim-workflow.cxx index 41cf22208100c..6bb6ecf0a96b9 100644 --- a/Detectors/TRD/calibration/workflow/trd-dcs-sim-workflow.cxx +++ b/Detectors/TRD/calibration/workflow/trd-dcs-sim-workflow.cxx @@ -32,19 +32,19 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_hvDriftUmon[00..539]", 2249., 2250.}); // temperatures, pressures, config and other - // dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_fedCFGtag[00..539]", "foo", "bar"}); + dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_fedCFGtag[00..539]", "foo", "bar"}); // FIXME if I put a longer string here, e.g. "cf2_krypton_tb30:r5927" then dcs-random-data-generator crashes (std::bad_alloc or std::length_error) - // dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_fedChamberStatus[00..539]", 0, 255}); - // dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_fedEnvTemp[00..539]", 10., 40.}); + dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_fedChamberStatus[00..539]", 1, 5}); + dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_fedEnvTemp[00..539]", 10., 40.}); dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_aliEnvTempCavern", 0, 100.}); dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_aliEnvTempP2", 0, 100.}); dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_aliEnvPressure00", 0, 100.}); dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_aliEnvPressure01", 0, 100.}); dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_aliEnvPressure02", 0, 100.}); - // dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_cavernHumidity", 0, 100.}); + dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_cavernHumidity", 0, 100.}); dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_runNo", 254, 255}); - dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_runType", 254, 255}); + dphints.emplace_back(o2::dcs::test::DataPointHint{"trd_runType", 1, 3}); return o2::framework::WorkflowSpec{o2::dcs::test::getDCSRandomDataGeneratorSpec(dphints, "TRD")}; } diff --git a/Detectors/TRD/reconstruction/include/TRDReconstruction/CruRawReader.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CruRawReader.h index 23b9d809eecfd..14a4af07f66f3 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CruRawReader.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CruRawReader.h @@ -63,6 +63,14 @@ class CruRawReader // configure the raw reader, done once at the init() stage void configure(int tracklethcheader, int halfchamberwords, int halfchambermajor, std::bitset<16> options); + // set number of time bins to fixed value instead of reading from DigitHCHeader + // (but still complain if DigitHCHeader is not consistent) + void setNumberOfTimeBins(int tb) + { + mTimeBins = tb; + mTimeBinsFixed = true; + } + // settings in order to avoid InfoLogger flooding void setMaxErrWarnPrinted(int nerr, int nwar) { @@ -111,8 +119,10 @@ class CruRawReader // given the total link size and the hcid from the RDH // parse the tracklet data. Overwrite hcid from TrackletHCHeader if mismatch is detected // trackletWordsRejected: count the number of words which were skipped (subset of words read) - // returns total number of words read (no matter if parsed successfully or not) - int parseTrackletLinkData(int linkSize32, int& hcid, int& trackletWordsRejected); + // trackletWordsReadOK: count the number of words which could be read consecutively w/o errors + // numberOfTrackletsFound: count the number of tracklets found + // returns total number of words read (no matter if parsed successfully or not) or -1 in case of failure + int parseTrackletLinkData(int linkSize32, int& hcid, int& trackletWordsRejected, int& trackletWordsReadOK, int& numberOfTrackletsFound); // the parsing begins after the DigitHCHeaders have been parsed already // maxWords32 is the remaining number of words for the given link @@ -165,6 +175,7 @@ class CruRawReader bool mPreviousHalfCRUHeaderSet; // flag, whether we can use mPreviousHalfCRUHeader for additional sanity checks DigitHCHeader mDigitHCHeader; // Digit HalfChamber header we are currently on. uint16_t mTimeBins{constants::TIMEBINS}; // the number of time bins to be read out (default 30, can be overwritten from digit HC header) + bool mTimeBinsFixed{false}; // flag, whether number of time bins different from default was configured bool mHaveSeenDigitHCHeader3{false}; // flag, whether we can compare an incoming DigitHCHeader3 with a header we have seen before uint32_t mPreviousDigitHCHeadersvnver; // svn ver in the digithalfchamber header, used for validity checks uint32_t mPreviousDigitHCHeadersvnrver; // svn release ver also used for validity checks diff --git a/Detectors/TRD/reconstruction/macros/checkRawStats.C b/Detectors/TRD/reconstruction/macros/checkRawStats.C index 96a6fa85a796c..82661a4d55d91 100644 --- a/Detectors/TRD/reconstruction/macros/checkRawStats.C +++ b/Detectors/TRD/reconstruction/macros/checkRawStats.C @@ -24,9 +24,10 @@ #include "DataFormatsTRD/RawDataStats.h" #include "DataFormatsTRD/Constants.h" #include "DataFormatsTRD/HelperMethods.h" - +#include #include #include +#include #include #endif @@ -81,12 +82,23 @@ void checkRawStats() c->Divide(2, 2); gStyle->SetOptStat(""); + std::vector tfIndices(chain.GetEntries()); + std::iota(tfIndices.begin(), tfIndices.end(), static_cast(0)); + std::vector tfCounters; + for (int iEntry = 0; iEntry < chain.GetEntries(); ++iEntry) { chain.GetEntry(iEntry); // for each TimeFrame there is one tree entry + tfCounters.push_back(rawstats.mTFIDInfo.tfCounter); + } + std::sort(tfIndices.begin(), tfIndices.end(), [&](size_t a, size_t b) { return tfCounters[a] < tfCounters[b]; }); + LOGP(info, "Got {} TFs in total from {} tree entries", tfCounters.size(), chain.GetEntries()); + + for (int iEntry = 0; iEntry < chain.GetEntries(); ++iEntry) { + chain.GetEntry(tfIndices[iEntry]); int triggerCounter = 0; for (const auto& stats : linkstats) { c->Update(); - c->SetTitle(TString::Format("TF %i, Trigger %i", iEntry, triggerCounter)); + c->SetTitle(TString::Format("Run %i: TF %u, Trigger %i", rawstats.mTFIDInfo.runNumber, rawstats.mTFIDInfo.tfCounter, triggerCounter)); for (int hcid = 0; hcid < MAXHALFCHAMBER; ++hcid) { int stackLayer = HelperMethods::getStack(hcid / 2) * NLAYER + HelperMethods::getLayer(hcid / 2); int sectorSide = (hcid / NHCPERSEC) * 2 + (hcid % 2); diff --git a/Detectors/TRD/reconstruction/src/CruCompressor.cxx b/Detectors/TRD/reconstruction/src/CruCompressor.cxx deleted file mode 100644 index 85fd3baceb8f4..0000000000000 --- a/Detectors/TRD/reconstruction/src/CruCompressor.cxx +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file crucompressor.cxx -/// @author Sean Murray -/// @brief Basic DPL workflow for TRD CRU output(raw) to tracklet/digit data. -/// There may or may not be some compression in this at some point. - -#include "TRDReconstruction/CruCompressorTask.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/ConfigParamSpec.h" -#include "Framework/ConcreteDataMatcher.h" -#include "Framework/Logger.h" -#include "DetectorsRaw/RDHUtils.h" - -using namespace o2::framework; - -// add workflow options, note that customization needs to be declared before -// including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - auto config = ConfigParamSpec{"trd-crucompressor-config", VariantType::String, "A:TRD/RAWDATA", {"TRD raw data config"}}; - auto outputDesc = ConfigParamSpec{"trd-crucompressor-output-desc", VariantType::String, "TRDTLT", {"Output specs description string"}}; - auto verbosity = ConfigParamSpec{"trd-crucompressor-verbose", VariantType::Bool, false, {"Enable verbose compressor"}}; - auto verboseheaders = ConfigParamSpec{"trd-crucompressor-hedaerverbose", VariantType::Bool, false, {"Enable header verbose compressor"}}; - auto verbosedata = ConfigParamSpec{"trd-crucompressor-dataverbose", VariantType::Bool, false, {"Enable data verbose compressor"}}; - - auto digithcheader = ConfigParamSpec{"trd-crucompressor-digitheader", VariantType::Bool, true, {"using digit half chamber headers"}}; - auto tracklethcheader = ConfigParamSpec{"trd-crucompressor-tracklethcheader", VariantType::Bool, true, {"using tracklet half chamber headers"}}; - auto trackletformat = ConfigParamSpec{"trd-crucompressor-trackletformat", VariantType::Int, 0, {"0: no pid scale factor, 1: pid scale factor"}}; - auto digitformat = ConfigParamSpec{"trd-crucompressor-digitformat", VariantType::Int, 1, {"0: zero supressed digits, 1: non zero suppressed digits "}}; - - workflowOptions.push_back(config); - workflowOptions.push_back(outputDesc); - workflowOptions.push_back(verbosity); - workflowOptions.push_back(verboseheaders); - workflowOptions.push_back(verbosedata); - workflowOptions.push_back(digithcheader); - workflowOptions.push_back(tracklethcheader); - workflowOptions.push_back(trackletformat); - workflowOptions.push_back(digitformat); -} - -#include "Framework/runDataProcessing.h" // the main driver - -/// This function hooks up the the workflow specifications into the DPL driver. -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - - auto config = cfgc.options().get("trd-crucompressor-config"); - auto verbosity = cfgc.options().get("trd-crucompressor-verbose"); - std::vector outputs; - outputs.emplace_back(OutputSpec(ConcreteDataTypeMatcher{"TRD", "CDATA"})); - - AlgorithmSpec algoSpec; - algoSpec = AlgorithmSpec{adaptFromTask()}; - - WorkflowSpec workflow; - - /* - * This is originaly replicated from TOF - We define at run time the number of devices to be attached - to the workflow and the input matching string of the device. - This is is done with a configuration string like the following - one, where the input matching for each device is provide in - comma-separated strings. For instance - A:TRD/RAWDATA/785;B:TRF/RAWDATA/2560,C:TRD/RAWDATA/1280;D:TRD/RAWDATA/1536 - - will lead to a workflow with 2 devices which will input match - - trd-crucompressor-0 --> A:TRD/RAWDATA/768;B:TRD/RAWDATA/1024 - trd-crucompressor-1 --> C:TRD/RAWDATA/1280;D:TRD/RAWDATA/1536 - The number after the RAWDATA is the FeeID in decimal - */ - - std::stringstream ssconfig(config); - std::string iconfig; - int idevice = 0; - LOG(info) << " config string is : " << config; - LOG(info) << "for now ignoring the multiple processors, something going wrong"; - while (getline(ssconfig, iconfig, ',')) { // for now we will keep the possibilty to have a device per half cru/feeid i.e. 6 per flp - // this is probably never going to be used but would to nice to know hence here. - workflow.emplace_back(DataProcessorSpec{ - std::string("trd-crucompressor-") + std::to_string(idevice), - select(iconfig.c_str()), - outputs, - algoSpec, - Options{ - {"trd-crucompressor-verbose", VariantType::Bool, false, {"verbose flag"}}}}); - idevice++; - } - - return workflow; -} diff --git a/Detectors/TRD/reconstruction/src/CruCompressorTask.cxx b/Detectors/TRD/reconstruction/src/CruCompressorTask.cxx deleted file mode 100644 index 89a3ba421ac96..0000000000000 --- a/Detectors/TRD/reconstruction/src/CruCompressorTask.cxx +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file CruCompressorTask.cxx -/// @author Sean Murray -/// @brief TRD cru output to tracklet task - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/RawDeviceService.h" -#include "Framework/DeviceSpec.h" -#include "Framework/DataSpecUtils.h" -#include "Headers/RDHAny.h" - -#include "DataFormatsTRD/Constants.h" -#include "DataFormatsTRD/CompressedHeader.h" - -#include "TRDReconstruction/CruCompressorTask.h" -#include "TRDReconstruction/CruRawReader.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace trd -{ - -void CruCompressorTask::init(InitContext& ic) -{ - LOG(info) << "FLP Compressore Task init"; -} - -uint64_t CruCompressorTask::buildEventOutput() -{ - //mReader holds the vectors of tracklets and digits. - // tracklets are 64 bit - // digits are DigitMCMHeader and DigitMCMData - - uint64_t currentpos = 0; - uint64_t trailer = 0xeeeeeeeeeeeeeeeeLL; - //first we write a start rdh block - CompressedRawHeader* trackletheader = (CompressedRawHeader*)&mOutBuffer[0]; - trackletheader->format = 1; - trackletheader->eventtime = 99; - currentpos = 1; - //write the - std::vector ir; - std::vector tracklets; - std::vector digits; - trackletheader->bc = ir[0].getBCData().bc; - trackletheader->orbit = ir[0].getBCData().orbit; - trackletheader->padding = 0xeeee; - trackletheader->size = mReader.sumTrackletsFound() * 8; // to get to bytes. TODO compare to getTrackletsFound - /* - for (auto tracklet : mReader.getTracklets(ir[0].getBCData())) { - //convert tracklet to 64 bit and add to blob - mOutBuffer[currentpos++] = tracklet.getTrackletWord(); - } - */ - CompressedRawTrackletDigitSeperator* tracklettrailer = (CompressedRawTrackletDigitSeperator*)&mOutBuffer[currentpos]; - tracklettrailer->word = trailer; - currentpos++; - CompressedRawHeader* digitheader = (CompressedRawHeader*)&mOutBuffer[currentpos]; - currentpos++; - digitheader->format = 2; - digitheader->eventtime = 99; - - /* - for (auto digit : mReader.getDigits(ir[0].getBCData())) { - uint64_t* word; - word = &mOutBuffer[currentpos]; - DigitMCMHeader mcmheader; - mcmheader.eventcount = 1; - mcmheader.mcm = digit.getMCM(); - mcmheader.rob = digit.getROB(); - mcmheader.yearflag = 1; - mcmheader.eventcount = 1; - mcmheader.res = 0xc; // formst is reservedto be 1100 binary - //unpack the digit. - mcmheader.word = (*word) >> 32; - currentpos++; - } - */ - CompressedRawTrackletDigitSeperator* digittrailer = (CompressedRawTrackletDigitSeperator*)&mOutBuffer[currentpos]; - digittrailer->word = trailer; - currentpos++; - //as far as I can tell this is almost always going to be blank. - CompressedRawHeader* configheader = (CompressedRawHeader*)&mOutBuffer[currentpos]; - currentpos++; - configheader->size = 2; - configheader->format = 3; - configheader->eventtime = 99; - CompressedRawTrackletDigitSeperator* configtrailer = (CompressedRawTrackletDigitSeperator*)&mOutBuffer[currentpos]; - configtrailer->word = trailer; - //finally we write a stop rdh block - - return currentpos; -} - -void CruCompressorTask::run(ProcessingContext& pc) -{ - LOG(info) << "TRD Compression Task run method"; - - auto device = pc.services().get().device(); - auto outputRoutes = pc.services().get().spec().outputs; - auto fairMQChannel = outputRoutes.at(0).channel; - int inputcount = 0; - /* loop over inputs routes */ - for (auto iit = pc.inputs().begin(), iend = pc.inputs().end(); iit != iend; ++iit) { - if (!iit.isValid()) { - continue; - } - //LOG(info) << "iit.mInputs " << iit.mInputs. - /* prepare output parts */ - fair::mq::Parts parts; - - /* loop over input parts */ - for (auto const& ref : iit) { - - auto headerIn = DataRefUtils::getHeader(ref); - auto dataProcessingHeaderIn = DataRefUtils::getHeader(ref); - auto payloadIn = ref.payload; - auto payloadInSize = DataRefUtils::getPayloadSize(ref); - std::cout << "payload In is : " << std::hex << payloadIn << std::endl; - std::cout << "payload In is : " << std::dec << payloadIn << std::endl; - std::cout << "payload In size is : " << std::dec << payloadInSize << std::endl; - mReader.setDataBuffer(payloadIn); - mReader.setDataBufferSize(payloadInSize); - /* run */ - mReader.run(); - - auto payloadOutSize = buildEventOutput(); - - auto payloadOutSizeBytes = payloadOutSize * 8; // payloadoutsize in bytes. - LOG(info) << "outgoing message has a data size of : " << payloadOutSize; - if (payloadOutSizeBytes > 32 * 1024) { - LOG(warn) << " buffer size for data is >32kB so will span rdh"; - } - int numberofpiecestocutinto = payloadOutSizeBytes / (32 * 1024); - int segmentsize = 32 * 1024 - 0x40; // 0x40 is the size of the rdh header in bytes. - //the above will drop the decimal due to int. - numberofpiecestocutinto++; - for (int datasegment = 0; datasegment < numberofpiecestocutinto; ++datasegment) { - auto payloadMessage = device->NewMessage(payloadOutSize); - std::memcpy(payloadMessage->GetData(), (char*)mOutBuffer.data() + datasegment * segmentsize, payloadOutSize); - /* output */ - auto headerOut = *headerIn; - auto dataProcessingHeaderOut = *dataProcessingHeaderIn; - headerOut.dataDescription = "CDATA"; - headerOut.payloadSize = payloadOutSizeBytes; - // what to do about the packet count? - //headerOut.packetCounter; - o2::header::Stack headerStack{headerOut, dataProcessingHeaderOut}; - auto headerMessage = device->NewMessage(headerStack.size()); - std::memcpy(headerMessage->GetData(), headerStack.data(), headerStack.size()); - - /* add parts */ - parts.AddPart(std::move(headerMessage)); - parts.AddPart(std::move(payloadMessage)); - } - } - - /* send message */ - device->Send(parts, fairMQChannel); - } -} - -} // namespace trd -} // namespace o2 diff --git a/Detectors/TRD/reconstruction/src/CruRawReader.cxx b/Detectors/TRD/reconstruction/src/CruRawReader.cxx index beb0b92869147..b2370dfc46b1b 100644 --- a/Detectors/TRD/reconstruction/src/CruRawReader.cxx +++ b/Detectors/TRD/reconstruction/src/CruRawReader.cxx @@ -303,7 +303,7 @@ bool CruRawReader::parseDigitHCHeaders(int hcid) mPreTriggerPhase = header1.ptrigphase; headersfound.set(0); - if ((header1.numtimebins > TIMEBINS) || (header1.numtimebins < 3)) { + if ((header1.numtimebins > TIMEBINS) || (header1.numtimebins < 3) || mTimeBinsFixed && header1.numtimebins != mTimeBins) { if (mOptions[TRDVerboseErrorsBit]) { LOGF(warn, "According to Digit HC Header 1 there are %i time bins configured", (int)header1.numtimebins); printDigitHCHeader(mDigitHCHeader, headers.data()); @@ -500,7 +500,9 @@ bool CruRawReader::processHalfCRU(int iteration) mEventRecords.incLinkWords(halfChamberId, mCurrentHalfCRULinkLengths[currentlinkindex]); uint32_t currentlinksize32 = mCurrentHalfCRULinkLengths[currentlinkindex] * 8; // x8 to go from 256 bits to 32 bit; uint32_t endOfCurrentLink = mHBFoffset32 + currentlinksize32; - + if (mOptions[TRDVerboseBit] && currentlinksize32 > 0) { + LOGP(info, "Reading {} (link ID {}, HCID {}) with {} 32-bit words", HelperMethods::getSectorStackLayerSide(halfChamberId), linkIdxGlobal, halfChamberId, currentlinksize32); + } linksizeAccum32 += currentlinksize32; if (currentlinksize32 == 0) { mEventRecords.incLinkNoData(halfChamberId); @@ -521,8 +523,13 @@ bool CruRawReader::processHalfCRU(int iteration) LOGF(info, "Tracklet parser starting at offset %u and processing up to %u words", mHBFoffset32, currentlinksize32); } int trackletWordsRejected = 0; - int trackletWordsRead = parseTrackletLinkData(currentlinksize32, halfChamberId, trackletWordsRejected); + int trackletWordsReadOK = 0; + int numberOfTrackletsFound = 0; // count the number of found tracklets for this link + int trackletWordsRead = parseTrackletLinkData(currentlinksize32, halfChamberId, trackletWordsRejected, trackletWordsReadOK, numberOfTrackletsFound); std::chrono::duration trackletparsingtime = std::chrono::high_resolution_clock::now() - trackletparsingstart; + if (mOptions[TRDVerboseBit]) { + LOGP(info, "Could read {} 32-bit words w/o errors, found {} tracklets, rejected {} 32-bit words for {}. Parsing OK? {}", trackletWordsReadOK, numberOfTrackletsFound, trackletWordsRejected, HelperMethods::getSectorStackLayerSide(halfChamberId), (trackletWordsRead != -1 && trackletWordsRejected == 0)); + } if (trackletWordsRead == -1) { // something went wrong bailout of here. mHBFoffset32 = hbfOffsetTmp + linksizeAccum32; @@ -544,7 +551,7 @@ bool CruRawReader::processHalfCRU(int iteration) } mEventRecords.getCurrentEventRecord().incTrackletTime(trackletparsingtime.count()); if (mOptions[TRDVerboseBit]) { - LOGF(info, "Read %i tracklet words and rejected %i words", trackletWordsRead, trackletWordsRejected); + LOGF(debug, "Read %i tracklet words and rejected %i words", trackletWordsRead, trackletWordsRejected); } mTrackletWordsRejected += trackletWordsRejected; mTrackletWordsRead += trackletWordsRead; @@ -760,10 +767,10 @@ int CruRawReader::parseDigitLinkData(int maxWords32, int hcid, int& wordsRejecte } else if (state == StateDigitMCMData) { - std::array adcValues; bool exitChannelLoop = false; state = StateDigitMCMHeader; // after we are done reading the ADC data, by default we expect another MCM header for (int iChannel = 0; iChannel < NADCMCM; ++iChannel) { + std::array adcValues{}; if (!(mDigitHCHeader.major & 0x20) || adcMask.adcmask & (1UL << iChannel)) { // either ZS is OFF, or the adcMask has iChannel flagged as active DigitMCMData data; @@ -836,10 +843,9 @@ int CruRawReader::parseDigitLinkData(int maxWords32, int hcid, int& wordsRejecte } // Returns number of words read (>=0) or error state (<0) -int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRejected) +int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRejected, int& wordsReadOK, int& trackletsFound) { int wordsRead = 0; // count the number of words which were parsed (both successful and not successful) - int numberOfTrackletsFound = 0; // count the number of found tracklets for this link int state = StateTrackletHCHeader; // we expect to always see a TrackletHCHeader at the beginning of the link // tracklet data for one link is expected to arrive ordered // first MCM in row=0, column=0, then row=0, column=1, ... row=1, column=0, ... @@ -865,6 +871,7 @@ int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRej // either the TrackletHCHeader is corrupt or we have only digits on this link return 0; } + ++wordsReadOK; state = StateTrackletMCMHeader; // we do expect tracklets following } else { // we always expect a TrackletHCHeader as first word for a link (default) @@ -876,18 +883,23 @@ int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRej ++wordsRejected; continue; } + ++wordsReadOK; state = StateTrackletMCMHeader; // we might have tracklets following or tracklet end markers } } // StateTrackletHCHeader else if (state == StateTrackletMCMHeader) { if (currWord == TRACKLETENDMARKER) { + ++wordsReadOK; state = StateSecondEndmarker; // we expect a second tracklet end marker to follow } else { mcmHeader.word = currWord; if (!sanityCheckTrackletMCMHeader(mcmHeader)) { incrementErrors(TrackletMCMHeaderSanityCheckFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletMCMHeader", currWord)); - state = StateMoveToEndMarker; // invalid MCM header, no chance to interpret the following MCM data + // invalid MCM header, but we can try to find another valid MCM header before the end markers. + // If there are no end markers we can anyhow not parse digits if they were there. + // Other invalid MCM headers can be caught by the check on their ordering (row/column) + // state = StateMoveToEndMarker; ++wordsRead; ++wordsRejected; continue; @@ -896,13 +908,20 @@ int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRej if (previousColumn >= 0) { if (mcmHeader.padrow < previousRow || (mcmHeader.padrow == previousRow && mcmHeader.col < previousColumn)) { incrementErrors(TrackletDataWrongOrdering, hcid, fmt::format("Current padrow/column = {}/{}, previous padrow/column = {}/{}", (int)mcmHeader.padrow, (int)mcmHeader.col, previousRow, previousColumn)); + ++wordsRead; + ++wordsRejected; + continue; } else if (mcmHeader.padrow == previousRow && mcmHeader.col == previousColumn) { incrementErrors(TrackletDataDuplicateMCM, hcid, fmt::format("Second MCM header {:#010x} for padrow/column = {}/{}", currWord, previousRow, previousColumn)); + ++wordsRead; + ++wordsRejected; + continue; } } else { previousColumn = mcmHeader.col; previousRow = mcmHeader.padrow; } + ++wordsReadOK; state = StateTrackletMCMData; // tracklet words must be following, unless the HC header format indicates sending of empty MCM headers } ++wordsRead; @@ -926,20 +945,23 @@ int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRej break; } if ((currWord & 0x1) == 0x1) { - // the reserved bit of the trackler MCM data is set + // the reserved bit of the tracklet MCM data is set, we don't create a tracklet from this word incrementErrors(TrackletMCMDataFailure, hcid, fmt::format("Invalid word {:#010x} for the expected TrackletMCMData", currWord)); ++wordsRejected; + ++wordsRead; + continue; } TrackletMCMData mcmData; mcmData.word = currWord; mEventRecords.getCurrentEventRecord().addTracklet(assembleTracklet64(hcHeader.format, mcmHeader, mcmData, iCpu, hcid)); - ++numberOfTrackletsFound; + ++trackletsFound; ++mTrackletsFound; addedTracklet = true; ++wordsRead; + ++wordsReadOK; if (wordsRead == linkSize32) { incrementErrors(TrackletNoTrackletEndMarker, hcid, fmt::format("After reading the word {:#010x} we are at the end of the link data", currWord)); - return wordsRead; + return -1; } currWord = mHBFPayload[mHBFoffset32 + wordsRead]; } @@ -969,9 +991,10 @@ int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRej else if (state == StateSecondEndmarker) { ++wordsRead; if (currWord != TRACKLETENDMARKER) { - incrementErrors(TrackletNoSecondEndMarker, hcid, fmt::format("Expected second tracklet end marker, but found {:#010x} instead", currWord)); + incrementErrors(TrackletNoSecondEndMarker, hcid, fmt::format("Invalid word {:#010x} for the expected second end marker", currWord)); return -1; } + ++wordsReadOK; state = StateFinished; } // StateSecondEndmarker @@ -992,7 +1015,7 @@ int CruRawReader::parseTrackletLinkData(int linkSize32, int& hcid, int& wordsRej } // end of state machine - if (mTrackletHCHeaderState == 1 && numberOfTrackletsFound == 0) { + if (mTrackletHCHeaderState == 1 && trackletsFound == 0) { if (mMaxErrsPrinted > 0) { LOG(error) << "We found a TrackletHCHeader in mode 1, but did not add any tracklets"; checkNoErr(); diff --git a/Detectors/TRD/reconstruction/src/DataReader.cxx b/Detectors/TRD/reconstruction/src/DataReader.cxx index 3e24fe0440153..4a5d74523c530 100644 --- a/Detectors/TRD/reconstruction/src/DataReader.cxx +++ b/Detectors/TRD/reconstruction/src/DataReader.cxx @@ -105,6 +105,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) algoSpec, Options{{"log-max-errors", VariantType::Int, 20, {"maximum number of errors to log"}}, {"log-max-warnings", VariantType::Int, 20, {"maximum number of warnings to log"}}, + {"number-of-TBs", VariantType::Int, -1, {"set to >=0 in order to overwrite number of time bins"}}, {"every-nth-tf", VariantType::Int, 1, {"process only every n-th TF"}}}}); if (!cfgc.options().get("disable-root-output")) { diff --git a/Detectors/TRD/reconstruction/src/DataReaderTask.cxx b/Detectors/TRD/reconstruction/src/DataReaderTask.cxx index 7bba5d2b085d4..2e1232e15aa4e 100644 --- a/Detectors/TRD/reconstruction/src/DataReaderTask.cxx +++ b/Detectors/TRD/reconstruction/src/DataReaderTask.cxx @@ -32,6 +32,11 @@ namespace o2::trd void DataReaderTask::init(InitContext& ic) { mReader.setMaxErrWarnPrinted(ic.options().get("log-max-errors"), ic.options().get("log-max-warnings")); + int nTimeBins = ic.options().get("number-of-TBs"); + if (nTimeBins >= 0) { + LOGP(info, "Number of time bins set to {} externally", nTimeBins); + mReader.setNumberOfTimeBins(nTimeBins); + } mReader.configure(mTrackletHCHeaderState, mHalfChamberWords, mHalfChamberMajor, mOptions); mProcessEveryNthTF = ic.options().get("every-nth-tf"); } diff --git a/Detectors/TRD/reconstruction/src/EventRecord.cxx b/Detectors/TRD/reconstruction/src/EventRecord.cxx index ab9207d2fc74d..147b052a8ca3f 100644 --- a/Detectors/TRD/reconstruction/src/EventRecord.cxx +++ b/Detectors/TRD/reconstruction/src/EventRecord.cxx @@ -28,6 +28,7 @@ #include "Framework/InputRecordWalker.h" #include "DataFormatsTRD/Constants.h" +#include "DetectorsBase/TFIDInfoHelper.h" #include #include @@ -81,6 +82,7 @@ void EventRecordContainer::sendData(o2::framework::ProcessingContext& pc, bool g pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTRD, "TRKTRGRD", 0, o2::framework::Lifetime::Timeframe}, triggers); if (generatestats) { accumulateStats(); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mTFStats.mTFIDInfo); pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginTRD, "RAWSTATS", 0, o2::framework::Lifetime::Timeframe}, mTFStats); } if (sendLinkStats) { diff --git a/Detectors/TRD/workflow/CMakeLists.txt b/Detectors/TRD/workflow/CMakeLists.txt index 069e5f11dc00e..53bb79565c8c4 100644 --- a/Detectors/TRD/workflow/CMakeLists.txt +++ b/Detectors/TRD/workflow/CMakeLists.txt @@ -21,7 +21,7 @@ o2_add_library(TRDWorkflow src/EntropyDecoderSpec.cxx src/EntropyEncoderSpec.cxx src/TrackBasedCalibSpec.cxx - src/KrClustererSpec.cxx + include/TRDWorkflow/KrClustererSpec.h include/TRDWorkflow/VdAndExBCalibSpec.h include/TRDWorkflow/GainCalibSpec.h include/TRDWorkflow/TRDPulseHeightSpec.h @@ -44,6 +44,7 @@ o2_add_library(TRDWorkflow O2::GlobalTrackingWorkflowReaders O2::GPUWorkflowHelper O2::ReconstructionDataFormats + O2::FT0Reconstruction O2::ITSWorkflow O2::TPCWorkflow O2::TRDWorkflowIO diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/KrClustererSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/KrClustererSpec.h index 649e4637eba54..26be31d51c084 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/KrClustererSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/KrClustererSpec.h @@ -19,7 +19,13 @@ // input TRD digits, TRD trigger records // output Kr clusters +#include "TRDWorkflow/KrClustererSpec.h" +#include "TRDCalibration/KrClusterFinder.h" +#include "Framework/Task.h" +#include "Framework/ConfigParamRegistry.h" #include "Framework/DataProcessorSpec.h" +#include "TStopwatch.h" +#include using namespace o2::framework; @@ -27,10 +33,68 @@ namespace o2 { namespace trd { -/// create a processor spec -framework::DataProcessorSpec getKrClustererSpec(); +class TRDKrClustererDevice : public Task +{ + public: + TRDKrClustererDevice() = default; + ~TRDKrClustererDevice() override = default; + void init(InitContext& ic) final; + void run(ProcessingContext& pc) final; + void endOfStream(framework::EndOfStreamContext& ec) final; + + private: + o2::trd::KrClusterFinder mKrClFinder; +}; + +void TRDKrClustererDevice::init(InitContext& ic) +{ + mKrClFinder.init(); +} + +void TRDKrClustererDevice::run(ProcessingContext& pc) +{ + TStopwatch timer; + + const auto digits = pc.inputs().get>("digits"); + const auto triggerRecords = pc.inputs().get>("triggerRecords"); + + mKrClFinder.reset(); + mKrClFinder.setInput(digits, triggerRecords); + timer.Start(); + mKrClFinder.findClusters(); + timer.Stop(); + + LOGP(info, "Found {} Kr clusters in {} input trigger records. Timing: CPU: {}, Real: {}", + mKrClFinder.getKrClusters().size(), triggerRecords.size(), timer.CpuTime(), timer.RealTime()); + + pc.outputs().snapshot(Output{o2::header::gDataOriginTRD, "KRCLUSTER", 0, Lifetime::Timeframe}, mKrClFinder.getKrClusters()); + pc.outputs().snapshot(Output{o2::header::gDataOriginTRD, "TRGKRCLS", 0, Lifetime::Timeframe}, mKrClFinder.getKrTrigRecs()); +} + +void TRDKrClustererDevice::endOfStream(EndOfStreamContext& ec) +{ + LOG(info) << "Done with the cluster finding (EoS received)"; +} + +framework::DataProcessorSpec getKrClustererSpec() +{ + std::vector inputs; + inputs.emplace_back("digits", ConcreteDataTypeMatcher{o2::header::gDataOriginTRD, "DIGITS"}, Lifetime::Timeframe); + inputs.emplace_back("triggerRecords", ConcreteDataTypeMatcher{o2::header::gDataOriginTRD, "TRKTRGRD"}, Lifetime::Timeframe); + std::vector outputs; + outputs.emplace_back(o2::header::gDataOriginTRD, "KRCLUSTER", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginTRD, "TRGKRCLS", 0, Lifetime::Timeframe); + + return DataProcessorSpec{ + "trd-kr-clusterer", + inputs, + outputs, + AlgorithmSpec{adaptFromTask()}, + Options{}}; +} } // namespace trd + } // namespace o2 #endif // O2_TRD_KRCLUSTERERSPEC_H diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h index 01688842333b4..6ba185ee28ccb 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h @@ -22,6 +22,7 @@ #include "DetectorsBase/GRPGeomHelper.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "TRDCalibration/PulseHeight.h" +#include "DataFormatsTRD/PHData.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -50,6 +51,8 @@ class PuseHeightDevice : public o2::framework::Task mRunStopRequested = false; } if (mRunStopRequested) { + std::vector mPHValues{}; // the calibration expects data at every TF, so inject dummy + pc.outputs().snapshot(Output{"TRD", "PULSEHEIGHT", 0, Lifetime::Timeframe}, mPHValues); return; } RecoContainer recoData; diff --git a/Detectors/TRD/workflow/io/CMakeLists.txt b/Detectors/TRD/workflow/io/CMakeLists.txt index 198113e2cdd46..4405d12b4f987 100644 --- a/Detectors/TRD/workflow/io/CMakeLists.txt +++ b/Detectors/TRD/workflow/io/CMakeLists.txt @@ -22,7 +22,7 @@ o2_add_library(TRDWorkflowIO src/TRDTrackReaderSpec.cxx src/TRDCalibWriterSpec.cxx src/TRDPHReaderSpec.cxx - src/KrClusterWriterSpec.cxx + include/TRDWorkflowIO/KrClusterWriterSpec.h PUBLIC_LINK_LIBRARIES O2::DataFormatsTRD O2::SimulationDataFormat O2::DPLUtils O2::GPUDataTypeHeaders O2::DataFormatsTPC) diff --git a/Detectors/TRD/workflow/io/include/TRDWorkflowIO/KrClusterWriterSpec.h b/Detectors/TRD/workflow/io/include/TRDWorkflowIO/KrClusterWriterSpec.h index a33886639c813..944ba6987806f 100644 --- a/Detectors/TRD/workflow/io/include/TRDWorkflowIO/KrClusterWriterSpec.h +++ b/Detectors/TRD/workflow/io/include/TRDWorkflowIO/KrClusterWriterSpec.h @@ -12,20 +12,170 @@ #ifndef O2_TRD_KRWRITERSPEC_H #define O2_TRD_KRWRITERSPEC_H +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataTakingContext.h" +#include "DataFormatsTRD/KrCluster.h" +#include "DataFormatsTRD/KrClusterTriggerRecord.h" +#include "CommonUtils/MemFileHelper.h" +#include "DetectorsCommonDataFormats/FileMetaData.h" + +#include +#include +#include +#include + namespace o2 { -namespace framework +namespace trd { -struct DataProcessorSpec; -} -} // namespace o2 -namespace o2 +class TRDKrClsWriterTask : public o2::framework::Task { -namespace trd + public: + TRDKrClsWriterTask() = default; + + void init(o2::framework::InitContext& ic) final + { + mOutputDir = o2::utils::Str::rectifyDirectory(ic.options().get("output-dir")); + + // should we write meta files for epn2eos? + mMetaFileDir = ic.options().get("meta-output-dir"); + if (mMetaFileDir != "/dev/null") { + mMetaFileDir = o2::utils::Str::rectifyDirectory(mMetaFileDir); + mStoreMetaFile = true; + } + + LOGP(info, "Storing output in {}, meta file writing enabled: {}", mOutputDir, mStoreMetaFile); + mAutoSave = ic.options().get("autosave-interval"); + + char hostname[_POSIX_HOST_NAME_MAX]; + gethostname(hostname, _POSIX_HOST_NAME_MAX); + mHostName = hostname; + mHostName = mHostName.substr(0, mHostName.find('.')); + } + + void createOutputFile(int runNumber, o2::framework::ProcessingContext& pc) + { + mFileName = fmt::format("o2_trdKrCls_run{}_{}.root", runNumber, mHostName); + auto fileNameTmp = o2::utils::Str::concat_string(mOutputDir, mFileName, ".part"); + mFileOut = std::make_unique(fileNameTmp.c_str(), "recreate"); + mTreeOut = std::make_unique("krData", "TRD krypton cluster data"); + mTreeOut->Branch("cluster", &krClusterPtr); + mTreeOut->Branch("trigRec", &krTrigRecPtr); + mDataTakingContext = pc.services().get(); + mOutputFileCreated = true; + } + + void writeToFile() + { + if (!mOutputFileCreated) { + return; + } + mFileOut->cd(); + mTreeOut->Write(); + } + + void closeOutputFile() + { + if (!mOutputFileCreated) { + return; + } + writeToFile(); + mTreeOut.reset(); + mFileOut->Close(); + mFileOut.reset(); + auto fileNameWithPath = mOutputDir + mFileName; + std::filesystem::rename(o2::utils::Str::concat_string(mOutputDir, mFileName, ".part"), fileNameWithPath); + if (mStoreMetaFile) { + o2::dataformats::FileMetaData fileMetaData; // object with information for meta data file + fileMetaData.fillFileData(fileNameWithPath); + fileMetaData.setDataTakingContext(mDataTakingContext); + fileMetaData.type = "calib"; + fileMetaData.priority = "high"; + auto metaFileNameTmp = fmt::format("{}{}.tmp", mMetaFileDir, mFileName); + auto metaFileName = fmt::format("{}{}.done", mMetaFileDir, mFileName); + try { + std::ofstream metaFileOut(metaFileNameTmp); + metaFileOut << fileMetaData; + metaFileOut.close(); + std::filesystem::rename(metaFileNameTmp, metaFileName); + } catch (std::exception const& e) { + LOG(error) << "Failed to store meta data file " << metaFileName << ", reason: " << e.what(); + } + } + } + + void run(o2::framework::ProcessingContext& pc) final + { + if (!mOutputFileCreated) { + auto tInfo = pc.services().get(); + createOutputFile(tInfo.runNumber, pc); + } + if (pc.transitionState() == TransitionHandlingState::Requested) { + LOG(info) << "Run stop requested, closing output file"; + mRunStopRequested = true; + closeOutputFile(); + } + if (mRunStopRequested) { + return; + } + auto cluster = pc.inputs().get>("krcluster"); + auto triggerRecords = pc.inputs().get>("krtrigrec"); + for (const auto& cls : cluster) { + krCluster.push_back(cls); + } + for (const auto& trig : triggerRecords) { + krTrigRec.push_back(trig); + } + mTreeOut->Fill(); + krCluster.clear(); + krTrigRec.clear(); + if (mAutoSave > 0 && ++mTFCounter % mAutoSave == 0) { + writeToFile(); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + if (mRunStopRequested) { + return; + } + LOG(info) << "End of stream received, closing output file"; + closeOutputFile(); + } + + private: + bool mRunStopRequested{false}; + bool mStoreMetaFile{false}; + bool mOutputFileCreated{false}; + int mAutoSave{0}; + uint64_t mTFCounter{0}; + std::string mOutputDir{"none"}; + std::string mMetaFileDir{"/dev/null"}; + std::string mHostName{}; + std::string mFileName{}; + std::unique_ptr mFileOut{}; + std::unique_ptr mTreeOut{}; + std::vector krCluster, *krClusterPtr{&krCluster}; + std::vector krTrigRec, *krTrigRecPtr{&krTrigRec}; + o2::framework::DataTakingContext mDataTakingContext; +}; + +framework::DataProcessorSpec getKrClusterWriterSpec() { + std::vector inputs; + inputs.emplace_back("krcluster", "TRD", "KRCLUSTER"); + inputs.emplace_back("krtrigrec", "TRD", "TRGKRCLS"); -o2::framework::DataProcessorSpec getKrClusterWriterSpec(); + return DataProcessorSpec{"kr-cluster-writer", + inputs, + Outputs{}, + AlgorithmSpec{adaptFromTask()}, + Options{ + {"output-dir", VariantType::String, "none", {"Output directory for data. Defaults to current working directory"}}, + {"meta-output-dir", VariantType::String, "/dev/null", {"metadata output directory, must exist (if not /dev/null)"}}, + {"autosave-interval", VariantType::Int, 0, {"Write output to file for every n-th TF. 0 means this feature is OFF"}}}}; +} } // end namespace trd } // end namespace o2 diff --git a/Detectors/TRD/workflow/io/src/KrClusterWriterSpec.cxx b/Detectors/TRD/workflow/io/src/KrClusterWriterSpec.cxx deleted file mode 100644 index 92fce34ada4a4..0000000000000 --- a/Detectors/TRD/workflow/io/src/KrClusterWriterSpec.cxx +++ /dev/null @@ -1,39 +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 "Framework/DataProcessorSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "TRDWorkflowIO/KrClusterWriterSpec.h" -#include "DataFormatsTRD/KrCluster.h" -#include "DataFormatsTRD/KrClusterTriggerRecord.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace trd -{ - -template -using BranchDefinition = framework::MakeRootTreeWriterSpec::BranchDefinition; - -o2::framework::DataProcessorSpec getKrClusterWriterSpec() -{ - - return framework::MakeRootTreeWriterSpec("TRDKrClWriter", - "trdkrclusters.root", - "krClusters", - BranchDefinition>{InputSpec{"clusters", "TRD", "KRCLUSTER"}, "KrCluster"}, - BranchDefinition>{InputSpec{"trigRecs", "TRD", "TRGKRCLS"}, "TriggerRecord"})(); -}; - -} // end namespace trd -} // end namespace o2 diff --git a/Detectors/TRD/workflow/src/KrClustererSpec.cxx b/Detectors/TRD/workflow/src/KrClustererSpec.cxx deleted file mode 100644 index 0cbacd08589b3..0000000000000 --- a/Detectors/TRD/workflow/src/KrClustererSpec.cxx +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file KrClustererSpec.cxx -/// \brief DPL device for running the TRD Krypton cluster finder -/// \author Ole Schmidt - -#include "TRDWorkflow/KrClustererSpec.h" -#include "TRDCalibration/KrClusterFinder.h" -#include "Framework/Task.h" -#include "Framework/ConfigParamRegistry.h" -#include "TStopwatch.h" -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace trd -{ - -class TRDKrClustererDevice : public Task -{ - public: - TRDKrClustererDevice() = default; - ~TRDKrClustererDevice() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - void endOfStream(framework::EndOfStreamContext& ec) final; - - private: - o2::trd::KrClusterFinder mKrClFinder; -}; - -void TRDKrClustererDevice::init(InitContext& ic) -{ - mKrClFinder.init(); -} - -void TRDKrClustererDevice::run(ProcessingContext& pc) -{ - TStopwatch timer; - - const auto digits = pc.inputs().get>("digits"); - const auto triggerRecords = pc.inputs().get>("triggerRecords"); - - mKrClFinder.reset(); - mKrClFinder.setInput(digits, triggerRecords); - timer.Start(); - mKrClFinder.findClusters(); - timer.Stop(); - - LOGF(info, "TRD Krypton cluster finder total timing: Cpu: %.3e Real: %.3e s", timer.CpuTime(), timer.RealTime()); - LOGF(info, "Found %lu Kr clusters in %lu input trigger records.", mKrClFinder.getKrClusters().size(), triggerRecords.size()); - - pc.outputs().snapshot(Output{o2::header::gDataOriginTRD, "KRCLUSTER", 0, Lifetime::Timeframe}, mKrClFinder.getKrClusters()); - pc.outputs().snapshot(Output{o2::header::gDataOriginTRD, "TRGKRCLS", 0, Lifetime::Timeframe}, mKrClFinder.getKrTrigRecs()); -} - -void TRDKrClustererDevice::endOfStream(EndOfStreamContext& ec) -{ - LOG(info) << "Done with the cluster finding (EoS received)"; -} - -DataProcessorSpec getKrClustererSpec() -{ - std::vector inputs; - inputs.emplace_back("digits", ConcreteDataTypeMatcher{o2::header::gDataOriginTRD, "DIGITS"}, Lifetime::Timeframe); - inputs.emplace_back("triggerRecords", ConcreteDataTypeMatcher{o2::header::gDataOriginTRD, "TRKTRGRD"}, Lifetime::Timeframe); - std::vector outputs; - outputs.emplace_back(o2::header::gDataOriginTRD, "KRCLUSTER", 0, Lifetime::Timeframe); - outputs.emplace_back(o2::header::gDataOriginTRD, "TRGKRCLS", 0, Lifetime::Timeframe); - - return DataProcessorSpec{ - "trd-kr-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask()}, - Options{}}; -} - -} // namespace trd -} // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 87c7037475236..afa2108a224a6 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -35,6 +35,7 @@ #include "DataFormatsFT0/RecPoints.h" #include "ITSReconstruction/RecoGeomHelper.h" #include "ITSMFTReconstruction/ClustererParam.h" +#include "FT0Reconstruction/InteractionTag.h" #include "DataFormatsGlobalTracking/TrackTuneParams.h" // GPU header @@ -402,7 +403,7 @@ void TRDGlobalTracking::run(ProcessingContext& pc) uint8_t fwd = 0, bwd = 0; for (size_t ft0id = curFT0; ft0id < ft0recPoints.size(); ft0id++) { const auto& f0rec = ft0recPoints[ft0id]; - if (f0rec.getTrigger().getVertex()) { + if (o2::ft0::InteractionTag::Instance().isSelected(f0rec)) { auto bcdiff = trig.getBCData().toLong() - f0rec.getInteractionRecord().toLong(); if (bcdiff > maxDiffBwd) { curFT0 = ft0id + 1; diff --git a/Detectors/Upgrades/ALICE3/Passive/CMakeLists.txt b/Detectors/Upgrades/ALICE3/Passive/CMakeLists.txt index 7333d7584a7cc..6d2e9433418aa 100644 --- a/Detectors/Upgrades/ALICE3/Passive/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/Passive/CMakeLists.txt @@ -12,9 +12,11 @@ o2_add_library(Alice3DetectorsPassive SOURCES src/Pipe.cxx src/PassiveBase.cxx + src/Absorber.cxx PUBLIC_LINK_LIBRARIES O2::Field O2::DetectorsBase O2::SimConfig) o2_target_root_dictionary(Alice3DetectorsPassive HEADERS include/Alice3DetectorsPassive/Pipe.h include/Alice3DetectorsPassive/PassiveBase.h - LINKDEF src/PassiveLinkDef.h) \ No newline at end of file + include/Alice3DetectorsPassive/Absorber.h + LINKDEF src/PassiveLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/Absorber.h b/Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/Absorber.h new file mode 100644 index 0000000000000..db8b7627ee79e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/Passive/include/Alice3DetectorsPassive/Absorber.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 ALICE3_PASSIVE_ABSORBER_H +#define ALICE3_PASSIVE_ABSORBER_H + +#include "Alice3DetectorsPassive/PassiveBase.h" + +namespace o2 +{ +namespace passive +{ +class Alice3Absorber : public Alice3PassiveBase +{ + public: + Alice3Absorber(const char* name, const char* Title = "ALICE3 Absorber"); + Alice3Absorber(); + ~Alice3Absorber() override; + void ConstructGeometry() override; + void createMaterials(); + + /// Clone this object (used in MT mode only) + FairModule* CloneModule() const override; + + private: + Alice3Absorber(const Alice3Absorber& orig); + Alice3Absorber& operator=(const Alice3Absorber&); + + ClassDefOverride(o2::passive::Alice3Absorber, 1); +}; +} // namespace passive +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx b/Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx new file mode 100644 index 0000000000000..7cb0148c12dc9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/Passive/src/Absorber.cxx @@ -0,0 +1,164 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include // for TGeoTrap +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef NDEBUG +#undef NDEBUG +#endif +#include + +using namespace o2::passive; + +Alice3Absorber::~Alice3Absorber() = default; + +Alice3Absorber::Alice3Absorber() : Alice3PassiveBase("A3ABSO", "") {} +Alice3Absorber::Alice3Absorber(const char* name, const char* Title) : Alice3PassiveBase(name, Title) {} +Alice3Absorber::Alice3Absorber(const Alice3Absorber& rhs) = default; + +Alice3Absorber& Alice3Absorber::operator=(const Alice3Absorber& rhs) +{ + // self assignment + if (this == &rhs) { + return *this; + } + + // base class assignment + FairModule::operator=(rhs); + + return *this; +} + +void Alice3Absorber::createMaterials() +{ + + auto& matmgr = o2::base::MaterialManager::Instance(); + // Define materials for muon absorber + // + Int_t isxfld = 2.; + Float_t sxmgmx = 10.; + o2::base::Detector::initFieldTrackingParams(isxfld, sxmgmx); + + // + // Steel + // + Float_t asteel[4] = {55.847, 51.9961, 58.6934, 28.0855}; + Float_t zsteel[4] = {26., 24., 28., 14.}; + Float_t wsteel[4] = {.715, .18, .1, .005}; + // + // Air + // + float aAir[4] = {12.0107, 14.0067, 15.9994, 39.948}; + float zAir[4] = {6., 7., 8., 18.}; + float wAir[4] = {0.000124, 0.755267, 0.231781, 0.012827}; + float dAir = 1.20479E-3; + float dAir1 = 1.20479E-11; + + // **************** + // Defines tracking media parameters. + // + Float_t epsil, stmin, tmaxfd, deemax, stemax; + epsil = .001; // Tracking precision, + stemax = -0.01; // Maximum displacement for multiple scat + tmaxfd = -20.; // Maximum angle due to field deflection + deemax = -.3; // Maximum fractional energy loss, DLS + stmin = -.8; + // *************** + // + + matmgr.Mixture("ALICE3ABSO", 16, "VACUUM0$", aAir, zAir, dAir1, 4, wAir); + matmgr.Medium("ALICE3ABSO", 16, "VA_C0", 16, 0, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); + + // + // Steel + matmgr.Mixture("ALICE3ABSO", 19, "STAINLESS STEEL0$", asteel, zsteel, 7.88, 4, wsteel); + matmgr.Mixture("ALICE3ABSO", 39, "STAINLESS STEEL1$", asteel, zsteel, 7.88, 4, wsteel); + matmgr.Mixture("ALICE3ABSO", 59, "STAINLESS STEEL2$", asteel, zsteel, 7.88, 4, wsteel); + matmgr.Medium("ALICE3ABSO", 19, "ST_C0", 19, 0, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); + matmgr.Medium("ALICE3ABSO", 39, "ST_C1", 39, 0, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); + matmgr.Medium("ALICE3ABSO", 59, "ST_C3", 59, 0, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); +} + +void Alice3Absorber::ConstructGeometry() +{ + createMaterials(); + + // + // Build muon shield geometry + // + // + + auto& matmgr = o2::base::MaterialManager::Instance(); + + // + // Media + // + + auto kMedVac = matmgr.getTGeoMedium("ALICE3ABSO_VA_C0"); + auto kMedSteel = matmgr.getTGeoMedium("ALICE3ABSO_ST_C0"); + auto kMedSteelSh = matmgr.getTGeoMedium("ALICE3ABSO_ST_C3"); + + // The top volume + TGeoVolume* top = gGeoManager->GetVolume("cave"); + TGeoVolume* barrel = gGeoManager->GetVolume("barrel"); + if (!barrel) { + LOG(fatal) << "Could not find the top volume"; + } + + TGeoPcon* absorings = new TGeoPcon(0., 360., 18); + + absorings->DefineSection(0, 500, 236, 274); + absorings->DefineSection(1, 400, 236, 274); + absorings->DefineSection(2, 400, 232.5, 277.5); + absorings->DefineSection(3, 300, 232.5, 277.5); + absorings->DefineSection(4, 300, 227.5, 282.5); + absorings->DefineSection(5, 200, 227.5, 282.5); + absorings->DefineSection(6, 200, 222.5, 287.5); + absorings->DefineSection(7, 100, 222.5, 287.5); + absorings->DefineSection(8, 100, 220, 290); + absorings->DefineSection(9, -100, 220, 290); + absorings->DefineSection(10, -100, 222.5, 287.5); + absorings->DefineSection(11, -200, 222.5, 287.5); + absorings->DefineSection(12, -200, 227.5, 282.5); + absorings->DefineSection(13, -300, 227.5, 282.5); + absorings->DefineSection(14, -300, 232.5, 277.5); + absorings->DefineSection(15, -400, 232.5, 277.5); + absorings->DefineSection(16, -400, 236, 274); + absorings->DefineSection(17, -500, 236, 274); + + // Insert + absorings->SetName("absorings"); + + TGeoVolume* abso = new TGeoVolume("Absorber", absorings, kMedSteel); + + abso->SetVisibility(1); + abso->SetTransparency(50); + abso->SetLineColor(kGray); + + // + // Adding volumes to mother volume + // + + barrel->AddNode(abso, 1, new TGeoTranslation(0, 30.f, 0)); +} + +FairModule* Alice3Absorber::CloneModule() const { return new Alice3Absorber(*this); } +ClassImp(o2::passive::Alice3Absorber); diff --git a/Detectors/Upgrades/ALICE3/Passive/src/PassiveLinkDef.h b/Detectors/Upgrades/ALICE3/Passive/src/PassiveLinkDef.h index bab082778a4c2..7974620fec1c6 100644 --- a/Detectors/Upgrades/ALICE3/Passive/src/PassiveLinkDef.h +++ b/Detectors/Upgrades/ALICE3/Passive/src/PassiveLinkDef.h @@ -17,5 +17,6 @@ #pragma link C++ class o2::passive::Alice3PassiveBase + ; #pragma link C++ class o2::passive::Alice3Pipe + ; +#pragma link C++ class o2::passive::Alice3Absorber + ; -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationSuperAlpide.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationSuperAlpide.h index 810fa19e882fa..fb888feb359ea 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationSuperAlpide.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationSuperAlpide.h @@ -40,20 +40,20 @@ class SegmentationSuperAlpide } SegmentationSuperAlpide(int layer = 0) : SegmentationSuperAlpide(layer, SuperAlpideParams::Instance().mPitchCol, SuperAlpideParams::Instance().mPitchRow, SuperAlpideParams::Instance().mDetectorThickness, DescriptorInnerBarrelITS3Param::Instance().mLength, DescriptorInnerBarrelITS3Param::Instance().mRadii) {} - double mRadii[4] = {1.8f, 2.4f, 3.0f, 6.0f}; ///< radii for different layers - const double mLength; ///< chip length - const int mLayer; ///< chip layer - const float mPitchCol; ///< pixel column size - const float mPitchRow; ///< pixel row size - const float mDetectorLayerThickness; ///< detector thickness - const int mNCols{static_cast(std::ceil(mLength / mPitchCol))}; ///< number of columns - const int mNRows{static_cast(std::ceil(double(mRadii[mLayer] + mDetectorLayerThickness - mSensorLayerThickness / 2.) * double(constants::math::PI) / double(mPitchRow) * (mLayer == 3 ? 0.5 : 1.)))}; ///< number of rows - const int mNPixels{mNRows * mNCols}; ///< total number of pixels - static constexpr float mPassiveEdgeReadOut = 0.; ///< width of the readout edge (Passive bottom) - static constexpr float mPassiveEdgeTop = 0.; ///< Passive area on top - static constexpr float mPassiveEdgeSide = 0.; ///< width of Passive area on left/right of the sensor - const float mActiveMatrixSizeCols{mPitchCol * mNCols}; ///< Active size along columns - const float mActiveMatrixSizeRows{mPitchRow * mNRows}; ///< Active size along rows + double mRadii[4] = {1.8f, 2.4f, 3.0f, 6.0f}; ///< radii for different layers + const double mLength; ///< chip length + const int mLayer; ///< chip layer + const float mPitchCol; ///< pixel column size at the external surface + const float mPitchRow; ///< pixel row size at the external surface + const float mDetectorLayerThickness; ///< detector thickness + const int mNCols{static_cast(std::floor(mLength / mPitchCol / 2) * 2)}; ///< number of columns (we force to have an even number) + const int mNRows{static_cast(std::floor(double(mRadii[mLayer] + mDetectorLayerThickness - mSensorLayerThicknessEff / 2.) * double(constants::math::PI) / double(mPitchRow) / 2 * (mLayer == 3 ? 0.5 : 1.)) * 2)}; ///< number of rows (we force to have an even number) + const int mNPixels{mNRows * mNCols}; ///< total number of pixels + static constexpr float mPassiveEdgeReadOut = 0.; ///< width of the readout edge (Passive bottom) + static constexpr float mPassiveEdgeTop = 0.; ///< Passive area on top + static constexpr float mPassiveEdgeSide = 0.; ///< width of Passive area on left/right of the sensor + const float mActiveMatrixSizeCols{mPitchCol * mNCols}; ///< Active size along columns + const float mActiveMatrixSizeRows{mPitchRow * mNRows}; ///< Active size along rows // effective thickness of sensitive layer, accounting for charge collection non-uniformity, https://alice.its.cern.ch/jira/browse/AOC-46 static constexpr float mSensorLayerThicknessEff = 28.e-4; ///< effective thickness of sensitive part @@ -140,7 +140,7 @@ inline void SegmentationSuperAlpide::curvedToFlat(float xCurved, float yCurved, float dist = std::sqrt(xCurved * xCurved + yCurved * yCurved); yFlat = dist - (mRadii[mLayer] + mDetectorLayerThickness - mSensorLayerThickness / 2.); float phi = (double)constants::math::PI / 2 - std::atan2((double)yCurved, (double)xCurved); - xFlat = dist * phi; + xFlat = (mRadii[mLayer] + mDetectorLayerThickness - mSensorLayerThickness / 2.) * phi; // we bring everything to the upper surface to avoid effects due to the different length of upper and lower surfaces } inline void SegmentationSuperAlpide::flatToCurved(float xFlat, float yFlat, float& xCurved, float& yCurved) @@ -157,8 +157,8 @@ inline void SegmentationSuperAlpide::localToDetectorUnchecked(float xRow, float // convert to row/col w/o over/underflow check xRow = 0.5 * (mActiveMatrixSizeRows - mPassiveEdgeTop + mPassiveEdgeReadOut) - xRow; // coordinate wrt top edge of Active matrix zCol += 0.5 * mActiveMatrixSizeCols; // coordinate wrt left edge of Active matrix - iRow = int(xRow / mPitchRow); - iCol = int(zCol / mPitchCol); + iRow = std::floor(xRow / mPitchRow); + iCol = std::floor(zCol / mPitchCol); if (xRow < 0) { iRow -= 1; } @@ -176,8 +176,8 @@ inline bool SegmentationSuperAlpide::localToDetector(float xRow, float zCol, int iRow = iCol = -1; return false; } - iRow = int(xRow / mPitchRow); - iCol = int(zCol / mPitchCol); + iRow = std::floor(xRow / mPitchRow); + iCol = std::floor(zCol / mPitchCol); return true; } diff --git a/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt b/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt index ea24aa43b1561..6f6ac2945fd0d 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/macros/test/CMakeLists.txt @@ -82,3 +82,51 @@ o2_add_test_root_macro(buildMatBudLUT.C O2::SimulationDataFormat O2::DetectorsBase LABELS its3) + +o2_add_test_root_macro(CheckHits.C + PUBLIC_LINK_LIBRARIES O2::ITSBase + O2::ITS3Base + O2::ITSMFTBase + O2::ITSMFTSimulation + O2::ITS3Simulation + O2::ITS3Reconstruction + O2::MathUtils + O2::SimulationDataFormat + O2::DetectorsBase + LABELS its3) + +o2_add_test_root_macro(CheckDigitsDensity.C + PUBLIC_LINK_LIBRARIES O2::ITSBase + O2::ITS3Base + O2::ITSMFTBase + O2::ITSMFTSimulation + O2::ITS3Simulation + O2::ITS3Reconstruction + O2::MathUtils + O2::SimulationDataFormat + O2::DetectorsBase + LABELS its3) + +o2_add_test_root_macro(CheckClusterSize.C + PUBLIC_LINK_LIBRARIES O2::ITSBase + O2::ITS3Base + O2::ITSMFTBase + O2::ITSMFTSimulation + O2::ITS3Simulation + O2::ITS3Reconstruction + O2::MathUtils + O2::SimulationDataFormat + O2::DetectorsBase + LABELS its3) + +o2_add_test_root_macro(CompareClusterSize.C + PUBLIC_LINK_LIBRARIES O2::ITSBase + O2::ITS3Base + O2::ITSMFTBase + O2::ITSMFTSimulation + O2::ITS3Simulation + O2::ITS3Reconstruction + O2::MathUtils + O2::SimulationDataFormat + O2::DetectorsBase + LABELS its3) diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckClusterSize.C b/Detectors/Upgrades/ITS3/macros/test/CheckClusterSize.C new file mode 100755 index 0000000000000..cd69cb9c2cf3c --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/test/CheckClusterSize.C @@ -0,0 +1,420 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckClusterSize.C +/// \brief analyze ITS3 cluster sizes +/// \dependencies CreateDictionariesITS3.C +/// \author felix.schlepper@cern.ch + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define ENABLE_UPGRADES +#include "DataFormatsITS3/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "ITS3Reconstruction/TopologyDictionary.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#endif + +static constexpr int nLayers = 4; // 3 Layers + 1 combined outer layer + +struct ParticleInfo { + int event{}; + int pdg{}; + double pt{}; + double eta{}; + double phi{}; + bool isPrimary{false}; +}; + +using o2::its3::CompClusterExt; +using ROFRec = o2::itsmft::ROFRecord; + +void checkFile(const std::unique_ptr& file); + +inline auto hist_map(unsigned short id) +{ + return std::clamp(id, static_cast(0), static_cast(6)) / 2; +} + +void CheckClusterSize(std::string clusFileName = "o2clus_it3.root", + std::string kineFileName = "o2sim_Kine.root", + std::string dictFileName = "", bool batch = true) +{ + gROOT->SetBatch(batch); + TStopwatch sw; + sw.Start(); + // TopologyDictionary + if (dictFileName.empty()) { + dictFileName = + o2::base::DetectorNameConf::getAlpideClusterDictionaryFileName( + o2::detectors::DetID::IT3, "", "root"); + } + o2::its3::TopologyDictionary dict; + std::ifstream file(dictFileName.c_str()); + if (file.good()) { + LOG(info) << "Running with dictionary: " << dictFileName.c_str(); + dict.readFromFile(dictFileName); + } else { + LOG(info) << "Running without dictionary !"; + } + + // Histograms + constexpr int maxClusterSize = 50; + TH1F hOuterBarrel("outerbarrel", "ClusterSize in OuterBarrel", maxClusterSize, 0, maxClusterSize); + std::vector hPrimary; + std::vector hPrimaryEta; + std::vector hPrimaryPt; + std::vector hPrimaryPhi; + std::vector hSecondary; + std::vector hSecondaryEta; + std::vector hSecondaryPt; + std::vector hSecondaryPhi; + std::vector hProtonPrimary; + std::vector hProtonPrimaryEta; + std::vector hProtonPrimaryPt; + std::vector hProtonPrimaryPhi; + std::vector hProtonSecondary; + std::vector hProtonSecondaryEta; + std::vector hProtonSecondaryPt; + std::vector hProtonSecondaryPhi; + std::vector hPionPrimary; + std::vector hPionPrimaryEta; + std::vector hPionPrimaryPt; + std::vector hPionPrimaryPhi; + std::vector hPionSecondary; + std::vector hPionSecondaryEta; + std::vector hPionSecondaryPt; + std::vector hPionSecondaryPhi; + std::vector hKaonPrimary; + std::vector hKaonPrimaryEta; + std::vector hKaonPrimaryPt; + std::vector hKaonPrimaryPhi; + std::vector hKaonSecondary; + std::vector hKaonSecondaryEta; + std::vector hKaonSecondaryPt; + std::vector hKaonSecondaryPhi; + std::vector hOtherPrimary; + std::vector hOtherPrimaryEta; + std::vector hOtherPrimaryPt; + std::vector hOtherPrimaryPhi; + std::vector hOtherSecondary; + std::vector hOtherSecondaryEta; + std::vector hOtherSecondaryPt; + std::vector hOtherSecondaryPhi; + for (int i = 0; i < 4; ++i) { + hPrimary.emplace_back(Form("primary/L%d", i), Form("L%d Primary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hPrimaryEta.emplace_back(Form("primary/EtaL%d", i), Form("L%d Primary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hPrimaryPt.emplace_back(Form("primary/Pt%d", i), Form("L%d Primary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hPrimaryPhi.emplace_back(Form("primary/Phi%d", i), Form("L%d Primary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + hSecondary.emplace_back(Form("seconday/L%d", i), Form("L%d Secondary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hSecondaryEta.emplace_back(Form("seconday/EtaL%d", i), Form("L%d Secondary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hSecondaryPt.emplace_back(Form("seconday/Pt%d", i), Form("L%d Secondary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hSecondaryPhi.emplace_back(Form("seconday/Phi%d", i), Form("L%d Secondary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + + hProtonPrimary.emplace_back(Form("proton/primary/L%d", i), Form("Proton - L%d Primary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hProtonPrimaryEta.emplace_back(Form("proton/primary/EtaL%d", i), Form("Proton - L%d Primary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hProtonPrimaryPt.emplace_back(Form("proton/primary/Pt%d", i), Form("Proton - L%d Primary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hProtonPrimaryPhi.emplace_back(Form("proton/primary/Phi%d", i), Form("Proton - L%d Primary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + hProtonSecondary.emplace_back(Form("proton/seconday/L%d", i), Form("Proton - L%d Secondary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hProtonSecondaryEta.emplace_back(Form("proton/seconday/EtaL%d", i), Form("Proton - L%d Secondary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hProtonSecondaryPt.emplace_back(Form("proton/seconday/Pt%d", i), Form("Proton - L%d Secondary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hProtonSecondaryPhi.emplace_back(Form("proton/seconday/Phi%d", i), Form("Proton - L%d Secondary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + + hPionPrimary.emplace_back(Form("pion/primary/L%d", i), Form("Pion- L%d Primary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hPionPrimaryEta.emplace_back(Form("pion/primary/EtaL%d", i), Form("Pion- L%d Primary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hPionPrimaryPt.emplace_back(Form("pion/primary/Pt%d", i), Form("Pion- L%d Primary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hPionPrimaryPhi.emplace_back(Form("pion/primary/Phi%d", i), Form("Pion- L%d Primary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + hPionSecondary.emplace_back(Form("pion/seconday/L%d", i), Form("Pion- L%d Secondary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hPionSecondaryEta.emplace_back(Form("pion/seconday/EtaL%d", i), Form("Pion- L%d Secondary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hPionSecondaryPt.emplace_back(Form("pion/seconday/Pt%d", i), Form("Pion- L%d Secondary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hPionSecondaryPhi.emplace_back(Form("pion/seconday/Phi%d", i), Form("Pion- L%d Secondary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + + hKaonPrimary.emplace_back(Form("kaon/primary/L%d", i), Form("Kaon- L%d Primary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hKaonPrimaryEta.emplace_back(Form("kaon/primary/EtaL%d", i), Form("Kaon- L%d Primary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hKaonPrimaryPt.emplace_back(Form("kaon/primary/Pt%d", i), Form("Kaon- L%d Primary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hKaonPrimaryPhi.emplace_back(Form("kaon/primary/Phi%d", i), Form("Kaon- L%d Primary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + hKaonSecondary.emplace_back(Form("kaon/seconday/L%d", i), Form("Kaon- L%d Secondary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hKaonSecondaryEta.emplace_back(Form("kaon/seconday/EtaL%d", i), Form("Kaon- L%d Secondary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hKaonSecondaryPt.emplace_back(Form("kaon/seconday/Pt%d", i), Form("Kaon- L%d Secondary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hKaonSecondaryPhi.emplace_back(Form("kaon/seconday/Phi%d", i), Form("Kaon- L%d Secondary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + + hOtherPrimary.emplace_back(Form("other/primary/L%d", i), Form("Other - L%d Primary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hOtherPrimaryEta.emplace_back(Form("other/primary/EtaL%d", i), Form("Other - L%d Primary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hOtherPrimaryPt.emplace_back(Form("other/primary/Pt%d", i), Form("Other - L%d Primary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hOtherPrimaryPhi.emplace_back(Form("other/primary/Phi%d", i), Form("Other - L%d Primary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + hOtherSecondary.emplace_back(Form("other/seconday/L%d", i), Form("Other - L%d Secondary Cluster Size", i), maxClusterSize, 0, maxClusterSize); + hOtherSecondaryEta.emplace_back(Form("other/seconday/EtaL%d", i), Form("Other - L%d Secondary Cluster Size vs Eta", i), maxClusterSize, 0, maxClusterSize, 100, -3.0, 3.0); + hOtherSecondaryPt.emplace_back(Form("other/seconday/Pt%d", i), Form("Other - L%d Secondary Cluster Size vs Pt", i), maxClusterSize, 0, maxClusterSize, 100, 0.0, 10.0); + hOtherSecondaryPhi.emplace_back(Form("other/seconday/Phi%d", i), Form("Other - L%d Secondary Cluster Size vs Phi", i), maxClusterSize, 0, maxClusterSize, 100, 0., 2 * o2::constants::math::PI); + } + + // Clusters + std::unique_ptr clusFile(TFile::Open(clusFileName.data())); + checkFile(clusFile); + auto clusTree = clusFile->Get("o2sim"); + std::vector clusArr; + std::vector* clusArrP{&clusArr}; + clusTree->SetBranchAddress("IT3ClusterComp", &clusArrP); + std::vector patterns; + std::vector* patternsPtr{&patterns}; + clusTree->SetBranchAddress("IT3ClusterPatt", &patternsPtr); + + // MC tracks + std::unique_ptr kineFile(TFile::Open(kineFileName.data())); + checkFile(kineFile); + auto mcTree = kineFile->Get("o2sim"); + mcTree->SetBranchStatus("*", false); // disable all branches + mcTree->SetBranchStatus("MCTrack*", true); + mcTree->SetBranchStatus("MCEventHeader*", true); + + std::vector mcArr; + std::vector* mcArrP{&mcArr}; + mcTree->SetBranchAddress("MCTrack", &mcArrP); + o2::dataformats::MCEventHeader* mcEvent = nullptr; + mcTree->SetBranchAddress("MCEventHeader.", &mcEvent); + + // Cluster MC labels + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + clusTree->SetBranchAddress("IT3ClusterMCTruth", &clusLabArr); + + std::cout << "** Filling particle table ... " << std::flush; + int lastEventIDcl = -1; + auto nev = mcTree->GetEntriesFast(); + std::vector> info(nev); + for (int iEntry = 0; mcTree->LoadTree(iEntry) >= 0; ++iEntry) { // loop over MC events + mcTree->GetEvent(iEntry); + info[iEntry].resize(mcArr.size()); + for (unsigned int mcI{0}; mcI < mcArr.size(); ++mcI) { + const auto part = mcArr[mcI]; + info[iEntry][mcI].event = iEntry; + info[iEntry][mcI].pdg = std::abs(part.GetPdgCode()); + info[iEntry][mcI].pt = part.GetPt(); + info[iEntry][mcI].phi = part.GetPhi(); + info[iEntry][mcI].eta = part.GetEta(); + if (std::sqrt(part.GetStartVertexCoordinatesX() * part.GetStartVertexCoordinatesX() + part.GetStartVertexCoordinatesY() * part.GetStartVertexCoordinatesY()) < 0.1) { + info[iEntry][mcI].isPrimary = true; + } + } + } + std::cout << " done." << std::endl; + + // ROFrecords + std::vector rofRecVec; + std::vector* rofRecVecP{&rofRecVec}; + clusTree->SetBranchAddress("IT3ClustersROF", &rofRecVecP); + clusTree->GetEntry(0); + int nROFRec = (int)rofRecVec.size(); + auto pattIt = patternsPtr->cbegin(); + + for (int irof = 0; irof < nROFRec; irof++) { + const auto& rofRec = rofRecVec[irof]; + // rofRec.print(); + + for (int icl = 0; icl < rofRec.getNEntries(); icl++) { + int clEntry = rofRec.getFirstEntry() + icl; + const auto& cluster = clusArr[clEntry]; + // cluster.print(); + + auto pattId = cluster.getPatternID(); + auto id = cluster.getSensorID(); + int clusterSize{-1}; + if (pattId == o2::its3::CompCluster::InvalidPatternID || + dict.isGroup(pattId)) { + o2::itsmft::ClusterPattern patt(pattIt); + clusterSize = patt.getNPixels(); + } else { + clusterSize = dict.getNpixels(pattId); + } + + const auto& label = (clusLabArr->getLabels(clEntry))[0]; + if (!label.isValid() || label.getSourceID() != 0 || !label.isCorrect()) { + continue; + } + + const int trackID = label.getTrackID(); + int evID = label.getEventID(); + const auto& pInfo = info[evID][trackID]; + if (id > 6) { + hOuterBarrel.Fill(clusterSize); + } + + if (pInfo.isPrimary) { + hPrimary[hist_map(id)].Fill(clusterSize); + hPrimaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hPrimaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hPrimaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } else { + hSecondary[hist_map(id)].Fill(clusterSize); + hSecondaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hSecondaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hSecondaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } + if (pInfo.pdg == kProton) { + if (pInfo.isPrimary) { + hProtonPrimary[hist_map(id)].Fill(clusterSize); + hProtonPrimaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hProtonPrimaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hProtonPrimaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } else { + hProtonSecondary[hist_map(id)].Fill(clusterSize); + hProtonSecondaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hProtonSecondaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hProtonSecondaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } + } else if (pInfo.pdg == kPiPlus) { + if (pInfo.isPrimary) { + hProtonPrimary[hist_map(id)].Fill(clusterSize); + hProtonPrimaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hProtonPrimaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hProtonPrimaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } else { + hPionSecondary[hist_map(id)].Fill(clusterSize); + hPionSecondaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hPionSecondaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hPionSecondaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } + } else if (pInfo.pdg == kKPlus) { + if (pInfo.isPrimary) { + hKaonPrimary[hist_map(id)].Fill(clusterSize); + hKaonPrimaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hKaonPrimaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hKaonPrimaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } else { + hKaonSecondary[hist_map(id)].Fill(clusterSize); + hKaonSecondaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hKaonSecondaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hKaonSecondaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } + } else { + if (pInfo.isPrimary) { + hOtherPrimary[hist_map(id)].Fill(clusterSize); + hOtherPrimaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hOtherPrimaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hOtherPrimaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } else { + hOtherSecondary[hist_map(id)].Fill(clusterSize); + hOtherSecondaryEta[hist_map(id)].Fill(clusterSize, pInfo.eta); + hOtherSecondaryPt[hist_map(id)].Fill(clusterSize, pInfo.pt); + hOtherSecondaryPhi[hist_map(id)].Fill(clusterSize, pInfo.phi); + } + } + } + } + std::cout << "Done measuring cluster sizes:" << std::endl; + for (int i = 0; i < nLayers; ++i) { + std::cout << "* Layer " << i << ":\n"; + std::cout << "** Primary " << hPrimary[i].GetMean() << " +/- " << hPrimary[i].GetRMS() << "\n"; + std::cout << "** Secondary " << hSecondary[i].GetMean() << " +/- " << hSecondary[i].GetRMS() << std::endl; + } + std::unique_ptr oFile( + TFile::Open("checkClusterSize.root", "RECREATE")); + checkFile(oFile); + + char const* name[nLayers] = {"L0", "L1", "L2", "OuterBarrel"}; + const double delta = 0.2; + double x[nLayers], xP[nLayers], xS[nLayers], yP[nLayers], yS[nLayers], vyP[nLayers], vyS[nLayers]; + for (int i = 0; i < nLayers; ++i) { + x[i] = i; + xP[i] = i - delta; + xS[i] = i + delta; + yP[i] = hPrimary[i].GetMean(); + vyP[i] = hPrimary[i].GetRMS(); + yS[i] = hSecondary[i].GetMean(); + vyS[i] = hSecondary[i].GetRMS(); + } + + auto c1 = new TCanvas("c1", "A Simple Graph Example", 200, 10, 700, 500); + auto h = new TH1F("h", "", nLayers, x[0] - 0.5, x[nLayers - 1] + 0.5); + h->SetTitle("Cluster Sizes"); + h->GetYaxis()->SetTitleOffset(1.); + h->GetXaxis()->SetTitleOffset(1.); + h->GetYaxis()->SetTitle("cluster size"); + h->GetXaxis()->SetTitle("Layer"); + h->GetXaxis()->SetNdivisions(-10); + for (int i = 1; i <= nLayers; i++) + h->GetXaxis()->SetBinLabel(i, name[i - 1]); + h->SetMaximum(20); + h->SetMinimum(0); + h->SetStats(false); + h->Draw(); + auto grP = new TGraphErrors(nLayers, xP, yP, nullptr, vyP); + grP->SetMarkerStyle(4); + grP->SetMarkerSize(2); + grP->SetTitle("Primary"); + auto grS = new TGraphErrors(nLayers, xS, yS, nullptr, vyS); + grS->SetMarkerStyle(3); + grS->SetMarkerSize(2); + grS->SetTitle("Secondary"); + auto mg = new TMultiGraph("mg", ""); + mg->Add(grP); + mg->Add(grS); + mg->Draw("P pmc plc"); + auto leg = new TLegend(0.75, 0.75, 0.9, 0.9); + leg->AddEntry(grP); + leg->AddEntry(grS); + leg->Draw(); + c1->Write(); + c1->SaveAs("its3ClusterSize.pdf"); + for (const auto& hh : {hPrimary, hSecondary, hPionPrimary, hPionSecondary, hProtonPrimary, hProtonSecondary, hKaonPrimary, hKaonSecondary, hOtherPrimary, hOtherSecondary}) { + for (const auto& h : hh) { + h.Write(); + } + } + for (const auto& hh : {hPrimaryEta, hSecondaryEta, hPionPrimaryEta, hPionSecondaryEta, hProtonPrimaryEta, hProtonSecondaryEta, hKaonPrimaryEta, hKaonSecondaryEta, hOtherPrimaryEta, hOtherSecondaryEta}) { + for (const auto& h : hh) { + h.Write(); + } + } + for (const auto& hh : {hPrimaryPt, hSecondaryPt, hPionPrimaryPt, hPionSecondaryPt, hProtonPrimaryPt, hProtonSecondaryPt, hKaonPrimaryPt, hKaonSecondaryPt, hOtherPrimaryPt, hOtherSecondaryPt}) { + for (const auto& h : hh) { + h.Write(); + } + } + for (const auto& hh : {hPrimaryPhi, hSecondaryPhi, hPionPrimaryPhi, hPionSecondaryPhi, hProtonPrimaryPhi, hProtonSecondaryPhi, hKaonPrimaryPhi, hKaonSecondaryPhi, hOtherPrimaryPhi, hOtherSecondaryPhi}) { + for (const auto& h : hh) { + h.Write(); + } + } + hOuterBarrel.Write(); + sw.Stop(); + sw.Print(); +} + +void checkFile(const std::unique_ptr& file) +{ + if (!file || file->IsZombie()) { + printf("Could not open %s!\n", file->GetName()); + std::exit(1); + } +} diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckDigitsDensity.C b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsDensity.C new file mode 100755 index 0000000000000..1542b9a847d9e --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/test/CheckDigitsDensity.C @@ -0,0 +1,356 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckDigitsDensity.C +/// \brief analyze ITS3 digit density +/// \author felix.schlepper@cern.ch + +#include +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ENABLE_UPGRADES +#include "CommonConstants/MathConstants.h" +#include "DataFormatsITSMFT/Digit.h" +#include "ITS3Base/SegmentationSuperAlpide.h" +#endif + +/* + * How to simulate: + * $o2-sim-digitizer-workflow -b + */ + +constexpr double interaction_rate = 50e3; // Hz +constexpr double integration_time = 10e-6; // s +constexpr double qedXSection = 34346.9; // barn +constexpr double hadXSection = 8.f; // barn +constexpr double qedRate = qedXSection / hadXSection * interaction_rate; // Hz +constexpr double qedFactor = qedRate * integration_time; // a.u. +using o2::itsmft::Digit; +using densMap = std::pair; +using densMaps = std::vector; + +void checkFile(const std::unique_ptr& file); +densMaps makeDensMaps(int nLayers, std::string n = ""); +struct Maximum { + double y; + double vy; + Maximum(double y, double vy) : y{y}, vy{vy} {} +}; + +std::vector densityProjection(densMaps& maps, int nEvents, bool qed); +std::vector makeCanvasProjection(const densMaps& maps); + +std::vector CheckDigitsDensity(int nLayers = 3, int nEvents = 100, float pitchCol = 22.e-4f, + float pitchRow = 22.e-4f, + std::string digitFileName = "it3digits.root", + bool qed = false, bool batch = true) +{ + gROOT->SetBatch(batch); + + // Vars + const int nHemis = nLayers * 2; + o2::its3::SegmentationSuperAlpide temp; + std::vector seg; + std::vector totArea; + for (auto i = 0; i < nLayers; ++i) { + seg.emplace_back(i, pitchCol, pitchRow, 50.e-4, 26.f, temp.mRadii); + totArea.emplace_back(seg[i].mRadii[i] * seg[i].mLength * TMath::Pi()); + } + + // Digits + std::unique_ptr digitFile(TFile::Open(digitFileName.data())); + checkFile(digitFile); + auto digitTree = digitFile->Get("o2sim"); + std::vector digitArray; + std::vector* digitArrayPtr{&digitArray}; + digitTree->SetBranchAddress("IT3Digit", &digitArrayPtr); + std::vector hist; + auto densityMaps = makeDensMaps(nLayers); + auto densMG = new TMultiGraph("g_densMG", "Digit Density along z"); + for (auto i = 0; i < nLayers; ++i) { + hist.emplace_back(Form("h_L%d", i), Form("Digits L%i", i), seg[i].mNCols, 0, + seg[i].mNCols, seg[i].mNRows, 0, seg[i].mNRows); + } + + // Digits Loop + for (int iEntry = 0; digitTree->LoadTree(iEntry) >= 0; ++iEntry) { + digitTree->GetEntry(iEntry); + + // Digit + for (const auto& digit : digitArray) { + auto id = digit.getChipIndex(); + if (id >= nHemis) { // outside of Inner Barrel + continue; + } + auto col = digit.getColumn(); + auto row = digit.getRow(); + auto z = static_cast(seg[0].mLength) / + static_cast(seg[0].mNCols) * col - + seg[0].mLength / 2.f; + hist[id / 2].Fill(col, row); + if (id == 0 || id == 1) { + auto r = static_cast(TMath::Pi()) * seg[0].mRadii[0] / + static_cast(seg[0].mNRows) * row; + densityMaps[0].first->Fill(z, r); + } else if (id == 2 || id == 3) { + auto r = static_cast(TMath::Pi()) * seg[1].mRadii[1] / + static_cast(seg[1].mNRows) * row; + densityMaps[1].first->Fill(z, r); + } else if (id == 4 || id == 5) { + auto r = static_cast(TMath::Pi()) * seg[2].mRadii[2] / + static_cast(seg[2].mNRows) * row; + densityMaps[2].first->Fill(z, r); + } + } + } + + auto maxes = densityProjection(densityMaps, nEvents, qed); + + printf("----\n"); + printf("NEvents %d\n", nEvents); + for (auto i = 0; i < nLayers; ++i) { + printf("Layer %d\n", i); + auto nDigits = hist[i].Integral(); + printf("nDigits %f\n", nDigits); + printf("Number of digits per Area (cm^-2) per Event: %.2f\n", + nDigits / static_cast(totArea[i] * nEvents * 2)); + double nPixels = + seg[i].mNCols * seg[i].mNRows * 2; // both hemispheres combined + printf("Total number of Pixel %f\n", nPixels); + printf("Occupancy: %.6f\n", nDigits / (nPixels * nEvents)); + printf("L%i = %f +- %f\n", i, maxes[i].y, maxes[i].vy); + } + + std::unique_ptr oFile(TFile::Open("checkDigitsDensity.root", "RECREATE")); + checkFile(oFile); + for (auto& h : hist) { + h.Scale(1.f / (22.e-4 * 22.e-4 * nEvents * 2)); + h.Write(); + } + + auto canvDens = makeCanvasProjection(densityMaps); + for (auto& canvDen : canvDens) { + canvDen->Write(); + } + + auto canvDensLayers = new TCanvas("canvDensLayers", "", 1000, 800); + canvDensLayers->cd(); + for (const auto& [_, g] : densityMaps) { + densMG->Add(g); + g->Write(); + } + densMG->GetXaxis()->SetTitle("z (cm)"); + densMG->GetYaxis()->SetTitle("digit density (cm^{-2})"); + densMG->Draw("AP pmc plc"); + canvDensLayers->BuildLegend(0.75, 0.75, 0.9, 0.9); + canvDensLayers->Write(); + canvDensLayers->SaveAs("cDigits.pdf"); + + return maxes; +} + +void checkFile(const std::unique_ptr& file) +{ + if (!file || file->IsZombie()) { + printf("Could not open %s!\n", file->GetName()); + std::exit(1); + } +} + +// Make density plots for nLayers +densMaps makeDensMaps(int nLayers, std::string n) +{ + const o2::its3::SegmentationSuperAlpide ssAlpide; + const double half_length = ssAlpide.mLength / 2.f; + densMaps map; + for (int i = 0; i < nLayers; ++i) { + auto densityZ = + new TH2F(Form("h_densityZL%d%s", i, n.data()), "Local Digit Density", + static_cast(ssAlpide.mLength), -half_length, half_length, + static_cast(ssAlpide.mRadii[i] * o2::constants::math::PI / + (ssAlpide.mPitchRow * 100)), + 0.f, ssAlpide.mRadii[i] * o2::constants::math::PI); + densityZ->GetXaxis()->SetTitle("z (cm)"); + densityZ->GetYaxis()->SetTitle("#varphi"); + auto densityZErr = new TGraphErrors(densityZ->GetNbinsX()); + densityZErr->SetMarkerStyle(21); + densityZErr->SetTitle(Form("L%d", i)); + densityZErr->GetXaxis()->SetTitle("z (cm)"); + densityZErr->GetYaxis()->SetTitle("hit density (cm^{-2})"); + map.emplace_back(std::move(densityZ), std::move(densityZErr)); + } + return map; +} +std::vector densityProjection(densMaps& maps, int nEvents, bool qed) +{ + std::vector maxes; + for (auto& [densityZ, densityProZErr] : maps) { + double binArea = densityZ->GetXaxis()->GetBinWidth(1) * + densityZ->GetYaxis()->GetBinWidth(1); + double maxHitDensity = 0.f; + double maxHitDensityErr = 0.f; + // Calculate Hit density for each pixel + // For PbPb just per event + densityZ->Scale( + 1.f / + (2 * binArea * + nEvents)); // to increase statistics we looked at both hemispheres + if (qed) { + densityZ->Scale(qedFactor); + } + // Fill hit density projection + for (int x = 1; x <= densityZ->GetNbinsX(); ++x) { + std::vector v; + for (int y = 1; y <= densityZ->GetNbinsY(); ++y) { + if (densityZ->GetBinContent(x, y) > 0) { + v.push_back(densityZ->GetBinContent(x, y)); + } + } + double sum = std::accumulate(v.begin(), v.end(), 0.0); + double mean = sum / v.size(); + std::vector diff(v.size()); + std::transform(v.begin(), v.end(), diff.begin(), + [mean](double x) { return x - mean; }); + double sq_sum = + std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); + double stdev = std::sqrt(sq_sum / v.size()); + // Set point + densityProZErr->SetPoint(x - 1, densityZ->GetXaxis()->GetBinCenter(x), + mean); + densityProZErr->SetPointError( + x - 1, densityZ->GetXaxis()->GetBinWidth(x) / 2.f, stdev); + // update max + if (mean > maxHitDensity) { + maxHitDensity = mean; + maxHitDensityErr = stdev; + } + } + maxes.emplace_back(maxHitDensity, maxHitDensityErr); + } + return maxes; +} + +std::vector makeCanvasProjection(const densMaps& maps) +{ + std::vector canvases; + for (const auto& [densityZ, densityProZErr] : maps) { + auto canvDens = + new TCanvas(Form("c%s", densityZ->GetName()), "", 1600, 800); + canvDens->Divide(2, 1); + canvDens->cd(1); + densityZ->Draw("colz"); + canvDens->cd(2); + densityProZErr->Draw("AP"); + canvases.push_back(canvDens); + } + return canvases; +} + +void Plot() +{ + int i = 0; + int j = 0; + std::vector centralities{ + "0.00", "1.57", "2.22", "2.71", "3.13", "3.50", "4.94", + "6.05", "6.98", "7.81", "8.55", "9.23", "9.88", "10.47", + "11.04", "11.58", "12.09", "12.58", "13.05", "13.52", "13.97", + "14.43", "14.96", "15.67", "20.00"}; + std::vector> data; + for (auto it = centralities.cbegin(); it != centralities.cend() - 1; ++it) { + auto path = "./" + *it + "_" + *(it + 1) + "/"; + gSystem->cd(path.c_str()); + gSystem->Exec("pwd"); + data.push_back(CheckDigitsDensity()); + gSystem->cd(".."); + } + for (const auto& elem : data) { + std::cout << "+++++++++++++++++++++++++++++++++++++++++\n"; + std::cout << ++j << std::endl; + for (int i = 0; i < 3; ++i) { + std::cout << "===\nLayer " << i << "\n"; + std::cout << elem[i].y << " +/- " << elem[i].vy << "\n"; + } + std::cout << "-----------------------------------------" << std::endl; + } + auto c1 = new TCanvas("c1", "A Simple Graph Example", 200, 10, 700, 500); + const Int_t n = 24; + char const* range[n] = { + "0-1", "1-2", "2-3", "3-4", "4-5", "5-10", "10-15", "15-20", + "20-25", "25-30", "30-35", "35-40", "40-45", "45-50", "50-55", "55-60", + "60-65", "65-70", "70-75", "75-80", "80-85", "85-90", "90-95", "95-100"}; + double x[n]; + double y0[n], y1[n], y2[n]; + double vy0[n], vy1[n], vy2[n]; + for (i = 0; i < n; ++i) { + x[i] = i; + y0[i] = data[i][0].y; + y1[i] = data[i][1].y; + y2[i] = data[i][2].y; + vy0[i] = data[i][0].vy; + vy1[i] = data[i][1].vy; + vy2[i] = data[i][2].vy; + } + + auto h = new TH1F("h", "", n, x[0] - 0.5, x[n - 1] + 0.5); + h->SetTitle("Mean digit density per centrality class"); + h->GetYaxis()->SetTitleOffset(1.); + h->GetXaxis()->SetTitleOffset(1.); + h->GetYaxis()->SetTitle("digit density (cm^{-2})"); + h->GetXaxis()->SetTitle("centrality (%)"); + h->GetXaxis()->SetNdivisions(-10); + for (i = 1; i <= n; i++) + h->GetXaxis()->SetBinLabel(i, range[i - 1]); + h->SetMaximum(600); + h->SetMinimum(0); + h->SetStats(0); + h->Draw(""); + auto gr0 = new TGraphErrors(n, x, y0, nullptr, vy0); + gr0->SetMarkerStyle(4); + gr0->SetMarkerSize(0.5); + gr0->SetTitle("L0"); + auto gr1 = new TGraphErrors(n, x, y1, nullptr, vy1); + gr1->SetMarkerStyle(4); + gr1->SetMarkerSize(0.5); + gr1->SetTitle("L1"); + auto gr2 = new TGraphErrors(n, x, y2, nullptr, vy2); + gr2->SetMarkerStyle(4); + gr2->SetMarkerSize(0.5); + gr2->SetTitle("L2"); + auto mg = new TMultiGraph("mg", ""); + mg->Add(gr0); + mg->Add(gr1); + mg->Add(gr2); + mg->Draw("P pmc plc"); + auto leg = new TLegend(0.75, 0.75, 0.9, 0.9); + leg->AddEntry(gr0); + leg->AddEntry(gr1); + leg->AddEntry(gr2); + leg->Draw(); + + c1->SaveAs("its3DigitDensityCentrality.pdf"); +} diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckHits.C b/Detectors/Upgrades/ITS3/macros/test/CheckHits.C new file mode 100644 index 0000000000000..bb1a8073fc646 --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/test/CheckHits.C @@ -0,0 +1,466 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CheckHits.C +/// \brief analyze its3 hits +/// \author felix.schlepper@cern.ch + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define ENABLE_UPGRADES +#include "CommonConstants/MathConstants.h" +#include "ITS3Base/SegmentationSuperAlpide.h" +#include "ITSMFTSimulation/Hit.h" +#include "SimulationDataFormat/MCTrack.h" +#endif + +using o2::itsmft::Hit; + +constexpr int nLayers = 3; +constexpr double interaction_rate = 50e3; // Hz +constexpr double integration_time = 10e-6; // s +constexpr double qedXSection = 34962.1; // barn +constexpr double hadXSection = 8.f; // barn +constexpr double qedRate = qedXSection / hadXSection * interaction_rate; // Hz +constexpr double qedFactor = qedRate * integration_time; // a.u. +static constexpr std::array radii{1.9, 2.52, 3.15, 999}; +static const o2::its3::SegmentationSuperAlpide ssAlpide(0, 22.e-4, 22.e-4, 66.e-4, 26., radii.data()); + +struct Maximum { + double y; + double vy; + Maximum(double y, double vy) : y{y}, vy{vy} {} +}; + +struct Data { + double max; + double maxE; + double mean; + double meanRMS; +}; + +void checkFile(const std::unique_ptr& file); +TH2D* makeMap(int layer, std::string n); + +std::array CheckHits(bool qed = false, std::string hitFileName = "o2sim_HitsIT3.root", + std::string kineFileName = "o2sim_Kine.root", + bool batch = true) +{ + gROOT->SetBatch(batch); + // Vars + const int nHemis = nLayers * 2; + + if (qed) { + printf("----\n"); + printf("QEDXSection=%f;HadronXSection=%f;QEDRate=%f;QEDFactor=%f\n", + qedXSection, hadXSection, qedRate, qedFactor); + } + + // Hits + std::unique_ptr hitFile(TFile::Open(hitFileName.data())); + checkFile(hitFile); + auto hitTree = hitFile->Get("o2sim"); + std::vector hitArray, *hitArrayPtr{&hitArray}; + hitTree->SetBranchAddress("IT3Hit", &hitArrayPtr); + + // Kine + std::unique_ptr kineFile(TFile::Open(kineFileName.data())); + checkFile(kineFile); + auto kineTree = kineFile->Get("o2sim"); + std::vector trackArray, *trackArrayPtr{&trackArray}; + kineTree->SetBranchAddress("MCTrack", &trackArrayPtr); + + // Hit Plots + auto hitsXY = + new TH2F("h_hitsXY", "Hits XY", 1600, -5.f, 5.f, 1600, -5.f, 5.f); + hitsXY->GetXaxis()->SetTitle("x (cm)"); + hitsXY->GetYaxis()->SetTitle("y (cm)"); + auto hitsZY = + new TH2F("h_hitsZY", "Hits ZY", 1600, -30.f, 30.f, 1600, -5.f, 5.f); + hitsZY->GetXaxis()->SetTitle("z (cm)"); + hitsZY->GetYaxis()->SetTitle("y (cm)"); + std::array avgHitMaps{}; + for (int i = 0; i < nLayers; ++i) { + avgHitMaps[i] = makeMap(i, ""); + } + std::array avgHitMapsProj{}; + auto densMG = new TMultiGraph("g_densMG", "Hit Density along z"); + std::vector> eventHitMaps; + std::vector> eventHitMapsProj; + std::array, nLayers> eventMaxes; + // Hits Loop + const auto nEvents = hitTree->GetEntriesFast(); + for (int iEntry = 0; hitTree->LoadTree(iEntry) >= 0; ++iEntry) { + if (!qed) { + std::array maps{}; + for (int i = 0; i < nLayers; ++i) { + maps[i] = makeMap(i, Form("_event_%d", iEntry)); + } + eventHitMaps.push_back(maps); + } + hitTree->GetEntry(iEntry); + + // Hits + for (const auto& hit : hitArray) { + if (hit.GetDetectorID() >= + nHemis) { // outside of Inner Barrel, e.g. Outer Barrel + continue; + } + hitsXY->Fill(hit.GetX(), hit.GetY()); + hitsZY->Fill(hit.GetZ(), hit.GetY()); + auto z = hit.GetZ(); + auto phi = std::atan(hit.GetX() / (hit.GetY())) + + o2::constants::math::PI / 2.f; + if (hit.GetDetectorID() == 0) { + auto rphi = phi * ssAlpide.mRadii[0]; + if (!qed) { + eventHitMaps.back()[0]->Fill(z, rphi); + } + avgHitMaps[0]->Fill(z, rphi); + } else if (hit.GetDetectorID() == 2) { + auto rphi = phi * ssAlpide.mRadii[1]; + if (!qed) { + eventHitMaps.back()[1]->Fill(z, rphi); + } + avgHitMaps[1]->Fill(z, rphi); + } else if (hit.GetDetectorID() == 4) { + auto rphi = phi * ssAlpide.mRadii[2]; + if (!qed) { + eventHitMaps.back()[2]->Fill(z, rphi); + } + avgHitMaps[2]->Fill(z, rphi); + } + } + + if (!qed) { + std::array projs{}; + for (int iLayer = 0; iLayer < nLayers; ++iLayer) { + double binArea = eventHitMaps.back()[iLayer]->GetXaxis()->GetBinWidth(1) * + eventHitMaps.back()[iLayer]->GetYaxis()->GetBinWidth(1); + eventHitMaps.back()[iLayer]->Scale(1.f / binArea); + projs[iLayer] = eventHitMaps.back()[iLayer]->ProjectionX("_px", 0, -1, "e"); + projs[iLayer]->Scale(1. / eventHitMaps.back()[iLayer]->GetNbinsY()); + projs[iLayer]->SetMarkerStyle(21); + projs[iLayer]->SetTitle(Form("L%d", iLayer)); + projs[iLayer]->GetXaxis()->SetTitle("z (cm)"); + projs[iLayer]->GetYaxis()->SetTitle("hit density (cm^{-2})"); + eventMaxes[iLayer].push_back(projs[iLayer]->GetMaximum()); + } + eventHitMapsProj.push_back(projs); + } + } + + // Kinematic Plots + auto kineN = + new TH1D("h_kineN", "Multiplicity Stable particles", 100, -5.f, 5.f); + kineN->GetXaxis()->SetTitle("#eta"); + kineN->GetYaxis()->SetTitle("#frac{dN}{d#eta}"); + auto kineN05 = new TH1D("h_kineN05", "#||{#eta}<0.5", 50, -0.5f, 0.5f); + kineN05->GetXaxis()->SetTitle("#eta"); + kineN05->GetYaxis()->SetTitle("#frac{dN}{d#eta}"); + + // Kinematics Loop + for (int iEntry = 0; kineTree->LoadTree(iEntry) >= 0; ++iEntry) { + kineTree->GetEntry(iEntry); + + for (const auto& track : trackArray) { // MCTrack + // primary particles + auto vx = track.Vx(); + auto vy = track.Vy(); + if (vx * vx + vy * vy > 0.01) { + continue; + } + auto id = TMath::Abs(track.GetPdgCode()); + if (id != 2212 && // proton + id != 211 && // pion + id != 321 // kaon + ) { + continue; + } + kineN->Fill(track.GetEta()); + if (TMath::Abs(track.GetEta()) < 0.5) { + kineN05->Fill(track.GetEta()); + } + } + } + kineN->Scale(1.f / (kineN->GetXaxis()->GetBinWidth(1) * nEvents)); + kineN05->Scale(1.f / (kineN05->GetXaxis()->GetBinWidth(1) * nEvents)); + printf("----\n"); + printf("Kinematics: Abs(eta)<0.5=%.2f\n", kineN05->Integral("width")); + + // Projections + for (int i = 0; i < nLayers; ++i) { + double binArea = avgHitMaps[i]->GetXaxis()->GetBinWidth(1) * + avgHitMaps[i]->GetYaxis()->GetBinWidth(1); + avgHitMaps[i]->Scale(1.f / (binArea * nEvents)); + if (qed) { + avgHitMaps[i]->Scale(qedFactor); + } + avgHitMapsProj[i] = avgHitMaps[i]->ProjectionX("_px", 0, -1, "e"); + avgHitMapsProj[i]->Scale(1. / avgHitMaps[i]->GetNbinsY()); + avgHitMapsProj[i]->SetMarkerStyle(21); + avgHitMapsProj[i]->SetTitle(Form("L%d", i)); + } + + std::vector hMaxes; + hMaxes.reserve(nLayers); + for (int i = 0; i < nLayers; ++i) { + hMaxes.emplace_back(Form("hMaxes_%d", i), Form("RMS in Layer %d", i), 600, 0, 150); + } + + std::array ret; + // Report maximum + printf("----\n"); + printf("Max local hit density:\n"); + for (int i = 0; i < nLayers; ++i) { + printf("L%i = %f +- %f\n", i, avgHitMapsProj[i]->GetMaximum(), avgHitMapsProj[i]->GetBinError(avgHitMapsProj[i]->GetMaximumBin())); + + if (!qed) { + for (const auto& max : eventMaxes[i]) { + hMaxes[i].Fill(max); + } + + printf("Per Event Mean=%f with RMS=%f\n", hMaxes[i].GetMean(), + hMaxes[i].GetRMS()); + } + + Data r{}; + r.max = avgHitMapsProj[i]->GetMaximum(); + r.maxE = avgHitMapsProj[i]->GetBinError(avgHitMapsProj[i]->GetMaximumBin()); + if (!qed) { + r.mean = hMaxes[i].GetMean(); + r.meanRMS = hMaxes[i].GetRMS(); + } + ret[i] = r; + } + + std::unique_ptr oFile(TFile::Open("checkHits.root", "RECREATE")); + checkFile(oFile); + auto canvXY = new TCanvas("canvHits", "", 1600, 800); + canvXY->Divide(2, 1); + canvXY->cd(1); + hitsXY->Draw("colz"); + canvXY->cd(2); + hitsZY->Draw("colz"); + canvXY->Write(); + for (int i = 0; i < nLayers; ++i) { + auto canvDens = + new TCanvas(Form("c%s", avgHitMaps[i]->GetName()), "", 1600, 800); + canvDens->Divide(2, 1); + canvDens->cd(1); + avgHitMaps[i]->Draw("colz"); + canvDens->cd(2); + avgHitMapsProj[i]->GetXaxis()->SetTitle("z (cm)"); + avgHitMapsProj[i]->GetYaxis()->SetTitle("hit density (cm^{-2})"); + avgHitMapsProj[i]->Draw("AP"); + canvDens->Write(); + } + + auto canvDensLayers = new TCanvas("canvDensLayers", "", 1000, 800); + canvDensLayers->cd(); + for (int i = 0; i < nLayers; ++i) { + auto g = new TGraphErrors(avgHitMapsProj[i]); + densMG->Add(g); + g->Write(); + } + densMG->GetXaxis()->SetTitle("z (cm)"); + densMG->GetYaxis()->SetTitle("hit density (cm^{-2})"); + densMG->Draw("AP pmc plc"); + canvDensLayers->BuildLegend(0.75, 0.75, 0.9, 0.9); + canvDensLayers->Write(); + canvDensLayers->SaveAs("cHitsDens.pdf"); + + auto canvKineN = new TCanvas("canvKineN", "", 2000, 800); + canvKineN->Divide(2, 1); + auto pKineN = canvKineN->cd(1); + pKineN->SetLeftMargin(0.12); + kineN->Draw("hist"); + pKineN = canvKineN->cd(2); + pKineN->SetLeftMargin(0.12); + kineN05->Draw("hist"); + canvKineN->Write(); + + for (auto& h : eventHitMaps) { + for (auto& pp : h) { + pp->Write(); + } + } + for (auto& h : hMaxes) { + h.Write(); + } + return ret; +} + +void checkFile(const std::unique_ptr& file) +{ + if (!file || file->IsZombie()) { + printf("Could not open %s!\n", file->GetName()); + std::exit(1); + } +} + +TH2D* makeMap(int layer, std::string n) +{ + const double half_length = ssAlpide.mLength / 2.f; + auto map = + new TH2D(Form("h_densityZL%d%s", layer, n.data()), "Local Hit Density", + static_cast(ssAlpide.mLength), -half_length, half_length, + static_cast(ssAlpide.mRadii[layer] * o2::constants::math::PI / + (ssAlpide.mPitchRow * 100)), + 0.f, ssAlpide.mRadii[layer] * o2::constants::math::PI); + map->GetXaxis()->SetTitle("z (cm)"); + map->GetYaxis()->SetTitle("r#varphi"); + return map; +} + +void Plot() +{ + std::vector centralities{ + "0.00", "1.57", "2.22", "2.71", "3.13", "3.50", "4.94", + "6.05", "6.98", "7.81", "8.55", "9.23", "9.88", "10.47", + "11.04", "11.58", "12.09", "12.58", "13.05", "13.52", "13.97", + "14.43", "14.96", "15.67", "20.00"}; + std::vector> data; + for (auto it = centralities.cbegin(); it != centralities.cend() - 1; ++it) { + auto path = "./" + *it + "_" + *(it + 1) + "/"; + gSystem->cd(path.c_str()); + gSystem->Exec("pwd"); + data.push_back(CheckHits()); + gSystem->cd(".."); + } + for (const auto& elem : data) { + std::cout << "+++++++++++++++++++++++++++++++++++++++++\n"; + for (int i = 0; i < 3; ++i) { + std::cout << "===\nLayer " << i << "\n"; + std::cout << elem[i].max << " +/- " << elem[i].maxE << "\n"; + std::cout << elem[i].mean << " +/- " << elem[i].meanRMS << "\n"; + } + std::cout << "-----------------------------------------" << std::endl; + } + const Int_t n = 24; + char const* range[n] = { + "0-1", "1-2", "2-3", "3-4", "4-5", "5-10", "10-15", "15-20", + "20-25", "25-30", "30-35", "35-40", "40-45", "45-50", "50-55", "55-60", + "60-65", "65-70", "70-75", "75-80", "80-85", "85-90", "90-95", "95-100"}; + int i; + double x[n]; + double y0[n], y1[n], y2[n]; + double vy0[n], vy1[n], vy2[n]; + double y0M[n], y1M[n], y2M[n]; + double vy0M[n], vy1M[n], vy2M[n]; + for (i = 0; i < n; ++i) { + x[i] = i; + y0[i] = data[i][0].max; + y1[i] = data[i][1].max; + y2[i] = data[i][2].max; + vy0[i] = data[i][0].maxE; + vy1[i] = data[i][1].maxE; + vy2[i] = data[i][2].maxE; + y0M[i] = data[i][0].mean; + y1M[i] = data[i][1].mean; + y2M[i] = data[i][2].mean; + vy0M[i] = data[i][0].meanRMS; + vy1M[i] = data[i][1].meanRMS; + vy2M[i] = data[i][2].meanRMS; + } + + auto c1 = new TCanvas("c1", "A Simple Graph Example", 200, 10, 700, 500); + auto h = new TH1F("h", "", n, x[0] - 0.5, x[n - 1] + 0.5); + h->SetTitle("Mean hit density per centrality class"); + h->GetYaxis()->SetTitleOffset(1.); + h->GetXaxis()->SetTitleOffset(1.); + h->GetYaxis()->SetTitle("hit density (cm^{-2})"); + h->GetXaxis()->SetTitle("centrality (%)"); + h->GetXaxis()->SetNdivisions(-10); + for (i = 1; i <= n; i++) + h->GetXaxis()->SetBinLabel(i, range[i - 1]); + h->SetMaximum(70); + h->SetMinimum(0); + h->SetStats(0); + h->Draw(""); + auto gr0 = new TGraphErrors(n, x, y0, nullptr, vy0); + gr0->SetMarkerStyle(4); + gr0->SetMarkerSize(0.5); + gr0->SetTitle("L0"); + auto gr1 = new TGraphErrors(n, x, y1, nullptr, vy1); + gr1->SetMarkerStyle(4); + gr1->SetMarkerSize(0.5); + gr1->SetTitle("L1"); + auto gr2 = new TGraphErrors(n, x, y2, nullptr, vy2); + gr2->SetMarkerStyle(4); + gr2->SetMarkerSize(0.5); + gr2->SetTitle("L2"); + auto mg = new TMultiGraph("mg", ""); + mg->Add(gr0); + mg->Add(gr1); + mg->Add(gr2); + mg->Draw("P pmc plc"); + auto leg = new TLegend(0.75, 0.75, 0.9, 0.9); + leg->AddEntry(gr0); + leg->AddEntry(gr1); + leg->AddEntry(gr2); + leg->Draw(); + c1->SaveAs("its3HitsCentralityMean.pdf"); + + c1 = new TCanvas("c1", "A Simple Graph Example", 200, 10, 700, 500); + auto hh = new TH1F("hh", "", n, x[0] - 0.5, x[n - 1] + 0.5); + hh->SetTitle("Maximum hit density per centrality class per event with RMS"); + hh->GetYaxis()->SetTitleOffset(1.); + hh->GetXaxis()->SetTitleOffset(1.); + hh->GetYaxis()->SetTitle("hit density (cm^{-2})"); + hh->GetXaxis()->SetTitle("centrality (%)"); + hh->GetXaxis()->SetNdivisions(-10); + for (i = 1; i <= n; i++) + hh->GetXaxis()->SetBinLabel(i, range[i - 1]); + hh->SetMaximum(130); + hh->SetMinimum(0); + hh->SetStats(false); + hh->Draw(""); + auto gr0M = new TGraphErrors(n, x, y0M, nullptr, vy0M); + gr0M->SetMarkerStyle(4); + gr0M->SetMarkerSize(0.5); + gr0M->SetTitle("L0"); + auto gr1M = new TGraphErrors(n, x, y1M, nullptr, vy1M); + gr1M->SetMarkerStyle(4); + gr1M->SetMarkerSize(0.5); + gr1M->SetTitle("L1"); + auto gr2M = new TGraphErrors(n, x, y2M, nullptr, vy2M); + gr2M->SetMarkerStyle(4); + gr2M->SetMarkerSize(0.5); + gr2M->SetTitle("L2"); + auto mgM = new TMultiGraph("mg", ""); + mgM->Add(gr0M); + mgM->Add(gr1M); + mgM->Add(gr2M); + mgM->Draw("P pmc plc"); + auto legM = new TLegend(0.75, 0.75, 0.9, 0.9); + legM->AddEntry(gr0M); + legM->AddEntry(gr1M); + legM->AddEntry(gr2M); + legM->Draw(); + c1->SaveAs("its3HitsCentralityMaximum.pdf"); +} diff --git a/Detectors/Upgrades/ITS3/macros/test/CompareClusterSize.C b/Detectors/Upgrades/ITS3/macros/test/CompareClusterSize.C new file mode 100755 index 0000000000000..c8a0826cc2435 --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/test/CompareClusterSize.C @@ -0,0 +1,122 @@ +// Copyright 2020-2022 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file CompareClusterSize.C +/// \brief compare ITS2 cluster size with ITS3 one +/// \dependencies CheckClusterSize.C +/// \author felix.schlepper@cern.ch + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "CCDB/BasicCCDBManager.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#endif + +void CompareClusterSize(long timestamp = 0) +{ + gROOT->SetBatch(); + gStyle->SetOptStat(0); + + // Get cluster size from checkClusterSize.C output + std::unique_ptr its3ClusterFile(TFile::Open("checkClusterSize.root")); + std::unique_ptr its3Cluster(its3ClusterFile->Get("outerbarrel")); + its3Cluster->Sumw2(); + its3Cluster->SetDirectory(nullptr); + its3Cluster->Scale(1.0 / its3Cluster->Integral()); + its3Cluster->SetLineColor(kRed); + its3Cluster->SetLineWidth(2); + its3Cluster->SetFillColorAlpha(kRed + 2, 0.3); + + auto hITS = new TH1F("its", "", its3Cluster->GetNbinsX(), 0, + its3Cluster->GetNbinsX()); + + hITS->Sumw2(); + hITS->SetLineColor(kBlue); + hITS->SetDirectory(nullptr); + hITS->SetLineWidth(2); + hITS->SetFillColorAlpha(kBlue + 2, 0.3); + // get topology dict + auto& mgr = o2::ccdb::BasicCCDBManager::instance(); + mgr.setURL("http://alice-ccdb.cern.ch"); + mgr.setTimestamp(timestamp != 0 ? timestamp + : o2::ccdb::getCurrentTimestamp()); + const o2::itsmft::TopologyDictionary* dict = + mgr.get("ITS/Calib/ClusterDictionary"); + std::unique_ptr clusFile(TFile::Open("o2clus_its.root")); + auto clusTree = clusFile->Get("o2sim"); + std::vector clusArr, *clusArrP{&clusArr}; + clusTree->SetBranchAddress("ITSClusterComp", &clusArrP); + std::vector patterns; + std::vector* patternsPtr{&patterns}; + clusTree->SetBranchAddress("ITSClusterPatt", &patternsPtr); + std::vector rofRecVec, *rofRecVecP{&rofRecVec}; + clusTree->SetBranchAddress("ITSClustersROF", &rofRecVecP); + clusTree->GetEntry(0); + int nROFRec = (int)rofRecVec.size(); + auto pattIt = patternsPtr->cbegin(); + + for (int irof = 0; irof < nROFRec; irof++) { + const auto& rofRec = rofRecVec[irof]; + // rofRec.print(); + + for (int icl = 0; icl < rofRec.getNEntries(); icl++) { + int clEntry = rofRec.getFirstEntry() + icl; + const auto& cluster = clusArr[clEntry]; + // cluster.print(); + + auto pattId = cluster.getPatternID(); + int clusterSize = -1000; + if (pattId == o2::itsmft::CompCluster::InvalidPatternID || + dict->isGroup(pattId)) { + o2::itsmft::ClusterPattern patt(pattIt); + clusterSize = patt.getNPixels(); + } else { + clusterSize = dict->getNpixels(pattId); + } + hITS->Fill(clusterSize); + } + } + hITS->Scale(1.0 / hITS->Integral()); + + auto c = new TCanvas("c", "", 1200, 800); + c->SetLogy(); + hITS->GetYaxis()->SetRangeUser(1e-4, 1.); + hITS->GetXaxis()->SetTitle("cluster size"); + hITS->GetYaxis()->SetTitle("norm. counts"); + hITS->Draw("hist"); + its3Cluster->Draw("hist same"); + auto legend = new TLegend(0.6, 0.7, 0.9, 0.9); + legend->AddEntry( + hITS, Form("ITS2 data %.2f +/- %.2f", hITS->GetMean(), hITS->GetRMS()), + "f"); + legend->AddEntry(its3Cluster.get(), + Form("ITS3 MC %.2f +/- %.2f", its3Cluster->GetMean(), + its3Cluster->GetRMS()), + "f"); + legend->Draw(); + c->SaveAs("comp.pdf"); +} diff --git a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx index d508458d92e54..cbad587f7cf7f 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx @@ -231,7 +231,7 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID float nStepsInv = mParams.getNSimStepsInv(); int nSteps = mParams.getNSimSteps(); short detID{hit.GetDetectorID()}; - const auto& matrix = mGeometry->getMatrixL2G(detID); // <<<< ????? + const auto& matrix = mGeometry->getMatrixL2G(detID); bool innerBarrel{detID < mSuperSegmentations.size()}; math_utils::Vector3D xyzLocS, xyzLocE; xyzLocS = matrix ^ (hit.GetPosStart()); @@ -331,12 +331,8 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, uint32_t& maxFr, int evID // take into account that the AlpideSimResponse depth defintion has different min/max boundaries // although the max should coincide with the surface of the epitaxial layer, which in the chip // local coordinates has Y = +SensorLayerThickness/2 - float thickness = innerBarrel ? mSuperSegmentations[detID].mDetectorLayerThickness : Segmentation::SensorLayerThickness; - if (!innerBarrel) { - xyzLocS.SetY(xyzLocS.Y() + resp->getDepthMax() - Segmentation::SensorLayerThickness / 2.); - } else { - xyzLocS.SetY(xyzLocS.Y() * (mSuperSegmentations[detID].mSensorLayerThicknessEff / 2. / mSuperSegmentations[detID].mDetectorLayerThickness)); // to avoid holes in clusters // FIXME - } + float thickness = innerBarrel ? mSuperSegmentations[detID].mSensorLayerThicknessEff : Segmentation::SensorLayerThickness; + xyzLocS.SetY(xyzLocS.Y() + resp->getDepthMax() - thickness / 2.); // collect charge in evey pixel which might be affected by the hit for (int iStep = nSteps; iStep--;) { diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index 8ab06105e00c5..774b600ac3a09 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx @@ -68,7 +68,7 @@ void ITS3Layer::createLayer(TGeoVolume* motherVolume, double radiusBetweenLayer) TGeoVolume* volHalfLayer[nElements - 1]; for (int iEl{0}; iEl < nElements - 1; ++iEl) { TGeoMedium* med = (iEl <= 2) ? medSi : medAir; - if (iEl == 4) { + if (iEl == 3) { halfLayer[iEl] = new TGeoTubeSeg(rmin, rmax + radiusBetweenLayer, mZLen / 2, phiGap, piDeg - phiGap); volHalfLayer[iEl] = new TGeoVolume(names[iEl].data(), halfLayer[iEl], med); createCarbonFoamStructure(volHalfLayer[iEl], radiusBetweenLayer); @@ -180,7 +180,7 @@ void ITS3Layer::createLayerWithDeadZones(TGeoVolume* motherVolume, double radius } else { volHalfLayer[iEl] = new TGeoVolume(names[iEl].data(), halfLayer[iEl][0], med); volHalfLayer[iEl]->SetUniqueID(mChipTypeID); - if (iEl == 4) { + if (iEl == 3) { createCarbonFoamStructure(volHalfLayer[iEl], radiusBetweenLayer); volHalfLayer[iEl]->SetVisibility(true); volHalfLayer[iEl]->SetLineColor(kGray + 2); diff --git a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx index f66454ebad52e..e2078046d1966 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx @@ -189,7 +189,6 @@ void TrackerDPL::run(ProcessingContext& pc) LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; } - std::vector tracks; auto& allClusIdx = pc.outputs().make>(Output{"IT3", "TRACKCLSID", 0, Lifetime::Timeframe}); std::vector trackLabels; std::vector verticesLabels; @@ -281,7 +280,7 @@ void TrackerDPL::run(ProcessingContext& pc) for (unsigned int iROF{0}; iROF < rofs.size(); ++iROF) { auto& rof{rofs[iROF]}; - tracks = timeFrame->getTracks(iROF); + auto& tracks = timeFrame->getTracks(iROF); trackLabels = timeFrame->getTracksLabel(iROF); auto number{tracks.size()}; auto first{allTracks.size()}; diff --git a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/IndexTableUtils.h b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/IndexTableUtils.h index bfa2362155983..e7ae53d8f4d55 100644 --- a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/IndexTableUtils.h +++ b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/IndexTableUtils.h @@ -27,7 +27,7 @@ struct IndexTableUtils { int getPhiBin(float phi); int getBinIndex(float eta, float phi); std::vector getBinRect(float eta, float phi, float deltaEta, float deltaPhi); - int mEtaBins = 64, mPhiBins = 64; + int mEtaBins = 128, mPhiBins = 128; float minEta = -1.5, maxEta = 1.5; float minPhi = 0., maxPhi = 2 * TMath::Pi(); }; diff --git a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h index f6c8e81682fcc..c1b56c7c40110 100644 --- a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h +++ b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTracker.h @@ -290,7 +290,6 @@ class StrangenessTracker o2::base::PropagatorImpl::MatCorrType mCorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; // use mat correction std::vector> mDaughterTracks; // vector of daughter tracks (per thread) - StrangeTrack mStrangeTrack; // structure containing updated mother and daughter track refs ClusAttachments mStructClus; // # of attached tracks, 1 for mother, 2 for daughter ClassDefNV(StrangenessTracker, 1); diff --git a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTrackingConfigParam.h b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTrackingConfigParam.h index bc4ef7a8637a4..3b0226dbcdba7 100644 --- a/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTrackingConfigParam.h +++ b/Detectors/Vertexing/StrangenessTracking/include/StrangenessTracking/StrangenessTrackingConfigParam.h @@ -27,8 +27,8 @@ struct StrangenessTrackingParamConfig : public o2::conf::ConfigurableParamHelper // parameters float mRadiusTolIB = .3; // Radius tolerance for matching V0s in the IB float mRadiusTolOB = .1; // Radius tolerance for matching V0s in the OB - float mPhiBinSize = 0.1; // Phi bin size for the matching grid - float mEtaBinSize = 0.1; // Eta bin size for the matching grid + float mPhiBinSize = 0.02; // Phi bin size for the matching grid + float mEtaBinSize = 0.01; // Eta bin size for the matching grid float mMinMotherClus = 3.; // minimum number of cluster to be attached to the mother float mMaxChi2 = 50; // Maximum matching chi2 bool mVertexMatching = true; // Flag to enable/disable vertex matching diff --git a/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx b/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx index 97ce3d780faaa..e1ea987e6dbc8 100644 --- a/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx +++ b/Detectors/Vertexing/StrangenessTracking/src/StrangenessTracker.cxx @@ -207,8 +207,8 @@ void StrangenessTracker::processCascade(int iCasc, const Cascade& casc, const Ca std::array momV0, mombach; mFitter3Body[iThread].getTrack(0).getPxPyPzGlo(momV0); // V0 momentum at decay vertex mFitter3Body[iThread].getTrack(1).getPxPyPzGlo(mombach); // bachelor momentum at decay vertex - mStrangeTrack.mMasses[0] = calcMotherMass(momV0, mombach, PID::Lambda, PID::Pion); // Xi invariant mass at decay vertex - mStrangeTrack.mMasses[1] = calcMotherMass(momV0, mombach, PID::Lambda, PID::Kaon); // Omega invariant mass at decay vertex + strangeTrack.mMasses[0] = calcMotherMass(momV0, mombach, PID::Lambda, PID::Pion); // Xi invariant mass at decay vertex + strangeTrack.mMasses[1] = calcMotherMass(momV0, mombach, PID::Lambda, PID::Kaon); // Omega invariant mass at decay vertex LOG(debug) << "ITS Track matched with a Cascade decay topology ...."; LOG(debug) << "Number of ITS track clusters attached: " << itsTrack.getNumberOfClusters(); diff --git a/Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h index 49eb6670eae94..a10ff3d1b0f30 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/SVertexer.h @@ -16,6 +16,7 @@ #define O2_S_VERTEXER_H #include "gsl/span" +#include "DataFormatsCalibration/MeanVertexObject.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "ReconstructionDataFormats/PrimaryVertex.h" #include "ReconstructionDataFormats/V0.h" @@ -49,8 +50,6 @@ class CorrectionMapsHelper; namespace vertexing { -namespace o2d = o2::dataformats; - class SVertexer { public: @@ -112,7 +111,14 @@ class SVertexer int getN3Bodies() const { return mN3Bodies; } int getNStrangeTracks() const { return mNStrangeTracks; } auto& getMeanVertex() const { return mMeanVertex; } - void setMeanVertex(const o2d::VertexBase& v) { mMeanVertex = v; } + void setMeanVertex(const o2::dataformats::MeanVertexObject* v) + { + if (v == nullptr) { + return; + } + mMeanVertex = v->getMeanVertex(); + } + void setNThreads(int n); int getNThreads() const { return mNThreads; } void setUseMC(bool v) { mUseMC = v; } @@ -161,7 +167,7 @@ class SVertexer std::array, 2> mTracksPool{}; // pools of positive and negative seeds sorted in min VtxID std::array, 2> mVtxFirstTrack{}; // 1st pos. and neg. track of the pools for each vertex - o2d::VertexBase mMeanVertex{{0., 0., 0.}, {0.1 * 0.1, 0., 0.1 * 0.1, 0., 0., 6. * 6.}}; + o2::dataformats::VertexBase mMeanVertex{{0., 0., 0.}, {0.1 * 0.1, 0., 0.1 * 0.1, 0., 0., 6. * 6.}}; const SVertexerParams* mSVParams = nullptr; std::array mV0Hyps; std::array mCascHyps; diff --git a/Detectors/Vertexing/src/SVertexer.cxx b/Detectors/Vertexing/src/SVertexer.cxx index fda85b7e989fe..505f9baa2c2dc 100644 --- a/Detectors/Vertexing/src/SVertexer.cxx +++ b/Detectors/Vertexing/src/SVertexer.cxx @@ -170,14 +170,16 @@ void SVertexer::process(const o2::globaltracking::RecoContainer& recoData, o2::f for (int ith = 0; ith < mNThreads; ith++) { mNStrangeTracks += mStrTracker->getNTracks(ith); } - auto& strTracksOut = pc.outputs().make>(o2f::Output{"GLO", "STRANGETRACKS", 0, o2f::Lifetime::Timeframe}); - auto& strClustOut = pc.outputs().make>(o2f::Output{"GLO", "CLUSUPDATES", 0, o2f::Lifetime::Timeframe}); - o2::pmr::vector mcLabsOut; - strTracksOut.reserve(mNStrangeTracks); - strClustOut.reserve(mNStrangeTracks); + + std::vector strTracksTmp; + std::vector strClusTmp; + std::vector mcLabTmp; + strTracksTmp.reserve(mNStrangeTracks); + strClusTmp.reserve(mNStrangeTracks); if (mStrTracker->getMCTruthOn()) { - mcLabsOut.reserve(mNStrangeTracks); + mcLabTmp.reserve(mNStrangeTracks); } + for (int ith = 0; ith < mNThreads; ith++) { // merge results of all threads auto& strTracks = mStrTracker->getStrangeTrackVec(ith); auto& strClust = mStrTracker->getClusAttachments(ith); @@ -193,13 +195,39 @@ void SVertexer::process(const o2::globaltracking::RecoContainer& recoData, o2::f } else { LOGP(fatal, "Unknown strange track decay reference type {} for index {}", int(t.mPartType), t.mDecayRef); } - strTracksOut.push_back(t); - strClustOut.push_back(strClust[i]); + + strTracksTmp.push_back(t); + strClusTmp.push_back(strClust[i]); if (mStrTracker->getMCTruthOn()) { - mcLabsOut.push_back(stcTrMCLab[i]); + mcLabTmp.push_back(stcTrMCLab[i]); } } } + + auto& strTracksOut = pc.outputs().make>(o2f::Output{"GLO", "STRANGETRACKS", 0, o2f::Lifetime::Timeframe}); + auto& strClustOut = pc.outputs().make>(o2f::Output{"GLO", "CLUSUPDATES", 0, o2f::Lifetime::Timeframe}); + o2::pmr::vector mcLabsOut; + strTracksOut.resize(mNStrangeTracks); + strClustOut.resize(mNStrangeTracks); + if (mStrTracker->getMCTruthOn()) { + mcLabsOut.resize(mNStrangeTracks); + } + + std::vector sortIdx(strTracksTmp.size()); + std::iota(sortIdx.begin(), sortIdx.end(), 0); + // if mNTreads > 1 we need to sort tracks, clus and MCLabs by their mDecayRef + if (mNThreads > 1 && mNStrangeTracks > 1) { + std::sort(sortIdx.begin(), sortIdx.end(), [&strTracksTmp](int i1, int i2) { return strTracksTmp[i1].mDecayRef < strTracksTmp[i2].mDecayRef; }); + } + + for (int i = 0; i < (int)sortIdx.size(); i++) { + strTracksOut[i] = strTracksTmp[sortIdx[i]]; + strClustOut[i] = strClusTmp[sortIdx[i]]; + if (mStrTracker->getMCTruthOn()) { + mcLabsOut[i] = mcLabTmp[sortIdx[i]]; + } + } + if (mStrTracker->getMCTruthOn()) { auto& strTrMCLableOut = pc.outputs().make>(o2f::Output{"GLO", "STRANGETRACKS_MC", 0, o2f::Lifetime::Timeframe}); strTrMCLableOut.swap(mcLabsOut); @@ -1089,7 +1117,7 @@ bool SVertexer::processTPCTrack(const o2::tpc::TrackTPC& trTPC, GIndex gid, int const auto& vtx = mPVertices[vtxid]; auto twe = vtx.getTimeStamp(); int posneg = trTPC.getSign() < 0 ? 1 : 0; - auto trLoc = mTracksPool[posneg].emplace_back(TrackCand{trTPC, gid, {vtxid, vtxid}, 0.}); + auto& trLoc = mTracksPool[posneg].emplace_back(TrackCand{trTPC, gid, {vtxid, vtxid}, 0.}); auto err = correctTPCTrack(trLoc, trTPC, twe.getTimeStamp(), twe.getTimeStampError()); if (err < 0) { mTracksPool[posneg].pop_back(); // discard diff --git a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h index 87bb6886f7cad..2818146d75f32 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibData.h @@ -59,12 +59,18 @@ struct WaveformCalibData { inline void setFirstValid(int isig, int ipos) { if (ipos > mWave[isig].mFirstValid) { +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + printf("WaveformCalibChData::%s isig=%-2d mFirstValid %5d -> %5d\n", __func__, isig, mWave[isig].mFirstValid, ipos); +#endif mWave[isig].mFirstValid = ipos; } } inline void setLastValid(int isig, int ipos) { if (ipos < mWave[isig].mLastValid) { +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + printf("WaveformCalibChData::%s isig=%-2d mLastValid %5d -> %5d\n", __func__, isig, mWave[isig].mLastValid, ipos); +#endif mWave[isig].mLastValid = ipos; } } @@ -77,6 +83,7 @@ struct WaveformCalibData { int getLastValid(int is) const; void print() const; void clear(); + void clearWaveforms(); void setCreationTime(uint64_t ctime); void setN(int n); int saveDebugHistos(const std::string fn); diff --git a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h index b759d94ff781a..929190f09d162 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibEPN.h @@ -29,7 +29,7 @@ class WaveformCalibEPN public: WaveformCalibEPN() = default; int init(); - void clear(int ih = -1); + void clear(); int process(const gsl::span& bcrec, const gsl::span& energy, const gsl::span& tdc, @@ -54,7 +54,7 @@ class WaveformCalibEPN int mFirst = 0; int mLast = 0; - int mN = 1; + int mN = 10; void configure(int ifirst, int ilast) { @@ -64,6 +64,7 @@ class WaveformCalibEPN mFirst = ifirst; mLast = ilast; mN = ilast - ifirst + 1; + LOG(info) << "WaveformCalibEPN::" << __func__ << " mN=" << mN << "[" << mFirst << ":" << mLast << "]"; } WaveformCalibQueue mQueue; diff --git a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibQueue.h b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibQueue.h index a18f23fab7ea8..20d39b6fdb8ce 100644 --- a/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibQueue.h +++ b/Detectors/ZDC/calib/include/ZDCCalib/WaveformCalibQueue.h @@ -47,6 +47,7 @@ struct WaveformCalibQueue { int mNP = 0; // Number of interpolated points in waveform int mTimeLow[NChannels]; /// Cut on position difference low int mTimeHigh[NChannels]; /// Cut on position difference high + int mVerbosity = 0; const WaveformCalibConfig* mCfg = nullptr; diff --git a/Detectors/ZDC/calib/src/BaselineCalibConfig.cxx b/Detectors/ZDC/calib/src/BaselineCalibConfig.cxx index ac462250d20e6..ad9b889bd7d10 100644 --- a/Detectors/ZDC/calib/src/BaselineCalibConfig.cxx +++ b/Detectors/ZDC/calib/src/BaselineCalibConfig.cxx @@ -83,6 +83,6 @@ void BaselineCalibConfig::setCuts(int low, int high) void BaselineCalibConfig::setCuts(int isig, int low, int high) { - cutHigh[isig] = low; - cutLow[isig] = high; + cutLow[isig] = low; + cutHigh[isig] = high; } diff --git a/Detectors/ZDC/calib/src/InterCalibConfig.cxx b/Detectors/ZDC/calib/src/InterCalibConfig.cxx index c351cb31cf3c9..d0caff4c97d40 100644 --- a/Detectors/ZDC/calib/src/InterCalibConfig.cxx +++ b/Detectors/ZDC/calib/src/InterCalibConfig.cxx @@ -106,8 +106,8 @@ void InterCalibConfig::setCuts(double low, double high) void InterCalibConfig::setCuts(int ih, double low, double high) { - cutHigh[ih] = low; - cutLow[ih] = high; + cutLow[ih] = low; + cutHigh[ih] = high; } void InterCalibConfig::setBinning1D(int nb, double amin, double amax) diff --git a/Detectors/ZDC/calib/src/TDCCalibConfig.cxx b/Detectors/ZDC/calib/src/TDCCalibConfig.cxx index 7ca0254161ec9..d2bbbefb39abc 100644 --- a/Detectors/ZDC/calib/src/TDCCalibConfig.cxx +++ b/Detectors/ZDC/calib/src/TDCCalibConfig.cxx @@ -106,8 +106,8 @@ void TDCCalibConfig::setCuts(double low, double high) void TDCCalibConfig::setCuts(int ih, double low, double high) { - cutHigh[ih] = low; - cutLow[ih] = high; + cutLow[ih] = low; + cutHigh[ih] = high; } void TDCCalibConfig::setBinning1D(int nb, double amin, double amax) diff --git a/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx b/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx index fa0a5a78b3fcb..f0aeff5d53fc7 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibConfig.cxx @@ -21,8 +21,8 @@ WaveformCalibConfig::WaveformCalibConfig() cutHigh[isig] = std::numeric_limits::infinity(); } for (int itdc = 0; itdc < NTDCChannels; itdc++) { - cutTimeLow[itdc] = -2.5; - cutTimeHigh[itdc] = 2.5; + cutTimeLow[itdc] = -1.25; + cutTimeHigh[itdc] = 1.25; } } @@ -132,8 +132,8 @@ void WaveformCalibConfig::setCuts(double low, double high) void WaveformCalibConfig::setCuts(int isig, double low, double high) { - cutHigh[isig] = low; - cutLow[isig] = high; + cutLow[isig] = low; + cutHigh[isig] = high; } void WaveformCalibConfig::setTimeCuts(double low, double high) diff --git a/Detectors/ZDC/calib/src/WaveformCalibData.cxx b/Detectors/ZDC/calib/src/WaveformCalibData.cxx index 14063f9e93f40..759d85a6a0f88 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibData.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibData.cxx @@ -59,9 +59,15 @@ WaveformCalibChData& WaveformCalibChData::operator+=(const WaveformCalibChData& { if (other.mEntries > 0) { if (other.mFirstValid > mFirstValid) { +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + printf("WaveformCalibChData::+= mFirstValid %5d -> %5d\n", mFirstValid, other.mFirstValid); +#endif mFirstValid = other.mFirstValid; } if (other.mLastValid < mLastValid) { +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + printf("WaveformCalibChData::+= mLastValid %5d -> %5d\n", mLastValid, other.mLastValid); +#endif mLastValid = other.mLastValid; } mEntries = mEntries + other.mEntries; @@ -130,23 +136,23 @@ int WaveformCalibChData::getLastValid() const //______________________________________________________________________________ void WaveformCalibData::setN(int n) { - if (n >= 0 && n < NBT) { + if (n > 0 && n <= NBT) { mN = n; for (int is = 0; is < NChannels; is++) { mWave[is].setN(n); } } else { - LOG(fatal) << "WaveformCalibData " << __func__ << " wrong stored b.c. setting " << n << " not in range [0:" << NBT << "]"; + LOG(warn) << "WaveformCalibData " << __func__ << " wrong stored b.c. setting " << n << " not in range [1:" << NBT << "]"; } } void WaveformCalibChData::setN(int n) { - if (n >= 0 && n < NBT) { + if (n > 0 && n <= NBT) { mFirstValid = 0; mLastValid = n * NTimeBinsPerBC * TSN - 1; } else { - LOG(fatal) << "WaveformCalibChData " << __func__ << " wrong stored b.c. setting " << n << " not in range [0:" << NBT << "]"; + LOG(warn) << "WaveformCalibChData " << __func__ << " wrong stored b.c. setting " << n << " not in range [1:" << NBT << "]"; } } @@ -172,6 +178,8 @@ int WaveformCalibData::saveDebugHistos(const std::string fn) } h.SetEntries(mWave[is].mEntries); h.Write("", TObject::kOverwrite); + } else { + LOG(warn) << "WaveformCalibData " << __func__ << " waveform for ch " << is << " has too few entries: " << mWave[is].mEntries; } } f->Close(); @@ -191,6 +199,16 @@ void WaveformCalibData::clear() } } +//______________________________________________________________________________ +void WaveformCalibData::clearWaveforms() +{ + mCTimeBeg = 0; + mCTimeEnd = 0; + for (int32_t is = 0; is < NChannels; is++) { + mWave[is].clear(); + } +} + void WaveformCalibChData::clear() { mEntries = 0; diff --git a/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx b/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx index 37590995d9701..cda158e9f5b6a 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibEPN.cxx @@ -45,17 +45,29 @@ int WaveformCalibEPN::init() if (mVerbosity > DbgZero) { mQueue.printConf(); } + mQueue.mVerbosity = mVerbosity; // number of bins - mNBin = cfg->nbun * TSN; + mNBin = cfg->nbun * TSN * NTimeBinsPerBC; mFirst = cfg->ibeg; mLast = cfg->iend; mData.setN(cfg->nbun); mData.mPeak = mQueue.mPeak; + LOGF(info, "o2::zdc::WaveformCalibEPN::%s mNBin=%d mFirst=%d mLast=%d mN=%d mPeak=%d", __func__, mNBin, mFirst, mLast, cfg->nbun, mData.mPeak); mInitDone = true; return 0; } +void WaveformCalibEPN::clear() +{ +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + LOG(info) << "o2::zdc::WaveformCalibEPN::" << __func__; +#endif + // mQueue.clear(); + mData.clearWaveforms(); + mData.setN(mN); +} + //______________________________________________________________________________ int WaveformCalibEPN::process(const gsl::span& RecBC, const gsl::span& Energy, @@ -111,9 +123,10 @@ int WaveformCalibEPN::endOfRun() LOGF(info, "WaveformCalibEPN::endOfRun ts (%llu:%llu)", mData.mCTimeBeg, mData.mCTimeEnd); for (int is = 0; is < NChannels; is++) { if (mData.getEntries(is) > 0) { - LOGF(info, "Waveform %2d %s with %10d events and cuts AMP:(%g:%g) TDC:(%g:%g) Valid:[%d:%d:%d]", is, ChannelNames[is].data(), + int itdc = SignalTDC[is]; + LOGF(info, "Waveform %2d %s with %10d events and cuts AMP:(%g:%g) TDC:%d:(%g:%g) Valid:[%d:%d:%d]", is, ChannelNames[is].data(), mData.getEntries(is), mConfig->cutLow[is], mConfig->cutHigh[is], - mConfig->cutTimeLow[is], mConfig->cutTimeHigh[is], + itdc, mConfig->cutTimeLow[itdc], mConfig->cutTimeHigh[itdc], mData.getFirstValid(is), mData.mPeak, mData.getLastValid(is)); } } diff --git a/Detectors/ZDC/calib/src/WaveformCalibEPNSpec.cxx b/Detectors/ZDC/calib/src/WaveformCalibEPNSpec.cxx index d90f26d341515..62ee862905524 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibEPNSpec.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibEPNSpec.cxx @@ -88,10 +88,13 @@ void WaveformCalibEPNSpec::run(ProcessingContext& pc) mTimer.Stop(); mTimer.Reset(); mTimer.Start(false); + } else { + mWorker.clear(); } auto creationTime = pc.services().get().creation; // approximate time in ms WaveformCalibData& data = mWorker.getData(); + data.setCreationTime(creationTime); auto bcrec = pc.inputs().get>("bcrec"); diff --git a/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx b/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx index 25d48d9cbf825..c62306f21b1ad 100644 --- a/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx +++ b/Detectors/ZDC/calib/src/WaveformCalibQueue.cxx @@ -52,6 +52,9 @@ uint32_t WaveformCalibQueue::append(RecEventFlat& ev) auto& last = mIR.back(); // If BC are not consecutive, clear queue if (toadd.differenceInBC(last) > 1) { +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + LOG(info) << "WaveformCalibQueue::" << __func__ << " gap detected. Clearing " << mIR.size() << " bc"; +#endif clear(); } // If queue is not empty and is too long remove first element @@ -61,6 +64,9 @@ uint32_t WaveformCalibQueue::append(RecEventFlat& ev) // If BC are consecutive or cleared queue append element appendEv(ev); if (mIR.size() == mN) { +#ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + LOG(info) << "WaveformCalibQueue::" << __func__ << " processing " << mIR.size() << " bcs"; +#endif uint32_t mask = 0; for (int32_t itdc = 0; itdc < NTDCChannels; itdc++) { // Check which channels satisfy the condition on TDC @@ -92,6 +98,9 @@ uint32_t WaveformCalibQueue::append(RecEventFlat& ev) } return mask; } else { + // #ifdef O2_ZDC_WAVEFORMCALIB_DEBUG + // LOG(info) << "WaveformCalibQueue::" << __func__ << " IR size = " << mIR.size() << " != " << mN; + // #endif return 0; } } @@ -182,7 +191,7 @@ int WaveformCalibQueue::hasData(int isig, const gsl::span mTimeHigh[itdc]) { - // Put a warning message for a signal out of time - LOGF(warning, "%d.%04d Signal %2d peak position %d-%d=%d is outside allowed range [%d:%d]", mIR[mPk].orbit, mIR[mPk].bc, isig, ppos, mPeak, ppos - mPeak, mTimeLow[isig], mTimeHigh[isig]); + if (mVerbosity > DbgMinimal) { + // Put a warning message for a signal out of time + LOGF(warning, "%d.%04d Signal %2d peak position %d-%d=%d is outside allowed range [%d:%d]", mIR[mPk].orbit, mIR[mPk].bc, isig, ppos, mPeak, ppos - mPeak, mTimeLow[isig], mTimeHigh[isig]); + } return -1; } } @@ -277,7 +291,7 @@ int WaveformCalibQueue::addData(int isig, const gsl::span 0) { + if (!printed) { + printf("mNTDC:"); + printed = true; + } printf(" %2d=%6u", j, mNTDC[j][i]); } } - printf("\n"); - printf("mTDCA:"); + if (printed) { + printf("\n"); + printed = false; + } for (int j = 0; j < NTDCChannels; j++) { if (mNTDC[j][i] > 0) { + if (!printed) { + printf("mTDCA:"); + printed = true; + } printf(" %2d=%6.1f", j, mTDCA[j][i]); } } - printf("\n"); - printf("mTDCP:"); + if (printed) { + printf("\n"); + printed = false; + } for (int j = 0; j < NTDCChannels; j++) { if (mNTDC[j][i] > 0) { + if (!printed) { + printf("mTDCP:"); + printed = true; + } printf(" %2d=%6.1f", j, mTDCP[j][i]); } } - printf("\n"); + if (printed) { + printf("\n"); + } } } diff --git a/Detectors/ZDC/macro/CreateRecoConfigZDC.C b/Detectors/ZDC/macro/CreateRecoConfigZDC.C index 30808371bcff2..c504241346787 100644 --- a/Detectors/ZDC/macro/CreateRecoConfigZDC.C +++ b/Detectors/ZDC/macro/CreateRecoConfigZDC.C @@ -33,16 +33,16 @@ void CreateRecoConfigZDC(long tmin = 0, long tmax = -1, std::string ccdbHost = " // Offline trigger // Set trigger bitsincoincidence to ignore dead channels - // conf.setBit(IdZNAC); - // conf.setBit(IdZNASum); - // conf.setBit(IdZPAC); - // conf.setBit(IdZPASum); - // conf.setBit(IdZEM1); - // conf.setBit(IdZEM2); - // conf.setBit(IdZNCC); - // conf.setBit(IdZNCSum); - // conf.setBit(IdZPCC); - // conf.setBit(IdZPCSum); + // conf.setBit(TDCZNAC); + // conf.setBit(TDCZNAS); + // conf.setBit(TDCZPAC); + // conf.setBit(TDCZPAS); + // conf.setBit(TDCZEM1); + // conf.setBit(TDCZEM2); + // conf.setBit(TDCZNCC); + // conf.setBit(TDCZNCS); + // conf.setBit(TDCZPCC); + // conf.setBit(TDCZPCS); // conf.setTripleTrigger(); conf.setDoubleTrigger(); diff --git a/Detectors/ZDC/macro/CreateWaveformCalibConfig.C b/Detectors/ZDC/macro/CreateWaveformCalibConfig.C index 7c19eef5bbd64..fbe1063b0933d 100644 --- a/Detectors/ZDC/macro/CreateWaveformCalibConfig.C +++ b/Detectors/ZDC/macro/CreateWaveformCalibConfig.C @@ -13,6 +13,7 @@ #include #include +#include #include "Framework/Logger.h" #include "CCDB/CcdbApi.h" #include "ZDCBase/Constants.h" @@ -38,13 +39,29 @@ void CreateWaveformCalibConfig(long tmin = 0, long tmax = -1, std::string ccdbHo // range -2048 : 2047 one should not use signals too close to // maximum allowed amplitude (1800+2048) conf.setCuts(100, 3000); + conf.setCuts(o2::zdc::IdZNA1, 100, 2500); + conf.setCuts(o2::zdc::IdZNA2, 100, 2500); + conf.setCuts(o2::zdc::IdZNA3, 100, 2500); + conf.setCuts(o2::zdc::IdZNA4, 100, 2500); + conf.setCuts(o2::zdc::IdZPA1, 50, 2500); + conf.setCuts(o2::zdc::IdZPA2, 100, 2500); + conf.setCuts(o2::zdc::IdZPA3, 100, 2500); + conf.setCuts(o2::zdc::IdZPA4, 100, 2500); + conf.setCuts(o2::zdc::IdZNC1, 100, 2500); + conf.setCuts(o2::zdc::IdZNC2, 100, 2500); + conf.setCuts(o2::zdc::IdZNC3, 100, 2500); + conf.setCuts(o2::zdc::IdZNC4, 100, 2500); + conf.setCuts(o2::zdc::IdZPC1, 100, 2500); + conf.setCuts(o2::zdc::IdZPC2, 100, 2500); + conf.setCuts(o2::zdc::IdZPC3, 100, 2500); + conf.setCuts(o2::zdc::IdZPC4, 50, 2500); conf.setDescription("Simulated data"); conf.setMinEntries(200); // Restrict waveform range (default is -3, 6 as defined in WaveformCalib_NBB // WaveformCalib_NBA in file Detectors/ZDC/base/include/ZDCBase/Constants.h) - conf.restrictRange(-1, 0); + // conf.restrictRange(-1, 0); conf.print(); diff --git a/Detectors/ZDC/raw/include/ZDCRaw/DumpRaw.h b/Detectors/ZDC/raw/include/ZDCRaw/DumpRaw.h index 11da9a96533cf..41158552d86cb 100644 --- a/Detectors/ZDC/raw/include/ZDCRaw/DumpRaw.h +++ b/Detectors/ZDC/raw/include/ZDCRaw/DumpRaw.h @@ -54,6 +54,7 @@ class DumpRaw std::unique_ptr mCounts[NDigiChannels] = {nullptr}; std::unique_ptr mSignalA[NDigiChannels] = {nullptr}; std::unique_ptr mSignalT[NDigiChannels] = {nullptr}; + std::unique_ptr mSignalTH[NDigiChannels] = {nullptr}; std::unique_ptr mBunchA[NDigiChannels] = {nullptr}; // Bunch pattern ALICE std::unique_ptr mBunchT[NDigiChannels] = {nullptr}; // Bunch pattern Autotrigger std::unique_ptr mBunchH[NDigiChannels] = {nullptr}; // Bunch pattern Hit diff --git a/Detectors/ZDC/raw/src/DumpRaw.cxx b/Detectors/ZDC/raw/src/DumpRaw.cxx index 3a2cfd79f9ccb..7375c946fb3e3 100644 --- a/Detectors/ZDC/raw/src/DumpRaw.cxx +++ b/Detectors/ZDC/raw/src/DumpRaw.cxx @@ -133,6 +133,11 @@ void DumpRaw::init() TString htit = TString::Format("Signal mod. %d ch. %d AUTOT; Sample; ADC", imod, ich); mSignalT[i] = std::make_unique(hname, htit, nbx, xmin, xmax, ADCRange, ADCMin - 0.5, ADCMax + 0.5); } + if (mSignalTH[i] == nullptr) { + TString hname = TString::Format("hsth%d%d", imod, ich); + TString htit = TString::Format("Signal mod. %d ch. %d AUTOT & Hit; Sample; ADC", imod, ich); + mSignalTH[i] = std::make_unique(hname, htit, 2 * NTimeBinsPerBC, -0.5 - NTimeBinsPerBC, NTimeBinsPerBC - 0.5, ADCRange, ADCMin - 0.5, ADCMax + 0.5); + } if (mBunchA[i] == nullptr) { TString hname = TString::Format("hba%d%d", imod, ich); TString htit = TString::Format("Bunch mod. %d ch. %d ALICET; Sample; ADC", imod, ich); @@ -203,6 +208,10 @@ void DumpRaw::write() setStat(mSignalT[i].get()); mSignalT[i]->Write(); } + if (mSignalTH[i] && mSignalTH[i]->GetEntries() > 0) { + setStat(mSignalTH[i].get()); + mSignalTH[i]->Write(); + } } mTransmitted->Write(); mFired->Write(); @@ -279,11 +288,11 @@ int DumpRaw::process(const EventChData& ch) // Not empty event auto f = ch.f; int ih = getHPos(f.board, f.ch); - if (ih < 0) { + if (ih < 0 || ih >= NDigiChannels) { return -1; } - if (mVerbosity > 0) { + if (mVerbosity > 1) { for (int32_t iw = 0; iw < NWPerBc; iw++) { Digits2Raw::print_gbt_word(ch.w[iw]); } @@ -294,6 +303,10 @@ int DumpRaw::process(const EventChData& ch) mFired->Fill(f.board, f.ch); } + static int16_t prev_s[NDigiChannels][NTimeBinsPerBC] = {0}; + static uint32_t prev_orbit[NDigiChannels] = {0}; + static uint16_t prev_bc[NDigiChannels] = {0}; + uint16_t us[12]; int16_t s[12]; us[0] = f.s00; @@ -386,6 +399,12 @@ int DumpRaw::process(const EventChData& ch) mBits->Fill(ih, 2); if (f.Hit) { mBitsH->Fill(ih, 2); + if ((prev_orbit[ih] == f.orbit && (f.bc - prev_bc[ih]) == 1) || ((f.orbit - prev_orbit[ih]) == 1 && prev_bc[ih] == 3563 && f.bc == 0)) { + for (int32_t i = 0; i < 12; i++) { + mSignalTH[ih]->Fill(i + 0., double(s[i])); + mSignalTH[ih]->Fill(i - NTimeBinsPerBC + 0., double(prev_s[ih][i])); + } + } } for (int32_t i = 0; i < 12; i++) { mSignalT[ih]->Fill(i + 0., double(s[i])); @@ -429,6 +448,12 @@ int DumpRaw::process(const EventChData& ch) mError->Fill(ih); } } + // Save information to process next bunch crossing + prev_orbit[ih] = f.orbit; + prev_bc[ih] = f.bc; + for (int32_t i = 0; i < 12; i++) { + prev_s[ih][i] = s[i]; + } return 0; } diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/DigiReco.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/DigiReco.h index 32368791f9817..f611da568b2a8 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/DigiReco.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/DigiReco.h @@ -157,6 +157,7 @@ class DigiReco bool mLowPassFilterSet = false; /// Low pass filtering set via function call bool mFullInterpolation = false; /// Full waveform interpolation bool mFullInterpolationSet = false; /// Full waveform interpolation set via function call + int mFullInterpolationMinLength = 2; /// Minimum length to perform full interpolation int mInterpolationStep = 25; /// Coarse interpolation step bool mCorrSignal = true; /// Enable TDC signal correction bool mCorrSignalSet = false; /// TDC signal correction set via function call diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/RecoParamZDC.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/RecoParamZDC.h index 79b6bca3b1bc4..fe5652da6fc15 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/RecoParamZDC.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/RecoParamZDC.h @@ -33,10 +33,11 @@ struct RecoParamZDC : public o2::conf::ConfigurableParamHelper { uint8_t triggerCondition = 0x0; // Trigger condition: 0x1 single, 0x3 double and 0x7 triple // Signal processing - int low_pass_filter = -1; // Low pass filtering - int full_interpolation = -1; // Full interpolation of waveform - int corr_signal = -1; // TDC signal correction - int corr_background = -1; // TDC pile-up correction + int low_pass_filter = -1; // Low pass filtering + int full_interpolation = -1; // Full interpolation of waveform + int full_interpolation_min_length = -1; // Minimum length to perform full interpolation + int corr_signal = -1; // TDC signal correction + int corr_background = -1; // TDC pile-up correction int debug_output = -1; // Debug output diff --git a/Detectors/ZDC/reconstruction/src/DigiReco.cxx b/Detectors/ZDC/reconstruction/src/DigiReco.cxx index 136588cd0fdf4..bdf66c8a45a77 100644 --- a/Detectors/ZDC/reconstruction/src/DigiReco.cxx +++ b/Detectors/ZDC/reconstruction/src/DigiReco.cxx @@ -101,8 +101,12 @@ void DigiReco::init() } mFullInterpolation = ropt.full_interpolation > 0 ? true : false; } + if (ropt.full_interpolation_min_length >= 2) { + // Minimum is 2 consecutive bunch crossings + mFullInterpolationMinLength = ropt.full_interpolation_min_length; + } if (mVerbosity > DbgZero) { - LOG(info) << "Full waveform interpolation is " << (mFullInterpolation ? "enabled" : "disabled"); + LOG(info) << "Full waveform interpolation is " << (mFullInterpolation ? "enabled" : "disabled") << " min length is " << mFullInterpolationMinLength; } if (ropt.triggerCondition == 0) { @@ -828,8 +832,8 @@ int DigiReco::reconstructTDC(int ibeg, int iend) istop = ibun; } else { // No data from channel // A gap is detected - if (istart >= 0 && (istop - istart) > 0) { - // Need data for at least two consecutive bunch crossings + if (istart >= 0 && (istop - istart + 1) >= mFullInterpolationMinLength) { + // Need data for at least mFullInterpolationMinLength (two) consecutive bunch crossings int rval = fullInterpolation(isig, istart, istop); if (rval) { return rval; @@ -839,8 +843,8 @@ int DigiReco::reconstructTDC(int ibeg, int iend) istop = -1; } } - // Check if there are consecutive bunch crossings at the end of group - if (istart >= 0 && (istop - istart) > 0) { + // Check if there are mFullInterpolationMinLength consecutive bunch crossings at the end of group + if (istart >= 0 && (istop - istart + 1) >= mFullInterpolationMinLength) { int rval = fullInterpolation(isig, istart, istop); if (rval) { return rval; diff --git a/Detectors/ZDC/reconstruction/src/RecoConfigZDC.cxx b/Detectors/ZDC/reconstruction/src/RecoConfigZDC.cxx index 001c64100b82f..8e4bd173691b3 100644 --- a/Detectors/ZDC/reconstruction/src/RecoConfigZDC.cxx +++ b/Detectors/ZDC/reconstruction/src/RecoConfigZDC.cxx @@ -14,6 +14,15 @@ using namespace o2::zdc; +void RecoConfigZDC::setBit(uint32_t ibit, bool val) +{ + if (ibit >= 0 && ibit < NTDCChannels) { + bitset[ibit] = val; + } else { + LOG(fatal) << __func__ << " channel " << ibit << " not in allowed range"; + } +} + void RecoConfigZDC::setSearch(uint32_t ich, int val) { if (ich >= 0 && ich < NTDCChannels) { @@ -95,7 +104,7 @@ void RecoConfigZDC::print() const (storeEvPileup ? " StoreEvPileup(EvE)" : " DontStoreEvPileup(EvE)"), (triggerCondition == 0x1 ? " SINGLEtrigger" : (triggerCondition == 0x3 ? " DOUBLEtrigger" : (triggerCondition == 0x7 ? "TRIPLEtrigger" : "WRONGtrigger")))); for (int itdc = 0; itdc < NTDCChannels; itdc++) { - LOG(info) << itdc << " " << ChannelNames[TDCSignal[itdc]] << " search= " << tdc_search[itdc] << " = " << tdc_search[itdc] * FTDCVal << " ns"; + LOG(info) << itdc << " " << ChannelNames[TDCSignal[itdc]] << " search= " << tdc_search[itdc] << " = " << tdc_search[itdc] * FTDCVal << " ns" << (bitset[itdc] ? " BITSET" : ""); } for (Int_t ich = 0; ich < NChannels; ich++) { LOG(info) << ChannelNames[ich] << " integration: signal=[" << beg_int[ich] << ":" << end_int[ich] << "] pedestal=[" << beg_ped_int[ich] << ":" << end_ped_int[ich] diff --git a/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx b/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx index 1d47b1884c3f5..d504d33483081 100644 --- a/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx +++ b/Detectors/ZDC/workflow/src/DigitRecoSpec.cxx @@ -229,7 +229,9 @@ void DigitRecoSpec::run(ProcessingContext& pc) } // Limit the number of waveforms in output message if (mMaxWave > 0 && ntw >= mMaxWave) { - LOG(warning) << "Maximum number of output waveforms per TF reached: " << mMaxWave; + if (mVerbosity > DbgMinimal) { + LOG(warning) << "Maximum number of output waveforms per TF reached: " << mMaxWave; + } break; } } diff --git a/Detectors/gconfig/g4Config.C b/Detectors/gconfig/g4Config.C index 96650006270f5..8f74c0105dbf5 100644 --- a/Detectors/gconfig/g4Config.C +++ b/Detectors/gconfig/g4Config.C @@ -133,3 +133,17 @@ void Config() std::cout << "g4Config.C finished" << std::endl; } + +void Terminate() +{ + static bool terminated = false; + if (!terminated) { + std::cout << "Executing G4 terminate\n"; + TGeant4* geant4 = dynamic_cast(TVirtualMC::GetMC()); + if (geant4) { + // we need to call finish run for Geant4 ... Since we use ProcessEvent() interface; + geant4->FinishRun(); + } + terminated = true; + } +} diff --git a/Detectors/gconfig/include/SimSetup/SimSetup.h b/Detectors/gconfig/include/SimSetup/SimSetup.h index 5bf685ab9a4f5..020315fb0f2c0 100644 --- a/Detectors/gconfig/include/SimSetup/SimSetup.h +++ b/Detectors/gconfig/include/SimSetup/SimSetup.h @@ -10,11 +10,15 @@ // or submit itself to any jurisdiction. #ifndef O2_SIMSETUP_H +#define O2_SIMSETUP_H namespace o2 { struct SimSetup { static void setup(const char* engine); + + // a static entrypoint to shutdown the VMC system + static void shutdown(); }; } // namespace o2 diff --git a/Detectors/gconfig/src/G4Config.cxx b/Detectors/gconfig/src/G4Config.cxx index 662bb60067c42..b8a27f5c1975f 100644 --- a/Detectors/gconfig/src/G4Config.cxx +++ b/Detectors/gconfig/src/G4Config.cxx @@ -29,16 +29,23 @@ using std::endl; // these are used in commonConfig.C using o2::eventgen::DecayerPythia8; +#include "../g4Config.C" + namespace o2 { namespace g4config { -#include "../g4Config.C" void G4Config() { LOG(info) << "Setting up G4 sim from library code"; Config(); } + +void G4Terminate() +{ + Terminate(); +} + } // namespace g4config } // namespace o2 diff --git a/Detectors/gconfig/src/SimSetup.cxx b/Detectors/gconfig/src/SimSetup.cxx index 6e11c50fae865..e9dc1da667369 100644 --- a/Detectors/gconfig/src/SimSetup.cxx +++ b/Detectors/gconfig/src/SimSetup.cxx @@ -11,6 +11,7 @@ #include #include "SimSetup/SimSetup.h" +#include "TVirtualMC.h" #include #include "SetCuts.h" #include @@ -25,9 +26,8 @@ namespace o2 typedef void (*setup_fnc)(); -void setupFromPlugin(const char* libname, const char* setupfuncname) +void execFromPlugin(const char* libname, const char* funcname) { - LOG(info) << "Loading simulation plugin " << libname; auto libHandle = dlopen(libname, RTLD_NOW); // try to make the library loading a bit more portable: if (!libHandle) { @@ -43,26 +43,43 @@ void setupFromPlugin(const char* libname, const char* setupfuncname) libHandle = dlopen(stream.str().c_str(), RTLD_NOW); } assert(libHandle); - auto setup = (setup_fnc)dlsym(libHandle, setupfuncname); - assert(setup); - setup(); + auto func = (setup_fnc)dlsym(libHandle, funcname); + assert(func); + func(); +} + +void execSetupFromPlugin(const char* libname, const char* funcname) +{ + LOG(info) << "Seting up transport engine from library " << libname; + execFromPlugin(libname, funcname); } void SimSetup::setup(const char* engine) { if (strcmp(engine, "TGeant3") == 0) { - setupFromPlugin("libO2G3Setup", "_ZN2o28g3config8G3ConfigEv"); + execSetupFromPlugin("libO2G3Setup", "_ZN2o28g3config8G3ConfigEv"); } else if (strcmp(engine, "TGeant4") == 0) { - setupFromPlugin("libO2G4Setup", "_ZN2o28g4config8G4ConfigEv"); + execSetupFromPlugin("libO2G4Setup", "_ZN2o28g4config8G4ConfigEv"); } else if (strcmp(engine, "TFluka") == 0) { - setupFromPlugin("libO2FLUKASetup", "_ZN2o211flukaconfig11FlukaConfigEv"); + execSetupFromPlugin("libO2FLUKASetup", "_ZN2o211flukaconfig11FlukaConfigEv"); } else if (strcmp(engine, "MCReplay") == 0) { - setupFromPlugin("libO2MCReplaySetup", "_ZN2o214mcreplayconfig14MCReplayConfigEv"); + execSetupFromPlugin("libO2MCReplaySetup", "_ZN2o214mcreplayconfig14MCReplayConfigEv"); } else if (strcmp(engine, "O2TrivialMCEngine") == 0) { - setupFromPlugin("libO2O2TrivialMCEngineSetup", "_ZN2o223o2trivialmcengineconfig23O2TrivialMCEngineConfigEv"); + execSetupFromPlugin("libO2O2TrivialMCEngineSetup", "_ZN2o223o2trivialmcengineconfig23O2TrivialMCEngineConfigEv"); } else { LOG(fatal) << "Unsupported engine " << engine; } o2::SetCuts(); } + +// function to shutdown the engines and do some necessary +// finalisation work +void SimSetup::shutdown() +{ + auto vmc = TVirtualMC::GetMC(); + if (strcmp(vmc->GetName(), "TGeant4") == 0) { + execFromPlugin("libO2G4Setup", "_ZN2o28g4config11G4TerminateEv"); + } +} + } // namespace o2 diff --git a/EventVisualisation/Base/include/EventVisualisationBase/ConfigurationManager.h b/EventVisualisation/Base/include/EventVisualisationBase/ConfigurationManager.h index 74783252dff2a..f9abc00774298 100644 --- a/EventVisualisation/Base/include/EventVisualisationBase/ConfigurationManager.h +++ b/EventVisualisation/Base/include/EventVisualisationBase/ConfigurationManager.h @@ -25,7 +25,7 @@ namespace o2 namespace event_visualisation { /// Version of the software -const static std::string o2_eve_version = "1.60"; +const static std::string o2_eve_version = "1.70"; /// Configuration Manager allows an easy access to the config file. /// diff --git a/EventVisualisation/Base/src/ConfigurationManager.cxx b/EventVisualisation/Base/src/ConfigurationManager.cxx index c6e2f5400f4ab..6205f3c2ab67e 100644 --- a/EventVisualisation/Base/src/ConfigurationManager.cxx +++ b/EventVisualisation/Base/src/ConfigurationManager.cxx @@ -38,21 +38,21 @@ void ConfigurationManager::getConfig(TEnv& settings) const return; // precise located options file name read succesfully } if (settings.ReadFile(fileName = ".o2eve_config", kEnvUser) < 0) { - LOG(warn) << "could not find .o2eve_config in working directory! Trying .o2eve_config in home directory"; + LOGF(warn, "could not find .o2eve_config in working directory! Trying .o2eve_config in home directory"); if (settings.ReadFile(fileName = Form("%s/.o2eve_config", gSystem->Getenv("HOME")), kEnvUser) < 0) { - LOG(warn) << "could not find .o2eve_config in home directory! Trying o2eve_config in home directory"; + LOGF(warn, "could not find .o2eve_config in home directory! Trying o2eve_config in home directory"); if (settings.ReadFile(fileName = Form("%s/o2eve_config", gSystem->Getenv("HOME")), kEnvUser) < 0) { - LOG(warn) << "could not find o2eve_config in home directory! Trying o2eve_config in O2/EventVisualisation"; + LOGF(warn, "could not find o2eve_config in home directory! Trying o2eve_config in O2/EventVisualisation"); if (settings.ReadFile(fileName = Form("%s/EventVisualisation/o2eve_config", gSystem->Getenv("ALICEO2_INSTALL_PATH")), kEnvUser) < 0) { - LOG(fatal) << "could not find .o2eve_config or o2eve_config file!."; + LOGF(fatal, "could not find .o2eve_config or o2eve_config file!."); exit(0); } } } } - LOG(info) << Form("using %s config settings", fileName.Data()); + LOGF(info, Form("using %s config settings", fileName.Data())); } const ConfigurationManager* ConfigurationManager::loadSettings() diff --git a/EventVisualisation/Base/src/DataSourceOnline.cxx b/EventVisualisation/Base/src/DataSourceOnline.cxx index 35d6b8c8b7f0e..d6ed7c9adcb72 100644 --- a/EventVisualisation/Base/src/DataSourceOnline.cxx +++ b/EventVisualisation/Base/src/DataSourceOnline.cxx @@ -83,7 +83,7 @@ std::vector> } auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(stop - start); - LOG(info) << "getVisualisationList: " << duration.count(); + LOGF(info, "getVisualisationList: ", duration.count()); return res; } diff --git a/EventVisualisation/Base/src/DirectoryLoader.cxx b/EventVisualisation/Base/src/DirectoryLoader.cxx index fd4186bf6bde7..6bfe6f50d4b0a 100644 --- a/EventVisualisation/Base/src/DirectoryLoader.cxx +++ b/EventVisualisation/Base/src/DirectoryLoader.cxx @@ -104,10 +104,10 @@ std::string DirectoryLoader::getLatestFile(std::string& path, std::vector& ext, int remaining) +void DirectoryLoader::removeOldestFiles(std::string& path, std::vector& ext, const int remaining) { while (getNumberOfFiles(path, ext) > remaining) { - LOG(info) << "removing oldest file in folder: " << path << " : " << getLatestFile(path, ext); + LOGF(info, "removing oldest file in folder: ", path, " : ", getLatestFile(path, ext)); filesystem::remove(path + "/" + getLatestFile(path, ext)); } } diff --git a/EventVisualisation/Base/src/FileWatcher.cxx b/EventVisualisation/Base/src/FileWatcher.cxx index 7cc04d79d4130..99b27c706a793 100644 --- a/EventVisualisation/Base/src/FileWatcher.cxx +++ b/EventVisualisation/Base/src/FileWatcher.cxx @@ -143,8 +143,8 @@ int FileWatcher::getPos() const bool FileWatcher::refresh() { string previous = this->currentItem(); - LOG(info) << "previous:" << previous; - LOG(info) << "currentFile:" << this->mCurrentFile; + LOGF(info, "previous:", previous); + LOGF(info, "currentFile:", this->mCurrentFile); this->mFiles = DirectoryLoader::load(this->mDataFolders, "_", this->mExt); // already sorted according part staring with marker if (this->mCurrentFile != mEndGuard) { @@ -165,17 +165,17 @@ bool FileWatcher::refresh() this->mFiles.push_front(mLowGuard); this->mFiles.push_back(mEndGuard); - LOG(info) << "this->mFiles.size() = " << this->mFiles.size(); - LOG(info) << "this->mCurrentFile = " << this->mCurrentFile; - LOG(info) << "current:" << this->currentItem(); + LOGF(info, "this->mFiles.size() = ", this->mFiles.size()); + LOGF(info, "this->mCurrentFile = ", this->mCurrentFile); + LOGF(info, "current:", this->currentItem()); return previous != this->currentItem(); } void FileWatcher::setCurrentItem(int no) { this->mCurrentFile = this->mFiles[no]; - LOG(info) << "this->setCurrentItem(" << no << ")"; - LOG(info) << "this->mCurrentFile = " << this->mCurrentFile; + LOGF(info, "this->setCurrentItem(", no, ")"); + LOGF(info, "this->mCurrentFile = ", this->mCurrentFile); } std::string FileWatcher::currentFilePath() const diff --git a/EventVisualisation/Base/src/GeometryManager.cxx b/EventVisualisation/Base/src/GeometryManager.cxx index d41733c017de0..59f91ce097974 100644 --- a/EventVisualisation/Base/src/GeometryManager.cxx +++ b/EventVisualisation/Base/src/GeometryManager.cxx @@ -47,13 +47,13 @@ TEveGeoShape* GeometryManager::getGeometryForDetector(string detectorName) // load ROOT file with geometry TFile* f = TFile::Open(Form("%s/simple_geom_%s.root", geomPath.c_str(), detectorName.c_str())); if (!f) { - LOG(error) << "GeometryManager::GetSimpleGeom -- no file with geometry found for: " << detectorName << "!"; + LOGF(error, "GeometryManager::GetSimpleGeom -- no file with geometry found for: ", detectorName, "!"); return nullptr; } - LOG(info) << "GeometryManager::GetSimpleGeom for: " << detectorName << " from " - << Form("%s/simple_geom_%s.root", geomPath.c_str(), detectorName.c_str()); + LOGF(info, "GeometryManager::GetSimpleGeom for: ", detectorName, " from ", + Form("%s/simple_geom_%s.root", geomPath.c_str(), detectorName.c_str())); - TEveGeoShapeExtract* geomShapreExtract = static_cast(f->Get(detectorName.c_str())); + auto geomShapreExtract = dynamic_cast(f->Get(detectorName.c_str())); TEveGeoShape* geomShape = TEveGeoShape::ImportShapeExtract(geomShapreExtract); f->Close(); diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCalo.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCalo.h index 1d79897e57942..e7c00064a909b 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCalo.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCalo.h @@ -46,8 +46,9 @@ class VisualisationCalo std::string gid = ""; o2::dataformats::GlobalTrackID::Source source; }; + // Constructor with properties initialisation - VisualisationCalo(const VisualisationCaloVO& vo); + explicit VisualisationCalo(const VisualisationCaloVO& vo); VisualisationCalo(const VisualisationCalo& src); @@ -103,8 +104,6 @@ class VisualisationCalo int mPID; /// PDG code of the particle std::string mGID; /// String representation of gid - float mStartCoordinates[3]; /// Vector of track's start coordinates - float mEta; /// An angle from Z-axis to the radius vector pointing to the particle float mPhi; /// An angle from X-axis to the radius vector pointing to the particle diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCluster.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCluster.h index f70dc8cefd0b1..da07dcf39ce68 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCluster.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationCluster.h @@ -19,6 +19,7 @@ #include "ReconstructionDataFormats/GlobalTrackID.h" #include "rapidjson/document.h" +#include #include #include @@ -42,6 +43,14 @@ class VisualisationCluster public: // Default constructor VisualisationCluster(float XYZ[], float time); + VisualisationCluster(TVector3 xyz) + { + mTime = 0; + mCoordinates[0] = xyz[0]; + mCoordinates[1] = xyz[1]; + mCoordinates[2] = xyz[2]; + mSource = o2::dataformats::GlobalTrackID::HMP; + } float X() const { return mCoordinates[0]; } float Y() const { return mCoordinates[1]; } @@ -59,4 +68,4 @@ class VisualisationCluster }; } // namespace event_visualisation } // namespace o2 -#endif // ALICE_O2_DATACONVERTER_VISUALISATIONCLUSTER_H \ No newline at end of file +#endif // ALICE_O2_DATACONVERTER_VISUALISATIONCLUSTER_H diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationConstants.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationConstants.h index 3b9d0750b89d6..b118c19f00621 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationConstants.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationConstants.h @@ -36,6 +36,7 @@ enum EVisualisationGroup { EMC, PHS, CPV, + HMP, NvisualisationGroups }; @@ -49,7 +50,8 @@ const std::string gVisualisationGroupName[NvisualisationGroups] = { "MID", "EMC", "PHS", - "CPV"}; + "CPV", + "HMP"}; const bool R3Visualisation[NvisualisationGroups] = { true, //"ITS", @@ -61,7 +63,8 @@ const bool R3Visualisation[NvisualisationGroups] = { true, //"MID", true, //"EMC", true, //"PHS", - true, // "CPV" + true, //"CPV" + true, //"HMP" }; enum EVisualisationDataType { diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEvent.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEvent.h index 85e0c1a7a3c47..5e01e665a0f93 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEvent.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEvent.h @@ -89,6 +89,11 @@ class VisualisationEvent return mTracks.back().addCluster(pos); } + VisualisationCluster& addGlobalCluster(const TVector3& xyz) + { + return mClusters.emplace_back(xyz); + } + VisualisationCalo* addCalo(VisualisationCalo::VisualisationCaloVO vo) { mCalo.emplace_back(vo); diff --git a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h index 6b26a0d615789..233d43651db67 100644 --- a/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h +++ b/EventVisualisation/DataConverter/include/EventVisualisationDataConverter/VisualisationEventJSONSerializer.h @@ -28,10 +28,12 @@ namespace event_visualisation class VisualisationEventJSONSerializer : public VisualisationEventSerializer { + public: static int getIntOrDefault(rapidjson::Value& tree, const char* key, int defaultValue = 0); - float getFloatOrDefault(rapidjson::Value& tree, const char* key, float defaultValue = 0.0f); - std::string getStringOrDefault(rapidjson::Value& tree, const char* key, const char* defaultValue = ""); + static float getFloatOrDefault(rapidjson::Value& tree, const char* key, float defaultValue = 0.0f); + static std::string getStringOrDefault(rapidjson::Value& tree, const char* key, const char* defaultValue = ""); + private: std::string toJson(const VisualisationEvent& event) const; void fromJson(VisualisationEvent& event, std::string json); diff --git a/EventVisualisation/DataConverter/src/VisualisationEvent.cxx b/EventVisualisation/DataConverter/src/VisualisationEvent.cxx index 188c087e715b5..24448ae5e88dd 100644 --- a/EventVisualisation/DataConverter/src/VisualisationEvent.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationEvent.cxx @@ -86,6 +86,9 @@ VisualisationEvent::GIDVisualisation VisualisationEvent::mVis = [] { if (filter == o2::event_visualisation::EVisualisationGroup::PHS) { res.contains[o2::dataformats::GlobalTrackID::PHS][filter] = true; } + if (filter == o2::event_visualisation::EVisualisationGroup::HMP) { + res.contains[o2::dataformats::GlobalTrackID::HMP][filter] = true; + } } return res; }(); diff --git a/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx b/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx index c3663bf141f7f..e9de16551303d 100644 --- a/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationEventJSONSerializer.cxx @@ -18,10 +18,8 @@ #include #include #include -#include #include "rapidjson/document.h" -#include "rapidjson/writer.h" #include "rapidjson/prettywriter.h" #include "rapidjson/stringbuffer.h" @@ -40,7 +38,7 @@ void VisualisationEventJSONSerializer::toFile(const VisualisationEvent& event, s bool VisualisationEventJSONSerializer::fromFile(VisualisationEvent& event, std::string fileName) { - LOG(info) << "VisualisationEventJSONSerializer <- " << fileName; + LOGF(info, "VisualisationEventJSONSerializer <- ", fileName); if (FILE* file = fopen(fileName.c_str(), "r")) { fclose(file); // file exists } else { @@ -195,7 +193,7 @@ VisualisationCluster VisualisationEventJSONSerializer::clusterFromJSON(rapidjson XYZ[2] = jsonZ.GetDouble(); VisualisationCluster cluster(XYZ, 0); - cluster.mSource = o2::dataformats::GlobalTrackID::TPC; // temporary + cluster.mSource = o2::dataformats::GlobalTrackID::HMP; return cluster; } diff --git a/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx b/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx index 38da87d8bed51..fd9e70aa02153 100644 --- a/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx +++ b/EventVisualisation/DataConverter/src/VisualisationEventROOTSerializer.cxx @@ -188,7 +188,7 @@ void VisualisationEventROOTSerializer::toFile(const VisualisationEvent& event, s bool VisualisationEventROOTSerializer::fromFile(VisualisationEvent& event, std::string fileName) { - LOG(info) << "VisualisationEventROOTSerializer <- " << fileName; + LOGF(info, "VisualisationEventROOTSerializer <- ", fileName); event.mTracks.clear(); event.mClusters.clear(); event.mCalo.clear(); diff --git a/EventVisualisation/DataConverter/src/converter.cxx b/EventVisualisation/DataConverter/src/converter.cxx index 3ba11be3c3b9c..f33638f686d5c 100644 --- a/EventVisualisation/DataConverter/src/converter.cxx +++ b/EventVisualisation/DataConverter/src/converter.cxx @@ -26,9 +26,9 @@ int main(int argc, char** argv) { - LOG(info) << "Welcome in O2 event conversion tool"; + LOGF(info, "Welcome in O2 event conversion tool"); if (argc != 3) { - LOG(error) << "two filename required, second should point to not existent file"; + LOGF(error, "two filename required, second should point to not existent file"); exit(-1); } @@ -45,13 +45,13 @@ int main(int argc, char** argv) srcSerializer->fromFile(vEvent, src); endTime = std::chrono::high_resolution_clock::now(); - LOG(info) << "read took " - << std::chrono::duration_cast(endTime - currentTime).count() * 1e-6; + LOGF(info, "read took ", + std::chrono::duration_cast(endTime - currentTime).count() * 1e-6); currentTime = std::chrono::high_resolution_clock::now(); dstSerializer->toFile(vEvent, dst); endTime = std::chrono::high_resolution_clock::now(); - LOG(info) << "write took " - << std::chrono::duration_cast(endTime - currentTime).count() * 1e-6; + LOGF(info, "write took ", + std::chrono::duration_cast(endTime - currentTime).count() * 1e-6); return 0; } diff --git a/EventVisualisation/Geometry/simple_geom_HMP.root b/EventVisualisation/Geometry/simple_geom_HMP.root new file mode 100644 index 0000000000000..af37d833a4aa8 Binary files /dev/null and b/EventVisualisation/Geometry/simple_geom_HMP.root differ diff --git a/EventVisualisation/Geometry/simple_geom_ITS.root b/EventVisualisation/Geometry/simple_geom_ITS.root old mode 100755 new mode 100644 diff --git a/EventVisualisation/View/src/EventManager.cxx b/EventVisualisation/View/src/EventManager.cxx index 3f57d603cc4d4..0c0756583aa83 100644 --- a/EventVisualisation/View/src/EventManager.cxx +++ b/EventVisualisation/View/src/EventManager.cxx @@ -22,6 +22,7 @@ #include "EventVisualisationView/MultiView.h" #include "EventVisualisationView/Options.h" #include "EventVisualisationDataConverter/VisualisationEvent.h" +#include "EventVisualisationDataConverter/VisualisationEventJSONSerializer.h" #include #include "EventVisualisationBase/ConfigurationManager.h" #include "DataFormatsParameters/ECSDataAdapters.h" @@ -59,7 +60,7 @@ EventManager& EventManager::getInstance() EventManager::EventManager() : TEveEventManager("Event", "") { - LOG(info) << "Initializing TEveManager"; + LOGF(info, "Initializing TEveManager"); ConfigurationManager::getInstance().getConfig(settings); @@ -141,7 +142,6 @@ void EventManager::displayCurrentEvent() multiView->redraw3D(); auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(stop - start); - LOG(info) << "+++ displayCurrentEvent: " << duration.count(); } void EventManager::GotoEvent(Int_t no) @@ -221,7 +221,7 @@ void EventManager::displayVisualisationEvent(VisualisationEvent& event, const st { double eta = 0.1; size_t trackCount = event.getTrackCount(); - LOG(info) << "displayVisualisationEvent: " << trackCount << " detector: " << detectorName; + // tracks auto* list = new TEveTrackList(detectorName.c_str()); list->IncDenyDestroy(); @@ -266,7 +266,6 @@ void EventManager::displayVisualisationEvent(VisualisationEvent& event, const st if (trackCount != 0) { dataTypeLists[EVisualisationDataType::Tracks]->AddElement(list); if (detectorName != "MCH" && detectorName != "MFT" && detectorName != "MID") { - // LOG(info) << "phi: " << trackCount << " detector: " << detectorName; dataTypeListsPhi[EVisualisationDataType::Tracks]->AddElement(list); } } @@ -283,15 +282,14 @@ void EventManager::displayVisualisationEvent(VisualisationEvent& event, const st if (clusterCount != 0) { dataTypeLists[EVisualisationDataType::Clusters]->AddElement(point_list); if (detectorName != "MCH" && detectorName != "MFT" && detectorName != "MID") { - // LOG(info) << "phi: " << clusterCount << " detector: " << detectorName; dataTypeListsPhi[EVisualisationDataType::Clusters]->AddElement(point_list); } } - LOG(info) << "tracks: " << trackCount << " detector: " << detectorName << ":" - << dataTypeLists[EVisualisationDataType::Tracks]->NumChildren(); - LOG(info) << "clusters: " << clusterCount << " detector: " << detectorName << ":" - << dataTypeLists[EVisualisationDataType::Clusters]->NumChildren(); + LOGF(info, "tracks: ", trackCount, " detector: ", detectorName, ":", + dataTypeLists[EVisualisationDataType::Tracks]->NumChildren()); + LOGF(info, "clusters: ", clusterCount, " detector: ", detectorName, ":", + dataTypeLists[EVisualisationDataType::Clusters]->NumChildren()); } void EventManager::displayCalorimeters(VisualisationEvent& event, const std::string& detectorName) @@ -428,7 +426,7 @@ void EventManager::saveVisualisationSettings() return arr; }; - + d.AddMember("version", rapidjson::Value().SetString(o2_eve_version.c_str(), o2_eve_version.length()), allocator); d.AddMember("trackVisibility", jsonArray(vizSettings.trackVisibility, allocator), allocator); d.AddMember("trackColor", jsonArray(vizSettings.trackColor, allocator), allocator); d.AddMember("trackStyle", jsonArray(vizSettings.trackStyle, allocator), allocator); @@ -479,6 +477,11 @@ void EventManager::restoreVisualisationSettings() Document d; d.Parse(json.c_str()); + if (VisualisationEventJSONSerializer::getStringOrDefault(d, "version", "0.0") != o2_eve_version) { + LOGF(info, "visualisation settings has wrong version and was not restored"); + return; + } + auto updateArray = [](auto& array, const auto& document, const char* name, const auto& accessor) { for (size_t i = 0; i < elemof(array); ++i) { array[i] = accessor(document[name][i]); diff --git a/EventVisualisation/View/src/EventManagerFrame.cxx b/EventVisualisation/View/src/EventManagerFrame.cxx index d1ca190ab6475..ff5ee59047abb 100644 --- a/EventVisualisation/View/src/EventManagerFrame.cxx +++ b/EventVisualisation/View/src/EventManagerFrame.cxx @@ -382,10 +382,9 @@ void EventManagerFrame::checkMemory() if (success == 1) { // properly readed size = 4 * size / 1024; // in MB this->memoryUsedInfo = size; - LOG(info) << "Memory used: " << size << " memory allowed: " << memoryLimit; + LOGF(info, "Memory used: ", size, " memory allowed: ", memoryLimit); if (size > memoryLimit) { - LOG(error) << "Memory used: " << size << " exceeds memory allowed: " - << memoryLimit; + LOGF(error, "Memory used: ", size, " exceeds memory allowed: ", memoryLimit); exit(-1); } } @@ -412,7 +411,7 @@ void EventManagerFrame::createOutreachScreenshot() if (!std::filesystem::is_regular_file(fileName)) { std::vector ext = {".png"}; DirectoryLoader::removeOldestFiles(imageFolder, ext, (int)ConfigurationManager::getOutreachFilesMax()); - LOG(info) << "Outreach screenshot: " << fileName; + LOGF(info, "Outreach screenshot: ", fileName); Screenshot::perform("outreach", fileName, this->mEventManager->getDataSource()->getDetectorsMask(), this->mEventManager->getDataSource()->getRunNumber(), diff --git a/EventVisualisation/View/src/Initializer.cxx b/EventVisualisation/View/src/Initializer.cxx index f11d9bb22701a..e9572e24f991d 100644 --- a/EventVisualisation/View/src/Initializer.cxx +++ b/EventVisualisation/View/src/Initializer.cxx @@ -54,7 +54,7 @@ void Initializer::setup() false); // hide left and bottom tabs const string ocdbStorage = settings.GetValue("OCDB.default.path", o2::base::NameConf::getCCDBServer().c_str()); // default path to OCDB - LOG(info) << "Initializer -- OCDB path:" << ocdbStorage; + LOGF(info, "Initializer -- OCDB path:", ocdbStorage); auto& eventManager = EventManager::getInstance(); eventManager.setCdbPath(ocdbStorage); @@ -132,7 +132,7 @@ void Initializer::setupGeometry() } EVisualisationGroup det = static_cast(iDet); string detName = gVisualisationGroupName[det]; - LOG(info) << detName; + LOGF(info, detName); if (detName == "TPC" || detName == "MCH" || detName == "MID" || detName == "MFT") { // don't load MUON+MFT and AD and standard TPC to R-Phi view diff --git a/EventVisualisation/View/src/MultiView.cxx b/EventVisualisation/View/src/MultiView.cxx index a2f1beed99abf..e651a328dac2b 100644 --- a/EventVisualisation/View/src/MultiView.cxx +++ b/EventVisualisation/View/src/MultiView.cxx @@ -186,7 +186,7 @@ void MultiView::drawGeometryForDetector(string detectorName, bool threeD, bool r void MultiView::registerGeometry(TEveGeoShape* geom, bool threeD, bool rPhi, bool zy) { if (!geom) { - LOG(error) << "MultiView::registerGeometry -- geometry is NULL!"; + LOGF(error, "MultiView::registerGeometry -- geometry is NULL!"); exit(-1); } TEveProjectionManager* projection; diff --git a/EventVisualisation/View/src/Options.cxx b/EventVisualisation/View/src/Options.cxx index 56f44baa7e358..8fad13cc5686e 100644 --- a/EventVisualisation/View/src/Options.cxx +++ b/EventVisualisation/View/src/Options.cxx @@ -84,8 +84,7 @@ bool Options::processCommandLine(int argc, char* argv[]) } if (varmap.count("help")) { - LOG(info) << eveOptions << std::endl - << " default values are always taken from o2eve.json in current folder if present" << std::endl; + LOGF(info, printOptions()); return false; } diff --git a/EventVisualisation/View/src/Screenshot.cxx b/EventVisualisation/View/src/Screenshot.cxx index ed99463987f47..0391b52108f7e 100644 --- a/EventVisualisation/View/src/Screenshot.cxx +++ b/EventVisualisation/View/src/Screenshot.cxx @@ -252,7 +252,6 @@ std::string auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(stop - start); - LOG(info) << "++++++ Screenshot::perform: " << duration.count(); return fileName; // saved screenshod file name } diff --git a/EventVisualisation/View/src/main.cxx b/EventVisualisation/View/src/main.cxx index fd9ab254c93bb..7b13838446246 100644 --- a/EventVisualisation/View/src/main.cxx +++ b/EventVisualisation/View/src/main.cxx @@ -33,7 +33,7 @@ using namespace o2::event_visualisation; int main(int argc, char** argv) { - LOG(info) << "Welcome in O2 event visualisation tool (" << o2_eve_version << ")"; + LOGF(info, "Welcome in O2 event visualisation tool (", o2_eve_version, ")"); if (!Options::Instance()->processCommandLine(argc, argv)) { exit(-1); @@ -54,18 +54,16 @@ int main(int argc, char** argv) } // create ROOT application environment - TApplication* app = new TApplication("o2eve", &argc, argv); + auto app = new TApplication("o2eve", &argc, argv); gApplication = app; //app->Connect("TEveBrowser", "CloseWindow()", "TApplication", app, "Terminate()"); - LOG(info) << "Initializing TEveManager"; + LOGF(info, "Initializing TEveManager"); if (!TEveManager::Create(kTRUE, "FI")) { - LOG(fatal) << "Could not create TEveManager!!"; + LOGF(fatal, "Could not create TEveManager!!"); exit(0); } - //gEve->SpawnNewViewer("3D View", ""); exit(0); - // Initialize o2 Event Visualisation Initializer::setup(); @@ -76,6 +74,6 @@ int main(int argc, char** argv) TEveManager::Terminate(); app->Terminate(0); - LOG(info) << "O2 event visualisation tool terminated properly"; + LOGF(info, "O2 event visualisation tool terminated properly"); return 0; } diff --git a/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h b/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h index 1c3a50d6a7131..83b448568bbff 100644 --- a/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h +++ b/EventVisualisation/Workflow/include/EveWorkflow/EveWorkflowHelper.h @@ -160,6 +160,7 @@ class EveWorkflowHelper void drawMCHMID(GID gid, float trackTime); void drawPHS(GID gid); void drawEMC(GID gid); + void drawHMP(GID gid); void drawAODBarrel(AODBarrelTrack const& track, float trackTime); void drawAODMFT(AODMFTTrack const& track, float trackTime); @@ -175,6 +176,7 @@ class EveWorkflowHelper void drawTRDClusters(const o2::trd::TrackTRD& trc, float trackTime); void drawTOFClusters(GID gid, float trackTime); void drawPoint(float x, float y, float z, float trackTime) { mEvent.addCluster(x, y, z, trackTime); } + void drawGlobalPoint(const TVector3& xyx) { mEvent.addGlobalCluster(xyx); } void prepareITSClusters(const o2::itsmft::TopologyDictionary* dict); // fills mITSClustersArray void prepareMFTClusters(const o2::itsmft::TopologyDictionary* dict); // fills mMFTClustersArray void clear() { mEvent.clear(); } diff --git a/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h b/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h index b6eb49d19750f..b3431951ed41f 100644 --- a/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h +++ b/EventVisualisation/Workflow/include/EveWorkflow/O2DPLDisplay.h @@ -47,8 +47,8 @@ class TPCFastTransform; class O2DPLDisplaySpec : public o2::framework::Task { public: - static constexpr auto allowedTracks = "ITS,TPC,MFT,MCH,MID,ITS-TPC,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF,MCH-MID,MFT-MCH,MFT-MCH-MID,PHS,EMC"; - static constexpr auto allowedClusters = "ITS,TPC,TRD,TOF,MFT,MCH,MID,PHS,EMC"; + static constexpr auto allowedTracks = "ITS,TPC,MFT,MCH,MID,ITS-TPC,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,ITS-TPC-TRD-TOF,MCH-MID,MFT-MCH,MFT-MCH-MID,PHS,EMC,HMP"; + static constexpr auto allowedClusters = "ITS,TPC,TRD,TOF,MFT,MCH,MID,PHS,EMC,HMP"; O2DPLDisplaySpec(bool disableWrite, bool useMC, o2::dataformats::GlobalTrackID::mask_t trkMask, o2::dataformats::GlobalTrackID::mask_t clMask, diff --git a/EventVisualisation/Workflow/src/AO2DConverter.cxx b/EventVisualisation/Workflow/src/AO2DConverter.cxx index 488e0ce28034e..a1ae1b1c5623f 100644 --- a/EventVisualisation/Workflow/src/AO2DConverter.cxx +++ b/EventVisualisation/Workflow/src/AO2DConverter.cxx @@ -48,7 +48,7 @@ struct AO2DConverter { void init(o2::framework::InitContext& ic) { - LOG(info) << "------------------------ AO2DConverter::init version " << mWorkflowVersion << " ------------------------------------"; + LOGF(info, "------------------------ AO2DConverter::init version ", mWorkflowVersion, " ------------------------------------"); mData.init(); mHelper = std::make_shared(); diff --git a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx index ad10ae9e9b6cb..3b1b40df9aba3 100644 --- a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx +++ b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx @@ -258,6 +258,7 @@ void EveWorkflowHelper::selectTowers() { const auto& allTriggersPHOS = mRecoCont->getPHOSTriggers(); const auto& allTriggersEMCAL = mRecoCont->getEMCALTriggers(); + const auto& allTriggersHMP = mRecoCont->getHMPClusterTriggers(); auto filterTrigger = [&](const auto& trig) { const auto time = bcDiffToTFTimeMUS(trig.getBCData()); @@ -281,14 +282,22 @@ void EveWorkflowHelper::selectTowers() for (std::size_t iv = 0; iv < totalPrimaryVertices; iv++) { const auto& vtref = vtxRefs[iv]; + // to be improved to recognize PV + for (std::size_t i = 0; i < allTriggersHMP.size(); i++) { + mTotalDataTypes[GID::HMP]++; + const auto& trig = allTriggersHMP[i]; + mPrimaryVertexTriggerGIDs[iv].emplace_back(GID{static_cast(i), GID::HMP}); + } + const auto triggersPHOS = gsl::span(trackIndex.data() + vtref.getFirstEntryOfSource(GID::PHS), vtref.getEntriesOfSource(GID::PHS)); for (const auto& tvid : triggersPHOS) { mTotalDataTypes[GID::PHS]++; - const auto& trig = allTriggersPHOS[tvid.getIndex()]; - - if (filterTrigger(trig)) { - mPrimaryVertexTriggerGIDs[iv].emplace_back(tvid); + if (tvid.getIndex() < allTriggersPHOS.size()) { + const auto& trig = allTriggersPHOS[tvid.getIndex()]; + if (filterTrigger(trig)) { + mPrimaryVertexTriggerGIDs[iv].emplace_back(tvid); + } } } @@ -296,14 +305,23 @@ void EveWorkflowHelper::selectTowers() for (const auto& tvid : triggersEMCAL) { mTotalDataTypes[GID::EMC]++; - const auto& trig = allTriggersEMCAL[tvid.getIndex()]; + if (tvid.getIndex() < allTriggersEMCAL.size()) { + const auto& trig = allTriggersEMCAL[tvid.getIndex()]; - if (filterTrigger(trig)) { - mPrimaryVertexTriggerGIDs[iv].emplace_back(tvid); + if (filterTrigger(trig)) { + mPrimaryVertexTriggerGIDs[iv].emplace_back(tvid); + } } } } } else { + + for (std::size_t i = 0; i < allTriggersHMP.size(); i++) { + mTotalDataTypes[GID::HMP]++; + const auto& trig = allTriggersHMP[i]; + mPrimaryVertexTriggerGIDs[0].emplace_back(GID{static_cast(i), GID::HMP}); + } + for (std::size_t i = 0; i < allTriggersPHOS.size(); i++) { mTotalDataTypes[GID::PHS]++; const auto& trig = allTriggersPHOS[i]; @@ -432,6 +450,9 @@ void EveWorkflowHelper::draw(std::size_t primaryVertexIdx, bool sortTracks) case GID::EMC: drawEMC(gid); break; + case GID::HMP: + drawHMP(gid); + break; default: LOGF(info, "Trigger type %s not handled", gid.getSourceName()); break; @@ -565,6 +586,27 @@ void EveWorkflowHelper::prepareMFTClusters(const o2::itsmft::TopologyDictionary* } } +void EveWorkflowHelper::drawHMP(GID gid) +{ + o2::hmpid::Param* pParam = o2::hmpid::Param::instance(); + const auto& trig = mRecoCont->getHMPClusterTriggers()[gid.getIndex()]; + const auto& clusters = mRecoCont->getHMPClusters(); + + auto bc = trig.getBc(); + auto orbit = trig.getOrbit(); + auto time = o2::InteractionTimeRecord::bc2ns(bc, orbit); // in ns absolute time + + for (int j = trig.getFirstEntry(); j <= trig.getLastEntry(); j++) { + auto cluster = clusters[j]; + auto module = cluster.ch(); + double x = cluster.x(); + double y = cluster.y(); + + TVector3 vec3 = pParam->lors2Mars(module, x, y); + drawGlobalPoint(vec3); + } +} + void EveWorkflowHelper::drawPHS(GID gid) { const auto& cells = mRecoCont->getPHOSCells(); diff --git a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx index 653ba00941a1c..3885bbce959a8 100644 --- a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx +++ b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx @@ -90,7 +90,7 @@ void customize(std::vector& workflowOptions) #include "Framework/runDataProcessing.h" // main method must be included here (otherwise customize not used) void O2DPLDisplaySpec::init(InitContext& ic) { - LOG(info) << "------------------------ O2DPLDisplay::init version " << o2_eve_version << " ------------------------------------"; + LOGF(info, "------------------------ O2DPLDisplay::init version ", o2_eve_version, " ------------------------------------"); mData.mConfig.configProcessing.runMC = mUseMC; o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); } @@ -103,7 +103,7 @@ void O2DPLDisplaySpec::run(ProcessingContext& pc) if (this->mOnlyNthEvent && this->mEventCounter++ % this->mOnlyNthEvent != 0) { return; } - LOG(info) << "------------------------ O2DPLDisplay::run version " << o2_eve_version << " ------------------------------------"; + LOGF(info, "------------------------ O2DPLDisplay::run version ", o2_eve_version, " ------------------------------------"); // filtering out any run which occur before reaching next time interval auto currentTime = std::chrono::high_resolution_clock::now(); std::chrono::duration elapsed = currentTime - this->mTimeStamp; @@ -114,28 +114,23 @@ void O2DPLDisplaySpec::run(ProcessingContext& pc) o2::globaltracking::RecoContainer recoCont; recoCont.collectData(pc, *mDataRequest); updateTimeDependentParams(pc); // Make sure that this is called after the RecoContainer collect data, since some condition objects are fetched there - EveWorkflowHelper::FilterSet enabledFilters; enabledFilters.set(EveWorkflowHelper::Filter::ITSROF, this->mFilterITSROF); enabledFilters.set(EveWorkflowHelper::Filter::TimeBracket, this->mFilterTime); enabledFilters.set(EveWorkflowHelper::Filter::EtaBracket, this->mRemoveTPCEta); enabledFilters.set(EveWorkflowHelper::Filter::TotalNTracks, this->mNumberOfTracks != -1); - EveWorkflowHelper helper(enabledFilters, this->mNumberOfTracks, this->mTimeBracket, this->mEtaBracket, this->mPrimaryVertexMode); helper.setRecoContainer(&recoCont); - helper.setITSROFs(); helper.selectTracks(&(mData.mConfig.configCalib), mClMask, mTrkMask, mTrkMask); helper.selectTowers(); - helper.prepareITSClusters(mData.mITSDict); helper.prepareMFTClusters(mData.mMFTDict); const auto& tinfo = pc.services().get(); std::size_t filesSaved = 0; - auto processData = [&](const auto& dataMap) { for (const auto& keyVal : dataMap) { if (filesSaved >= mMaxPrimaryVertices) { @@ -185,7 +180,6 @@ void O2DPLDisplaySpec::run(ProcessingContext& pc) helper.clear(); } }; - if (mPrimaryVertexTriggers) { processData(helper.mPrimaryVertexTriggerGIDs); } else { @@ -247,12 +241,12 @@ void O2DPLDisplaySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) return; } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "ITS cluster dictionary updated"; + LOGF(info, "ITS cluster dictionary updated"); mData.setITSDict((const o2::itsmft::TopologyDictionary*)obj); return; } if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { - LOG(info) << "MFT cluster dictionary updated"; + LOGF(info, "MFT cluster dictionary updated"); mData.setMFTDict((const o2::itsmft::TopologyDictionary*)obj); return; } @@ -260,17 +254,17 @@ void O2DPLDisplaySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { - LOG(info) << "------------------------ defineDataProcessing " << o2_eve_version << " ------------------------------------"; + LOGF(info, "------------------------ defineDataProcessing ", o2_eve_version, " ------------------------------------"); WorkflowSpec specs; - std::string jsonFolder = cfgc.options().get("jsons-folder"); + auto jsonFolder = cfgc.options().get("jsons-folder"); std::string ext = ".root"; // root files are default format auto useJsonFormat = cfgc.options().get("use-json-format"); if (useJsonFormat) { ext = ".json"; } - std::string eveHostName = cfgc.options().get("eve-hostname"); + auto eveHostName = cfgc.options().get("eve-hostname"); o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); bool useMC = !cfgc.options().get("disable-mc"); bool disableWrite = cfgc.options().get("disable-write"); @@ -284,9 +278,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) char* colIdx = getenv("DDS_COLLECTION_INDEX"); int myIdx = colIdx ? atoi(colIdx) : -1; if (myIdx == eveDDSColIdx) { - LOG(important) << "Restricting DPL Display to collection index, my index " << myIdx << ", enabled " << int(myIdx == eveDDSColIdx); + LOGF(important, "Restricting DPL Display to collection index, my index ", myIdx, ", enabled ", int(myIdx == eveDDSColIdx)); } else { - LOG(info) << "Restricting DPL Display to collection index, my index " << myIdx << ", enabled " << int(myIdx == eveDDSColIdx); + LOGF(info, "Restricting DPL Display to collection index, my index ", myIdx, ", enabled ", int(myIdx == eveDDSColIdx)); } eveHostNameMatch &= myIdx == eveDDSColIdx; } @@ -310,7 +304,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) if (!srcTrk.any() && !srcCl.any()) { if (cfgc.options().get("skipOnEmptyInput")) { - LOG(info) << "No valid inputs for event display, disabling event display"; + LOGF(info, "No valid inputs for event display, disabling event display"); return std::move(specs); } throw std::runtime_error("No input configured"); diff --git a/Framework/AODMerger/src/aodMerger.cxx b/Framework/AODMerger/src/aodMerger.cxx index 8edaefb5efb2c..363350882caaa 100644 --- a/Framework/AODMerger/src/aodMerger.cxx +++ b/Framework/AODMerger/src/aodMerger.cxx @@ -26,6 +26,7 @@ #include #include "aodMerger.h" +#include // AOD merger with correct index rewriting // No need to know the datamodel because the branch names follow a canonical standard (identified by fIndex) @@ -450,7 +451,7 @@ int main(int argc, char* argv[]) } if (totalCompressed > 0 && totalUncompressed > 0) { for (auto const& tree : sizeCompressed) { - printf(" Tree %20s | Compressed: %12llu (%2.0f%%) | Uncompressed: %12llu (%2.0f%%)\n", tree.first.c_str(), tree.second, 100.0 * tree.second / totalCompressed, sizeUncompressed[tree.first], 100.0 * sizeUncompressed[tree.first] / totalUncompressed); + printf(" Tree %20s | Compressed: %12" PRIu64 " (%2.0f%%) | Uncompressed: %12" PRIu64 " (%2.0f%%)\n", tree.first.c_str(), tree.second, 100.0 * tree.second / totalCompressed, sizeUncompressed[tree.first], 100.0 * sizeUncompressed[tree.first] / totalUncompressed); } } } diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index eabc3f7c30593..898fb3f6441fd 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -109,7 +109,6 @@ o2_add_library(Framework src/ProcessingContext.cxx src/Plugins.cxx src/RateLimiter.cxx - src/ReadoutAdapter.cxx src/ResourcesMonitoringHelper.cxx src/ResourcePolicy.cxx src/ResourcePolicyHelpers.cxx diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index d5c384611f226..a3af88a2db16b 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -533,7 +533,7 @@ template using is_persistent_t = decltype(persistent_type_helper::test(nullptr)); template -using is_persistent_v = typename is_persistent_t::value; +constexpr auto is_persistent_v = is_persistent_t::value; template using is_external_index_t = typename std::conditional, std::true_type, std::false_type>::type; @@ -1261,6 +1261,21 @@ class Table return static_cast(*this).template getDynamicValue(); } + template + auto getValue() const + { + using COL = std::decay_t; + static_assert(is_dynamic_t() || is_persistent_v, "Should be persistent or dynamic column with no argument that has a return type convertable to float"); + return static_cast(static_cast(*this).get()); + } + + template + std::array getValues() const + { + static_assert(std::is_same_v || std::is_same_v, "The common return type should be float or double"); + return {getValue()...}; + } + using IP::size; using RowViewCore::operator++; @@ -1685,6 +1700,11 @@ std::tuple getRowData(arrow::Table* table, T rowIterator, decltype(auto) _Getter_() const \ { \ return *mColumnIterator; \ + } \ + \ + decltype(auto) get() const \ + { \ + return _Getter_(); \ } \ }; \ static const o2::framework::expressions::BindingNode _Getter_ { _Label_, typeid(_Name_).hash_code(), \ @@ -2315,6 +2335,11 @@ std::tuple getRowData(arrow::Table* table, T rowIterator, return boundGetter(std::make_index_sequence>{}, freeArgs...); \ } \ \ + type get() const \ + { \ + return _Getter_(); \ + } \ + \ template \ type boundGetter(std::integer_sequence&&, FreeArgs... freeArgs) const \ { \ diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index 1c1871608391e..5f8f7fef5f506 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -21,8 +21,6 @@ #include "CommonConstants/ZDCConstants.h" #include "SimulationDataFormat/MCGenProperties.h" -using namespace o2::constants::math; - namespace o2 { namespace aod @@ -122,13 +120,13 @@ DECLARE_SOA_COLUMN(Snp, snp, float); //! DECLARE_SOA_COLUMN(Tgl, tgl, float); //! DECLARE_SOA_COLUMN(Signed1Pt, signed1Pt, float); //! (sign of charge)/Pt in c/GeV. Use pt() and sign() instead DECLARE_SOA_EXPRESSION_COLUMN(Phi, phi, float, //! Phi of the track, in radians within [0, 2pi) - ifnode(nasin(aod::track::snp) + aod::track::alpha < 0.0f, nasin(aod::track::snp) + aod::track::alpha + TwoPI, - ifnode(nasin(aod::track::snp) + aod::track::alpha >= TwoPI, nasin(aod::track::snp) + aod::track::alpha - TwoPI, + ifnode(nasin(aod::track::snp) + aod::track::alpha < 0.0f, nasin(aod::track::snp) + aod::track::alpha + o2::constants::math::TwoPI, + ifnode(nasin(aod::track::snp) + aod::track::alpha >= o2::constants::math::TwoPI, nasin(aod::track::snp) + aod::track::alpha - o2::constants::math::TwoPI, nasin(aod::track::snp) + aod::track::alpha))); DECLARE_SOA_EXPRESSION_COLUMN(Eta, eta, float, //! Pseudorapidity - -1.f * nlog(ntan(PIQuarter - 0.5f * natan(aod::track::tgl)))); + -1.f * nlog(ntan(o2::constants::math::PIQuarter - 0.5f * natan(aod::track::tgl)))); DECLARE_SOA_EXPRESSION_COLUMN(Pt, pt, float, //! Transverse momentum of the track in GeV/c - ifnode(nabs(aod::track::signed1Pt) <= Almost0, VeryBig, nabs(1.f / aod::track::signed1Pt))); + ifnode(nabs(aod::track::signed1Pt) <= o2::constants::math::Almost0, o2::constants::math::VeryBig, nabs(1.f / aod::track::signed1Pt))); DECLARE_SOA_DYNAMIC_COLUMN(IsWithinBeamPipe, isWithinBeamPipe, //! Is the track within the beam pipe (= successfully propagated to a collision vertex) [](float x) -> bool { return (std::fabs(x) < o2::constants::geom::XBeamPipeOuterRef); }); DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, //! Charge: positive: 1, negative: -1 @@ -156,18 +154,18 @@ DECLARE_SOA_DYNAMIC_COLUMN(Pz, pz, //! Momentum in z-direction in GeV/c }); DECLARE_SOA_EXPRESSION_COLUMN(P, p, float, //! Momentum in Gev/c - ifnode(nabs(aod::track::signed1Pt) <= Almost0, VeryBig, 0.5f * (ntan(PIQuarter - 0.5f * natan(aod::track::tgl)) + 1.f / ntan(PIQuarter - 0.5f * natan(aod::track::tgl))) / nabs(aod::track::signed1Pt))); + ifnode(nabs(aod::track::signed1Pt) <= o2::constants::math::Almost0, o2::constants::math::VeryBig, 0.5f * (ntan(o2::constants::math::PIQuarter - 0.5f * natan(aod::track::tgl)) + 1.f / ntan(o2::constants::math::PIQuarter - 0.5f * natan(aod::track::tgl))) / nabs(aod::track::signed1Pt))); DECLARE_SOA_DYNAMIC_COLUMN(Energy, energy, //! Track energy, computed under the mass assumption given as input [](float signed1Pt, float tgl, float mass) -> float { const auto pt = 1.f / std::abs(signed1Pt); - const auto p = 0.5f * (tan(PIQuarter - 0.5f * atan(tgl)) + 1.f / tan(PIQuarter - 0.5f * atan(tgl))) * pt; + const auto p = 0.5f * (tan(o2::constants::math::PIQuarter - 0.5f * atan(tgl)) + 1.f / tan(o2::constants::math::PIQuarter - 0.5f * atan(tgl))) * pt; return sqrt(p * p + mass * mass); }); DECLARE_SOA_DYNAMIC_COLUMN(Rapidity, rapidity, //! Track rapidity, computed under the mass assumption given as input [](float signed1Pt, float tgl, float mass) -> float { const auto pt = 1.f / std::abs(signed1Pt); const auto pz = pt * tgl; - const auto p = 0.5f * (tan(PIQuarter - 0.5f * atan(tgl)) + 1.f / tan(PIQuarter - 0.5f * atan(tgl))) * pt; + const auto p = 0.5f * (tan(o2::constants::math::PIQuarter - 0.5f * atan(tgl)) + 1.f / tan(o2::constants::math::PIQuarter - 0.5f * atan(tgl))) * pt; const auto energy = sqrt(p * p + mass * mass); return 0.5f * log((energy + pz) / (energy - pz)); }); @@ -223,7 +221,8 @@ DECLARE_SOA_EXPRESSION_COLUMN(C1Pt21Pt2, c1Pt21Pt2, float, //! // TRACKEXTRA TABLE definition DECLARE_SOA_COLUMN(TPCInnerParam, tpcInnerParam, float); //! Momentum at inner wall of the TPC DECLARE_SOA_COLUMN(Flags, flags, uint32_t); //! Track flags. Run 2: see TrackFlagsRun2Enum | Run 3: see TrackFlags -DECLARE_SOA_COLUMN(ITSClusterMap, itsClusterMap, uint8_t); //! ITS cluster map, one bit per a layer, starting from the innermost +DECLARE_SOA_COLUMN(ITSClusterSizes, itsClusterSizes, uint32_t); //! Clusters sizes, four bits per a layer, starting from the innermost +DECLARE_SOA_COLUMN(ITSClusterMap, itsClusterMap, uint8_t); //! Old cluster ITS cluster map, kept for version 0 compatibility DECLARE_SOA_COLUMN(TPCNClsFindable, tpcNClsFindable, uint8_t); //! Findable TPC clusters for this track geometry DECLARE_SOA_COLUMN(TPCNClsFindableMinusFound, tpcNClsFindableMinusFound, int8_t); //! TPC Clusters: Findable - Found DECLARE_SOA_COLUMN(TPCNClsFindableMinusCrossedRows, tpcNClsFindableMinusCrossedRows, int8_t); //! TPC Clusters: Findable - crossed rows @@ -241,11 +240,53 @@ DECLARE_SOA_COLUMN(TrackEtaEMCAL, trackEtaEmcal, float); DECLARE_SOA_COLUMN(TrackPhiEMCAL, trackPhiEmcal, float); //! DECLARE_SOA_COLUMN(TrackTime, trackTime, float); //! Estimated time of the track in ns wrt collision().bc() or ambiguoustrack.bcSlice()[0] DECLARE_SOA_COLUMN(TrackTimeRes, trackTimeRes, float); //! Resolution of the track time in ns (see TrackFlags::TrackTimeResIsRange) -DECLARE_SOA_EXPRESSION_COLUMN(DetectorMap, detectorMap, uint8_t, //! Detector map: see enum DetectorMapEnum + +// expression columns changing between versions have to be declared in different namespaces + +DECLARE_SOA_EXPRESSION_COLUMN(DetectorMap, detectorMap, uint8_t, //! Detector map: see enum DetectorMapEnum ifnode(aod::track::itsClusterMap > (uint8_t)0, static_cast(o2::aod::track::ITS), (uint8_t)0x0) | ifnode(aod::track::tpcNClsFindable > (uint8_t)0, static_cast(o2::aod::track::TPC), (uint8_t)0x0) | ifnode(aod::track::trdPattern > (uint8_t)0, static_cast(o2::aod::track::TRD), (uint8_t)0x0) | ifnode((aod::track::tofChi2 >= 0.f) && (aod::track::tofExpMom > 0.f), static_cast(o2::aod::track::TOF), (uint8_t)0x0)); + +namespace v001 +{ +DECLARE_SOA_EXPRESSION_COLUMN(DetectorMap, detectorMap, uint8_t, //! Detector map version 1, see enum DetectorMapEnum + ifnode(aod::track::itsClusterSizes > (uint32_t)0, static_cast(o2::aod::track::ITS), (uint8_t)0x0) | + ifnode(aod::track::tpcNClsFindable > (uint8_t)0, static_cast(o2::aod::track::TPC), (uint8_t)0x0) | + ifnode(aod::track::trdPattern > (uint8_t)0, static_cast(o2::aod::track::TRD), (uint8_t)0x0) | + ifnode((aod::track::tofChi2 >= 0.f) && (aod::track::tofExpMom > 0.f), static_cast(o2::aod::track::TOF), (uint8_t)0x0)); +DECLARE_SOA_DYNAMIC_COLUMN(ITSClusterMap, itsClusterMap, //! ITS cluster map, one bit per a layer, starting from the innermost + [](uint32_t itsClusterSizes) -> uint8_t { + uint8_t clmap = 0; + for (unsigned int layer = 0; layer < 7; layer++) { + if ((itsClusterSizes >> (layer * 4)) & 0xf) { + clmap |= (1 << layer); + } + } + return clmap; + }); +DECLARE_SOA_DYNAMIC_COLUMN(ITSNCls, itsNCls, //! Number of ITS clusters + [](uint8_t itsClusterSizes) -> uint8_t { + uint8_t itsNcls = 0; + for (int layer = 0; layer < 7; layer++) { + if ((itsClusterSizes >> (layer * 4)) & 0xf) + itsNcls++; + } + return itsNcls; + }); +DECLARE_SOA_DYNAMIC_COLUMN(ITSNClsInnerBarrel, itsNClsInnerBarrel, //! Number of ITS clusters in the Inner Barrel + [](uint8_t itsClusterSizes) -> uint8_t { + uint8_t itsNclsInnerBarrel = 0; + for (int layer = 0; layer < 3; layer++) { + if ((itsClusterSizes >> (layer * 4)) & 0xf) + itsNclsInnerBarrel++; + } + return itsNclsInnerBarrel; + }); + +} // namespace v001 + DECLARE_SOA_DYNAMIC_COLUMN(HasITS, hasITS, //! Flag to check if track has a ITS match [](uint8_t detectorMap) -> bool { return detectorMap & o2::aod::track::ITS; }); DECLARE_SOA_DYNAMIC_COLUMN(HasTPC, hasTPC, //! Flag to check if track has a TPC match @@ -282,7 +323,6 @@ DECLARE_SOA_DYNAMIC_COLUMN(ITSNClsInnerBarrel, itsNClsInnerBarrel, //! Number of } return itsNclsInnerBarrel; }); - DECLARE_SOA_DYNAMIC_COLUMN(TPCFoundOverFindableCls, tpcFoundOverFindableCls, //! Ratio of found over findable clusters [](uint8_t tpcNClsFindable, int8_t tpcNClsFindableMinusFound) -> float { int16_t tpcNClsFound = (int16_t)tpcNClsFindable - tpcNClsFindableMinusFound; @@ -392,7 +432,7 @@ DECLARE_SOA_EXTENDED_TABLE(TracksCovIU, StoredTracksCovIU, "TRACKCOV_IU", //! Tr aod::track::C1PtTgl, aod::track::C1Pt21Pt2); -DECLARE_SOA_TABLE_FULL(StoredTracksExtra, "TracksExtra", "AOD", "TRACKEXTRA", //! On disk version of TracksExtra +DECLARE_SOA_TABLE_FULL(StoredTracksExtra_000, "TracksExtra", "AOD", "TRACKEXTRA", //! On disk version of TracksExtra, version 0 track::TPCInnerParam, track::Flags, track::ITSClusterMap, track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, track::TPCNClsShared, track::TRDPattern, track::ITSChi2NCl, @@ -410,8 +450,31 @@ DECLARE_SOA_TABLE_FULL(StoredTracksExtra, "TracksExtra", "AOD", "TRACKEXTRA", // track::TPCFractionSharedCls, track::TrackEtaEMCAL, track::TrackPhiEMCAL, track::TrackTime, track::TrackTimeRes); -DECLARE_SOA_EXTENDED_TABLE(TracksExtra, StoredTracksExtra, "TRACKEXTRA", //! Additional track information (clusters, PID, etc.) +DECLARE_SOA_TABLE_FULL_VERSIONED(StoredTracksExtra_001, "TracksExtra", "AOD", "TRACKEXTRA", 1, // On disk version of TracksExtra, version 1 + track::TPCInnerParam, track::Flags, track::ITSClusterSizes, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusCrossedRows, + track::TPCNClsShared, track::TRDPattern, track::ITSChi2NCl, + track::TPCChi2NCl, track::TRDChi2, track::TOFChi2, + track::TPCSignal, track::TRDSignal, track::Length, track::TOFExpMom, + track::PIDForTracking, + track::IsPVContributor, + track::HasITS, track::HasTPC, + track::HasTRD, track::HasTOF, + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::TrackEtaEMCAL, track::TrackPhiEMCAL, track::TrackTime, track::TrackTimeRes); + +DECLARE_SOA_EXTENDED_TABLE(TracksExtra_000, StoredTracksExtra_000, "TRACKEXTRA", //! Additional track information (clusters, PID, etc.) track::DetectorMap); +DECLARE_SOA_EXTENDED_TABLE(TracksExtra_001, StoredTracksExtra_001, "TRACKEXTRA", //! Additional track information (clusters, PID, etc.) + track::v001::DetectorMap); + +using StoredTracksExtra = StoredTracksExtra_000; +using TracksExtra = TracksExtra_000; using Track = Tracks::iterator; using TrackIU = TracksIU::iterator; @@ -458,11 +521,11 @@ DECLARE_SOA_COLUMN(TrackTimeRes, trackTimeRes, float); DECLARE_SOA_DYNAMIC_COLUMN(Sign, sign, //! Sign of the track eletric charge [](float signed1Pt) -> short { return (signed1Pt > 0) ? 1 : -1; }); DECLARE_SOA_EXPRESSION_COLUMN(Eta, eta, float, //! - -1.f * nlog(ntan(PIQuarter - 0.5f * natan(aod::fwdtrack::tgl)))); + -1.f * nlog(ntan(o2::constants::math::PIQuarter - 0.5f * natan(aod::fwdtrack::tgl)))); DECLARE_SOA_EXPRESSION_COLUMN(Pt, pt, float, //! ifnode(nabs(aod::fwdtrack::signed1Pt) < o2::constants::math::Almost0, o2::constants::math::VeryBig, nabs(1.f / aod::fwdtrack::signed1Pt))); DECLARE_SOA_EXPRESSION_COLUMN(P, p, float, //! - ifnode((nabs(aod::fwdtrack::signed1Pt) < o2::constants::math::Almost0) || (nabs(PIQuarter - 0.5f * natan(aod::fwdtrack::tgl)) < o2::constants::math::Almost0), o2::constants::math::VeryBig, 0.5f * (ntan(PIQuarter - 0.5f * natan(aod::fwdtrack::tgl)) + 1.f / ntan(PIQuarter - 0.5f * natan(aod::fwdtrack::tgl))) / nabs(aod::fwdtrack::signed1Pt))); + ifnode((nabs(aod::fwdtrack::signed1Pt) < o2::constants::math::Almost0) || (nabs(o2::constants::math::PIQuarter - 0.5f * natan(aod::fwdtrack::tgl)) < o2::constants::math::Almost0), o2::constants::math::VeryBig, 0.5f * (ntan(o2::constants::math::PIQuarter - 0.5f * natan(aod::fwdtrack::tgl)) + 1.f / ntan(o2::constants::math::PIQuarter - 0.5f * natan(aod::fwdtrack::tgl))) / nabs(aod::fwdtrack::signed1Pt))); DECLARE_SOA_DYNAMIC_COLUMN(Px, px, //! [](float pt, float phi) -> float { return pt * std::cos(phi); @@ -1300,7 +1363,7 @@ using Run2BCInfo = Run2BCInfos::iterator; // ---- MC tables ---- namespace mccollision { -DECLARE_SOA_INDEX_COLUMN(BC, bc); //! BC index +DECLARE_SOA_INDEX_COLUMN(BC, bc); //! BC index DECLARE_SOA_COLUMN(GeneratorsID, generatorsID, short); //! disentangled generator IDs should be accessed from dynamic columns using getGenId, getCocktailId and getSourceId DECLARE_SOA_COLUMN(PosX, posX, float); //! X vertex position in cm DECLARE_SOA_COLUMN(PosY, posY, float); //! Y vertex position in cm @@ -1360,7 +1423,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(IsPhysicalPrimary, isPhysicalPrimary, //! True if par [](uint8_t flags) -> bool { return (flags & o2::aod::mcparticle::enums::PhysicalPrimary) == o2::aod::mcparticle::enums::PhysicalPrimary; }); DECLARE_SOA_EXPRESSION_COLUMN(Phi, phi, float, //! Phi in the range [0, 2pi) - PI + natan2(-1.0f * aod::mcparticle::py, -1.0f * aod::mcparticle::px)); + o2::constants::math::PI + natan2(-1.0f * aod::mcparticle::py, -1.0f * aod::mcparticle::px)); DECLARE_SOA_EXPRESSION_COLUMN(Eta, eta, float, //! Pseudorapidity, conditionally defined to avoid FPEs ifnode((nsqrt(aod::mcparticle::px * aod::mcparticle::px + aod::mcparticle::py * aod::mcparticle::py + @@ -1439,8 +1502,11 @@ namespace soa DECLARE_EQUIVALENT_FOR_INDEX(aod::Collisions_000, aod::Collisions_001); DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredMcParticles_000, aod::StoredMcParticles_001); DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracks, aod::StoredTracksIU); -DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracks, aod::StoredTracksExtra); -DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracksIU, aod::StoredTracksExtra); +DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracks, aod::StoredTracksExtra_000); +DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracksIU, aod::StoredTracksExtra_000); +DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracks, aod::StoredTracksExtra_001); +DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracksIU, aod::StoredTracksExtra_001); +DECLARE_EQUIVALENT_FOR_INDEX(aod::StoredTracksExtra_000, aod::StoredTracksExtra_001); DECLARE_EQUIVALENT_FOR_INDEX(aod::HMPID_000, aod::HMPID_001); } // namespace soa @@ -1511,12 +1577,14 @@ DECLARE_SOA_COLUMN(Attempted, attempted, uint64_t); //! The number of events DECLARE_SOA_COLUMN(XsectGen, xsectGen, float); //! Cross section in pb DECLARE_SOA_COLUMN(XsectErr, xsectErr, float); //! Error associated with this cross section DECLARE_SOA_COLUMN(PtHard, ptHard, float); //! PT-hard (event scale, for pp collisions) +DECLARE_SOA_COLUMN(NMPI, nMPI, int); //! number of MPIs (for pp collisions) +DECLARE_SOA_COLUMN(ProcessId, processId, int); //! process id from MC generator } // namespace hepmcxsection DECLARE_SOA_TABLE(HepMCXSections, "AOD", "HEPMCXSECTION", //! HepMC table for cross sections o2::soa::Index<>, hepmcxsection::McCollisionId, hepmcxsection::GeneratorsID, hepmcxsection::Accepted, hepmcxsection::Attempted, hepmcxsection::XsectGen, - hepmcxsection::XsectErr, hepmcxsection::PtHard); + hepmcxsection::XsectErr, hepmcxsection::PtHard, hepmcxsection::NMPI, hepmcxsection::ProcessId); using HepMCXSection = HepMCXSections::iterator; namespace hepmcpdfinfo diff --git a/Framework/Core/include/Framework/CallbackService.h b/Framework/Core/include/Framework/CallbackService.h index 91115e92af129..6243b15795fd3 100644 --- a/Framework/Core/include/Framework/CallbackService.h +++ b/Framework/Core/include/Framework/CallbackService.h @@ -71,6 +71,9 @@ class CallbackService NewTimeslice, /// Invoked before the processing callback PreProcessing, + /// Invoked after the processing callback and before the post processing + /// callback to allow for injecting data in the output stream. + FinaliseOutputs, /// Invoked after the processing callback, PostProcessing, /// Invoked whenever an object from CCDB is deserialised via ROOT. @@ -94,6 +97,7 @@ class CallbackService using RegionInfoCallback = std::function; using NewTimesliceCallback = std::function; using PreProcessingCallback = std::function; + using FinaliseOutputsCallback = std::function; using PostProcessingCallback = std::function; using CCDBDeserializedCallback = std::function; using DomainInfoUpdatedCallback = std::function; @@ -111,6 +115,7 @@ class CallbackService RegistryPair, // RegistryPair, // RegistryPair, // + RegistryPair, // RegistryPair, // RegistryPair, // RegistryPair, // diff --git a/Framework/Core/include/Framework/DataAllocator.h b/Framework/Core/include/Framework/DataAllocator.h index 2cc912c4ba534..f80d6d0836e28 100644 --- a/Framework/Core/include/Framework/DataAllocator.h +++ b/Framework/Core/include/Framework/DataAllocator.h @@ -58,13 +58,12 @@ namespace o2::framework { struct ServiceRegistry; -#define ERROR_STRING \ - "data type T not supported by API, " \ - "\n specializations available for" \ - "\n - trivially copyable, non-polymorphic structures" \ - "\n - arrays of those" \ - "\n - TObject with additional constructor arguments" \ - "\n - Classes and structs with boost serialization support" \ +#define ERROR_STRING \ + "data type T not supported by API, " \ + "\n specializations available for" \ + "\n - trivially copyable, non-polymorphic structures" \ + "\n - arrays of those" \ + "\n - TObject with additional constructor arguments" \ "\n - std containers of those" /// Helper to allow framework managed objecs to have a callback @@ -174,12 +173,13 @@ class DataAllocator template decltype(auto) make(const Output& spec, Args... args) { + auto& timingInfo = mRegistry.get(); + auto routeIndex = matchDataHeader(spec, timingInfo.timeslice); + auto& context = mRegistry.get(); + if constexpr (is_specialization_v) { // plain buffer as polymorphic spectator std::vector, which does not run constructors / destructors using ValueType = typename T::value_type; - auto& timingInfo = mRegistry.get(); - auto routeIndex = matchDataHeader(spec, timingInfo.timeslice); - auto& context = mRegistry.get(); // Note: initial payload size is 0 and will be set by the context before sending fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodNone, 0); @@ -190,9 +190,6 @@ class DataAllocator // this catches all std::vector objects with messageable value type before checking if is also // has a root dictionary, so non-serialized transmission is preferred using ValueType = typename T::value_type; - auto& timingInfo = mRegistry.get(); - auto routeIndex = matchDataHeader(spec, timingInfo.timeslice); - auto& context = mRegistry.get(); // Note: initial payload size is 0 and will be set by the context before sending fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodNone, 0); @@ -200,9 +197,6 @@ class DataAllocator } else if constexpr (has_root_dictionary::value == true && is_messageable::value == false) { // Extended support for types implementing the Root ClassDef interface, both TObject // derived types and others - auto& timingInfo = mRegistry.get(); - auto routeIndex = matchDataHeader(spec, timingInfo.timeslice); - auto& context = mRegistry.get(); if constexpr (enable_root_serialization::value) { fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodROOT, 0); @@ -239,9 +233,6 @@ class DataAllocator if constexpr (is_messageable::value == true) { auto [nElements] = std::make_tuple(args...); auto size = nElements * sizeof(T); - auto& timingInfo = mRegistry.get(); - auto routeIndex = matchDataHeader(spec, timingInfo.timeslice); - auto& context = mRegistry.get(); fair::mq::MessagePtr headerMessage = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodNone, size); return context.add>(std::move(headerMessage), routeIndex, 0, nElements).get(); @@ -261,14 +252,6 @@ class DataAllocator } } - template - T& make_boost(const Output& spec) - { - auto buff = new T{}; - adopt_boost(spec, buff); - return *buff; - } - /// Adopt a string in the framework and serialize / send /// it to the consumers of @a spec once done. void @@ -355,6 +338,21 @@ class DataAllocator "\n - pointers to those" "\n - types with ROOT dictionary and implementing ROOT ClassDef interface"); } + } else if constexpr (is_container::value == true && has_messageable_value_type::value == true) { + // Serialize a snapshot of a std::container of trivially copyable, non-polymorphic elements + // Note: in most cases it is better to use the `make` function und work with the provided + // reference object + constexpr auto elementSizeInBytes = sizeof(typename T::value_type); + auto sizeInBytes = elementSizeInBytes * object.size(); + payloadMessage = proxy.createOutputMessage(routeIndex, sizeInBytes); + + // serialize vector of pointers to elements + auto target = reinterpret_cast(payloadMessage->GetData()); + for (auto const& entry : object) { + memcpy(target, (void*)&entry, elementSizeInBytes); + target += elementSizeInBytes; + } + serializationType = o2::header::gSerializationMethodNone; } else if constexpr (has_root_dictionary::value == true || is_specialization_v == true) { // Serialize a snapshot of an object with root dictionary payloadMessage = proxy.createOutputMessage(routeIndex); diff --git a/Framework/Core/include/Framework/DataProcessingContext.h b/Framework/Core/include/Framework/DataProcessingContext.h index 2f7308a0ce810..d71ad203b1580 100644 --- a/Framework/Core/include/Framework/DataProcessingContext.h +++ b/Framework/Core/include/Framework/DataProcessingContext.h @@ -53,6 +53,9 @@ struct DataProcessorContext { void preStartCallbacks(ServiceRegistryRef); /// Invoke callbacks to be executed before every process method invokation void preProcessingCallbacks(ProcessingContext&); + /// Invoke callbacks to be executed after the outputs have been created + /// by the processing, but before the post processing callbacks. + void finaliseOutputsCallbacks(ProcessingContext&); /// Invoke callbacks to be executed after every process method invokation void postProcessingCallbacks(ProcessingContext&); /// Invoke callbacks to be executed before every dangling check @@ -91,6 +94,9 @@ struct DataProcessorContext { mutable std::vector preProcessingHandlers; /// Callback for services to be executed after every processing. /// The callback MUST BE REENTRANT and threadsafe. + mutable std::vector finaliseOutputsHandles; + /// Callback for services to be executed after every processing. + /// The callback MUST BE REENTRANT and threadsafe. mutable std::vector postProcessingHandlers; /// Callbacks for services to be executed before every dangling check mutable std::vector preDanglingHandles; diff --git a/Framework/Core/include/Framework/DataSpecUtils.h b/Framework/Core/include/Framework/DataSpecUtils.h index 42cdc42841157..3dcdf6d790cd9 100644 --- a/Framework/Core/include/Framework/DataSpecUtils.h +++ b/Framework/Core/include/Framework/DataSpecUtils.h @@ -230,6 +230,9 @@ struct DataSpecUtils { /// Updates list of InputSpecs by merging metadata static void updateInputList(std::vector& list, InputSpec&& input); + + /// Updates list of OutputSpecs by merging metadata (or adding output). + static void updateOutputList(std::vector& list, OutputSpec&& input); }; } // namespace framework diff --git a/Framework/Core/include/Framework/ExpirationHandler.h b/Framework/Core/include/Framework/ExpirationHandler.h index e4c6c8bff53e9..5c1b19331bfaf 100644 --- a/Framework/Core/include/Framework/ExpirationHandler.h +++ b/Framework/Core/include/Framework/ExpirationHandler.h @@ -30,7 +30,7 @@ struct TimesliceSlot; struct InputRecord; struct ExpirationHandler { - using Creator = std::function; + using Creator = std::function; /// Callback type to check if the record must be expired using Checker = std::function; /// Callback type to actually materialise a given record diff --git a/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h b/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h index 916d1738bf5d0..3ff77ae703b9b 100644 --- a/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h +++ b/Framework/Core/include/Framework/ExternalFairMQDeviceProxy.h @@ -23,7 +23,16 @@ namespace o2::framework /// A callback function to retrieve the fair::mq::Channel name to be used for sending /// messages of the specified OutputSpec using ChannelRetriever = std::function; -using InjectorFunction = std::function; +/// The callback which actually does the heavy lifting of converting the input data into +/// DPL messages. The callback is invoked with the following parameters: +/// @param timingInfo is the timing information of the current timeslice +/// @param services is the service registry +/// @param inputs is the list of input messages +/// @param channelRetriever is a callback to retrieve the fair::mq::Channel name to be used for +/// sending the messages +/// @param newTimesliceId is the timeslice ID of the current timeslice +/// @return true if any message were sent, false otherwise +using InjectorFunction = std::function; using ChannelSelector = std::function>& channels)>; struct InputChannelSpec; @@ -105,7 +114,9 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* label, const char* defaultChannelConfig, InjectorFunction converter, uint64_t minSHM = 0, - bool sendTFcounter = false); + bool sendTFcounter = false, + bool doInjectMissingData = false, + unsigned int doPrintSizes = 0); DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* label, Inputs const& inputSpecs, diff --git a/Framework/Core/include/Framework/MessageContext.h b/Framework/Core/include/Framework/MessageContext.h index 6d027da1ab5a7..4f6674576e8ef 100644 --- a/Framework/Core/include/Framework/MessageContext.h +++ b/Framework/Core/include/Framework/MessageContext.h @@ -528,6 +528,7 @@ class MessageContext o2::header::DataHeader* findMessageHeader(const Output& spec); o2::header::Stack* findMessageHeaderStack(const Output& spec); int countDeviceOutputs(bool excludeDPLOrigin = false); + void fakeDispatch() { mDidDispatch = true; } o2::framework::DataProcessingHeader* findMessageDataProcessingHeader(const Output& spec); std::pair findMessageHeaders(const Output& spec); diff --git a/Framework/Core/include/Framework/ReadoutAdapter.h b/Framework/Core/include/Framework/ReadoutAdapter.h deleted file mode 100644 index 873ba9cf807d2..0000000000000 --- a/Framework/Core/include/Framework/ReadoutAdapter.h +++ /dev/null @@ -1,31 +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. - -#ifndef o2_framework_ReadoutAdapter_H_DEFINED -#define o2_framework_ReadoutAdapter_H_DEFINED - -#include "ExternalFairMQDeviceProxy.h" - -namespace o2 -{ -namespace framework -{ - -/// An adapter function for data sent by Readout. It gets the raw pages as -/// provided by Readout and wraps them in a multipart message where the -/// DataHeader is specified by the OutputSpec, while the DataProcessing header -/// is an enumeration of the pages received. -InjectorFunction readoutAdapter(OutputSpec const& spec); - -} // namespace framework -} // namespace o2 - -#endif // o2_framework_ReadoutAdapter_H_DEFINED diff --git a/Framework/Core/include/Framework/ServiceSpec.h b/Framework/Core/include/Framework/ServiceSpec.h index d30280a2e2f85..910620fcd0655 100644 --- a/Framework/Core/include/Framework/ServiceSpec.h +++ b/Framework/Core/include/Framework/ServiceSpec.h @@ -146,6 +146,9 @@ struct ServiceSpec { ServiceConfigureCallback configure = nullptr; /// Callback executed before actual processing happens. ServiceProcessingCallback preProcessing = nullptr; + /// Callback executed after the processing callback is completed + /// and the user provided outputs have been created. + ServiceProcessingCallback finaliseOutputs = nullptr; /// Callback executed once actual processing happened. ServiceProcessingCallback postProcessing = nullptr; /// Callback executed before the dangling inputs loop @@ -170,7 +173,7 @@ struct ServiceSpec { ServicePreSchedule preSchedule = nullptr; ServicePostSchedule postSchedule = nullptr; - ///Callback executed after each metric is received by the driver. + /// Callback executed after each metric is received by the driver. ServiceMetricHandling metricHandling = nullptr; /// Callback executed after a given input record has been successfully diff --git a/Framework/Core/include/Framework/StreamContext.h b/Framework/Core/include/Framework/StreamContext.h index 5e4dc5f1b325c..0ab353e147276 100644 --- a/Framework/Core/include/Framework/StreamContext.h +++ b/Framework/Core/include/Framework/StreamContext.h @@ -38,6 +38,9 @@ struct StreamContext { void preStartStreamCallbacks(ServiceRegistryRef); void preProcessingCallbacks(ProcessingContext& pcx); + /// Invoke callbacks to be executed after the outputs have been created + /// by the processing, but before the post processing callbacks. + void finaliseOutputsCallbacks(ProcessingContext&); void postProcessingCallbacks(ProcessingContext& pcx); /// Invoke callbacks to be executed before every EOS user callback invokation @@ -47,6 +50,7 @@ struct StreamContext { /// Callbacks for services to be executed before every process method invokation std::vector preProcessingHandles; + std::vector finaliseOutputsHandles; /// Callbacks for services to be executed after every process method invokation std::vector postProcessingHandles; diff --git a/Framework/Core/include/Framework/Variant.h b/Framework/Core/include/Framework/Variant.h index f0664bcdc3bd4..0c055cfb84b12 100644 --- a/Framework/Core/include/Framework/Variant.h +++ b/Framework/Core/include/Framework/Variant.h @@ -50,6 +50,7 @@ enum class VariantType : int { Int = 0, UInt64, Int8, Int16, + LabeledArrayString, Empty, Dict, Unknown }; @@ -77,7 +78,8 @@ constexpr auto isLabeledArray() { return (V == VariantType::LabeledArrayInt || V == VariantType::LabeledArrayFloat || - V == VariantType::LabeledArrayDouble); + V == VariantType::LabeledArrayDouble || + V == VariantType::LabeledArrayString); } template @@ -146,6 +148,7 @@ DECLARE_VARIANT_TRAIT(Array2D, Array2DDouble); DECLARE_VARIANT_TRAIT(LabeledArray, LabeledArrayInt); DECLARE_VARIANT_TRAIT(LabeledArray, LabeledArrayFloat); DECLARE_VARIANT_TRAIT(LabeledArray, LabeledArrayDouble); +DECLARE_VARIANT_TRAIT(LabeledArray, LabeledArrayString); template struct variant_array_symbol { @@ -216,6 +219,7 @@ DECLARE_VARIANT_TYPE(Array2D, Array2DDouble); DECLARE_VARIANT_TYPE(LabeledArray, LabeledArrayInt); DECLARE_VARIANT_TYPE(LabeledArray, LabeledArrayFloat); DECLARE_VARIANT_TYPE(LabeledArray, LabeledArrayDouble); +DECLARE_VARIANT_TYPE(LabeledArray, LabeledArrayString); template struct variant_array_element_type { @@ -239,6 +243,7 @@ DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(std::string, ArrayString); DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(int, LabeledArrayInt); DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(float, LabeledArrayFloat); DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(double, LabeledArrayDouble); +DECLARE_VARIANT_ARRAY_ELEMENT_TYPE(std::string, LabeledArrayString); template using variant_array_element_type_t = typename variant_array_element_type::type; diff --git a/Framework/Core/include/Framework/VariantJSONHelpers.h b/Framework/Core/include/Framework/VariantJSONHelpers.h index a09192d9954e7..ce998275d26b3 100644 --- a/Framework/Core/include/Framework/VariantJSONHelpers.h +++ b/Framework/Core/include/Framework/VariantJSONHelpers.h @@ -437,6 +437,9 @@ struct VariantJSONHelpers { case VariantType::LabeledArrayDouble: writeVariant(o, v); break; + case VariantType::LabeledArrayString: + writeVariant(o, v); + break; case VariantType::Dict: writeVariant(o, v); default: diff --git a/Framework/Core/src/AODReaderHelpers.cxx b/Framework/Core/src/AODReaderHelpers.cxx index e8e9700a8f519..5f65f42e13427 100644 --- a/Framework/Core/src/AODReaderHelpers.cxx +++ b/Framework/Core/src/AODReaderHelpers.cxx @@ -147,7 +147,7 @@ AlgorithmSpec AODReaderHelpers::indexBuilderCallback(std::vector& req AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(std::vector& requested) { - return AlgorithmSpec::InitCallback{[requested](InitContext& ic) { + return AlgorithmSpec::InitCallback{[requested](InitContext& /*ic*/) { return [requested](ProcessingContext& pc) { auto outputs = pc.outputs(); // spawn tables @@ -176,7 +176,11 @@ AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(std::vector& reque } else if (description == header::DataDescription{"TRACKCOV_IU"}) { outputs.adopt(Output{origin, description, version}, maker(o2::aod::TracksCovIUExtensionMetadata{})); } else if (description == header::DataDescription{"TRACKEXTRA"}) { - outputs.adopt(Output{origin, description, version}, maker(o2::aod::TracksExtraExtensionMetadata{})); + if (version == 0U) { + outputs.adopt(Output{origin, description, version}, maker(o2::aod::TracksExtra_000ExtensionMetadata{})); + } else if (version == 1U) { + outputs.adopt(Output{origin, description, version}, maker(o2::aod::TracksExtra_001ExtensionMetadata{})); + } } else if (description == header::DataDescription{"MFTTRACK"}) { outputs.adopt(Output{origin, description, version}, maker(o2::aod::MFTTracksExtensionMetadata{})); } else if (description == header::DataDescription{"FWDTRACK"}) { @@ -184,9 +188,11 @@ AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(std::vector& reque } else if (description == header::DataDescription{"FWDTRACKCOV"}) { outputs.adopt(Output{origin, description, version}, maker(o2::aod::FwdTracksCovExtensionMetadata{})); } else if (description == header::DataDescription{"MCPARTICLE"}) { - outputs.adopt(Output{origin, description, version}, maker(o2::aod::McParticles_000ExtensionMetadata{})); - } else if (description == header::DataDescription{"MCPARTICLE_001"}) { - outputs.adopt(Output{origin, description, version}, maker(o2::aod::McParticles_001ExtensionMetadata{})); + if (version == 0U) { + outputs.adopt(Output{origin, description, version}, maker(o2::aod::McParticles_000ExtensionMetadata{})); + } else if (version == 1U) { + outputs.adopt(Output{origin, description, version}, maker(o2::aod::McParticles_001ExtensionMetadata{})); + } } else { throw runtime_error("Not an extended table"); } diff --git a/Framework/Core/src/BoostOptionsRetriever.cxx b/Framework/Core/src/BoostOptionsRetriever.cxx index 5f9381da9f0b3..fbf56a312cf66 100644 --- a/Framework/Core/src/BoostOptionsRetriever.cxx +++ b/Framework/Core/src/BoostOptionsRetriever.cxx @@ -97,6 +97,7 @@ void BoostOptionsRetriever::update(std::vector const& specs, case VariantType::LabeledArrayInt: case VariantType::LabeledArrayFloat: case VariantType::LabeledArrayDouble: + case VariantType::LabeledArrayString: case VariantType::Unknown: case VariantType::Empty: break; diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index d6e62ce10e16d..460702d654213 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -399,19 +399,15 @@ o2::framework::ServiceSpec CommonServices::ccdbSupportSpec() return ServiceHandle{.hash = TypeIdHelpers::uniqueId(), .instance = nullptr, .kind = ServiceKind::Serial}; }, .configure = noConfiguration(), - .postProcessing = [](ProcessingContext& pc, void* service) { + .finaliseOutputs = [](ProcessingContext& pc, void* service) { if (!service) { return; } - if (pc.services().get().streaming == StreamingState::EndOfStreaming) { - if (pc.outputs().countDeviceOutputs(true) == 0) { - LOGP(debug, "We are in EoS w/o outputs, do not automatically add DISTSUBTIMEFRAME to outgoing messages"); - return; - } + if (pc.outputs().countDeviceOutputs(true) == 0) { + LOGP(debug, "We are w/o outputs, do not automatically add DISTSUBTIMEFRAME to outgoing messages"); + return; } - const auto ref = pc.inputs().getFirstValid(true); - const auto* dh = DataRefUtils::getHeader(ref); - const auto* dph = DataRefUtils::getHeader(ref); + auto& timingInfo = pc.services().get(); // For any output that is a FLP/DISTSUBTIMEFRAME with subspec != 0, // we create a new message. @@ -426,9 +422,9 @@ o2::framework::ServiceSpec CommonServices::ccdbSupportSpec() continue; } auto& stfDist = pc.outputs().make(Output{concrete.origin, concrete.description, concrete.subSpec, output.matcher.lifetime}); - stfDist.id = dph->startTime; - stfDist.firstOrbit = dh->firstTForbit; - stfDist.runNumber = dh->runNumber; + stfDist.id = timingInfo.timeslice; + stfDist.firstOrbit = timingInfo.firstTForbit; + stfDist.runNumber = timingInfo.runNumber; } } }, .kind = ServiceKind::Global}; @@ -466,6 +462,11 @@ o2::framework::ServiceSpec CommonServices::decongestionSpec() timesliceIndex.updateOldestPossibleOutput(); auto& proxy = ctx.services().get(); auto oldestPossibleOutput = relayer.getOldestPossibleOutput(); + if (decongestion->nextEnumerationTimesliceRewinded && decongestion->nextEnumerationTimeslice < oldestPossibleOutput.timeslice.value) { + LOGP(detail, "Not sending oldestPossible if nextEnumerationTimeslice was rewinded"); + return; + } + if (decongestion->lastTimeslice && oldestPossibleOutput.timeslice.value == decongestion->lastTimeslice) { LOGP(debug, "Not sending already sent value"); return; diff --git a/Framework/Core/src/ConfigParamsHelper.cxx b/Framework/Core/src/ConfigParamsHelper.cxx index 2f0c75c131f48..a7b32c86e3eca 100644 --- a/Framework/Core/src/ConfigParamsHelper.cxx +++ b/Framework/Core/src/ConfigParamsHelper.cxx @@ -101,6 +101,7 @@ void ConfigParamsHelper::populateBoostProgramOptions( case VariantType::LabeledArrayInt: case VariantType::LabeledArrayFloat: case VariantType::LabeledArrayDouble: + case VariantType::LabeledArrayString: // FIXME: for Dict we should probably allow parsing stuff // provided on the command line case VariantType::Dict: diff --git a/Framework/Core/src/ContextHelpers.h b/Framework/Core/src/ContextHelpers.h index cb7af43d5006e..12ccfcca83fe1 100644 --- a/Framework/Core/src/ContextHelpers.h +++ b/Framework/Core/src/ContextHelpers.h @@ -41,6 +41,9 @@ void ContextHelpers::bindStreamService(DataProcessorContext& dpContext, StreamCo if (spec.preProcessing) { context.preProcessingHandles.push_back(ServiceProcessingHandle{spec, spec.preProcessing, service}); } + if (spec.finaliseOutputs) { + context.finaliseOutputsHandles.push_back(ServiceProcessingHandle{spec, spec.finaliseOutputs, service}); + } if (spec.postProcessing) { context.postProcessingHandles.push_back(ServiceProcessingHandle{spec, spec.postProcessing, service}); } @@ -59,6 +62,9 @@ void ContextHelpers::bindProcessorService(DataProcessorContext& dataProcessorCon if (spec.preProcessing) { dataProcessorContext.preProcessingHandlers.push_back(ServiceProcessingHandle{spec, spec.preProcessing, service}); } + if (spec.finaliseOutputs) { + dataProcessorContext.finaliseOutputsHandles.push_back(ServiceProcessingHandle{spec, spec.finaliseOutputs, service}); + } if (spec.postProcessing) { dataProcessorContext.postProcessingHandlers.push_back(ServiceProcessingHandle{spec, spec.postProcessing, service}); } diff --git a/Framework/Core/src/DDSConfigHelpers.cxx b/Framework/Core/src/DDSConfigHelpers.cxx index 272a5cf36ea03..e56afe34bc511 100644 --- a/Framework/Core/src/DDSConfigHelpers.cxx +++ b/Framework/Core/src/DDSConfigHelpers.cxx @@ -202,7 +202,7 @@ void DDSConfigHelpers::dumpDeviceSpec2DDS(std::ostream& out, << R"()"; out << fmt::format("cat ${{DDS_LOCATION}}/dpl_json{}.asset | ", workflowSuffix); for (auto ei : execution.environ) { - out << DeviceSpecHelpers::reworkEnv(ei, spec) << " "; + out << DeviceSpecHelpers::reworkTimeslicePlaceholder(ei, spec) << " "; } std::string accumulatedChannelPrefix; char* s = strdup(execution.args[0]); diff --git a/Framework/Core/src/DataDescriptorQueryBuilder.cxx b/Framework/Core/src/DataDescriptorQueryBuilder.cxx index cb4ad54b23b95..f79698ea2a08d 100644 --- a/Framework/Core/src/DataDescriptorQueryBuilder.cxx +++ b/Framework/Core/src/DataDescriptorQueryBuilder.cxx @@ -316,19 +316,43 @@ std::vector DataDescriptorQueryBuilder::parse(char const* config) if (*currentKey == "lifetime" && currentValue == "condition") { currentLifetime = Lifetime::Condition; } - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); + if (*currentKey == "ccdb-run-dependent" && (currentValue != "false" && currentValue != "0")) { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, true, {}}); + } else if (*currentKey == "ccdb-run-dependent" && (currentValue == "false" || currentValue == "0")) { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, false, {}}); + } else if (*currentKey == "ccdb-run-dependent") { + error("ccdb-run-dependent can only be true or false"); + } else { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); + } } else if (*next == ';') { assignLastStringMatch("value", 1000, currentValue, IN_END_ATTRIBUTES); if (*currentKey == "lifetime" && currentValue == "condition") { currentLifetime = Lifetime::Condition; } - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); + if (*currentKey == "ccdb-run-dependent" && (currentValue != "false" && currentValue != "0")) { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, true, {}}); + } else if (*currentKey == "ccdb-run-dependent" && (currentValue == "false" || currentValue == "0")) { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, false, {}}); + } else if (*currentKey == "ccdb-run-dependent") { + error("ccdb-run-dependent can only be true or false"); + } else { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); + } } else if (*next == '\0') { assignLastStringMatch("value", 1000, currentValue, IN_END_ATTRIBUTES); if (*currentKey == "lifetime" && currentValue == "condition") { currentLifetime = Lifetime::Condition; } - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); + if (*currentKey == "ccdb-run-dependent" && (currentValue != "false" && currentValue != "0")) { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, true, {}}); + } else if (*currentKey == "ccdb-run-dependent" && (currentValue == "false" || currentValue == "0")) { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, false, {}}); + } else if (*currentKey == "ccdb-run-dependent") { + error("ccdb-run-dependent can only be true or false"); + } else { + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); + } } else { error("missing value for string value"); } diff --git a/Framework/Core/src/DataProcessingContext.cxx b/Framework/Core/src/DataProcessingContext.cxx index 281a92134b5f6..365975c706722 100644 --- a/Framework/Core/src/DataProcessingContext.cxx +++ b/Framework/Core/src/DataProcessingContext.cxx @@ -22,6 +22,14 @@ void DataProcessorContext::preProcessingCallbacks(ProcessingContext& ctx) } } +void DataProcessorContext::finaliseOutputsCallbacks(ProcessingContext& ctx) +{ + for (auto& handle : finaliseOutputsHandles) { + LOGP(debug, "Invoking postProcessingCallback for service {}", handle.spec.name); + handle.callback(ctx, handle.service); + } +} + /// Invoke callbacks to be executed before every dangling check void DataProcessorContext::postProcessingCallbacks(ProcessingContext& ctx) { diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index a5b492d24fba2..23bdc9e75806b 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -172,7 +172,8 @@ DataProcessingDevice::DataProcessingDevice(RunningDeviceRef running, ServiceRegi } }; - this->SubscribeToStateChange("dpl", stateWatcher); + // 99 is to execute DPL callbacks last + this->SubscribeToStateChange("99-dpl", stateWatcher); // One task for now. mStreams.resize(1); @@ -956,9 +957,12 @@ void DataProcessingDevice::InitTask() LOG(detail) << "ptr: " << info.ptr; LOG(detail) << "size: " << info.size; LOG(detail) << "flags: " << info.flags; - context.expectedRegionCallbacks -= 1; + // Now we check for pending events with the mutex, + // so the lines below are atomic. pendingRegionInfos.push_back(info); - // We always want to handle these on the main loop + context.expectedRegionCallbacks -= 1; + // We always want to handle these on the main loop, + // so we awake it. ServiceRegistryRef ref{registry}; uv_async_send(ref.get().awakeMainThread); }); @@ -990,11 +994,17 @@ void DataProcessingDevice::InitTask() // We will get there. this->fillContext(mServiceRegistry.get(ServiceRegistry::globalDeviceSalt()), deviceContext); + auto hasPendingEvents = [&mutex = mRegionInfoMutex, &pendingRegionInfos = mPendingRegionInfos](DeviceContext& deviceContext) { + std::lock_guard lock(mutex); + return (pendingRegionInfos.empty() == false) || deviceContext.expectedRegionCallbacks > 0; + }; /// We now run an event loop also in InitTask. This is needed to: /// * Make sure region registration callbacks are invoked /// on the main thread. /// * Wait for enough callbacks to be delivered before moving to START - while (deviceContext.expectedRegionCallbacks > 0 && uv_run(state.loop, UV_RUN_ONCE)) { + while (hasPendingEvents(deviceContext)) { + // Wait for the callback to signal its done, so that we do not busy wait. + uv_run(state.loop, UV_RUN_ONCE); // Handle callbacks if any { std::lock_guard lock(mRegionInfoMutex); @@ -1501,6 +1511,10 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) if (info.channel == nullptr) { continue; } + // Only poll DPL channels for now. + if (info.channelType != ChannelAccountingType::DPL) { + continue; + } auto& socket = info.channel->GetSocket(); // If we have pending events from a previous iteration, // we do receive in any case. @@ -2290,6 +2304,15 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v allocator.make(OutputRef{"dpl-summary", compile_time_hash(spec.name.c_str())}, 1); } + // Extra callback which allows a service to add extra outputs. + // This is needed e.g. to ensure that injected CCDB outputs are added + // before an end of stream. + { + ref.get().call(o2::framework::ServiceRegistryRef{ref}, (int)action.op); + dpContext.finaliseOutputsCallbacks(processContext); + streamContext.finaliseOutputsCallbacks(processContext); + } + { ZoneScopedN("service post processing"); ref.get().call(o2::framework::ServiceRegistryRef{ref}, (int)action.op); diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index cc2ec51508044..7dc6d28a1a11f 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -125,7 +125,7 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector& list, InputSpec&& in } } +void DataSpecUtils::updateOutputList(std::vector& list, OutputSpec&& spec) +{ + auto locate = std::find(list.begin(), list.end(), spec); + if (locate != list.end()) { + // amend entry + auto& entryMetadata = locate->metadata; + entryMetadata.insert(entryMetadata.end(), spec.metadata.begin(), spec.metadata.end()); + std::sort(entryMetadata.begin(), entryMetadata.end(), [](ConfigParamSpec const& a, ConfigParamSpec const& b) { return a.name < b.name; }); + auto new_end = std::unique(entryMetadata.begin(), entryMetadata.end(), [](ConfigParamSpec const& a, ConfigParamSpec const& b) { return a.name == b.name; }); + entryMetadata.erase(new_end, entryMetadata.end()); + } else { + // add entry + list.emplace_back(std::move(spec)); + } +} + } // namespace o2::framework diff --git a/Framework/Core/src/DecongestionService.h b/Framework/Core/src/DecongestionService.h index 65ec821c9d83f..c45e9a36217ec 100644 --- a/Framework/Core/src/DecongestionService.h +++ b/Framework/Core/src/DecongestionService.h @@ -18,6 +18,12 @@ namespace o2::framework struct DecongestionService { /// Wether we are a source in the processing chain bool isFirstInTopology = true; + /// The last timeslice which the ExpirationHandler::Creator callback + /// created. This can be used to skip dummy iterations. + size_t nextEnumerationTimeslice = 0; + /// Flag to indicate that we rewinded the nextExnumerationTimeslice. + /// The rewinded value must be checked when sending the oldestPossible. + bool nextEnumerationTimesliceRewinded = false; /// Last timeslice we communicated. Notice this should never go backwards. int64_t lastTimeslice = 0; /// The next timeslice we should consume, when running in order, diff --git a/Framework/Core/src/DefaultsHelpers.cxx b/Framework/Core/src/DefaultsHelpers.cxx index 443eaed2ffae7..88956b93a855d 100644 --- a/Framework/Core/src/DefaultsHelpers.cxx +++ b/Framework/Core/src/DefaultsHelpers.cxx @@ -55,7 +55,7 @@ static DeploymentMode getDeploymentMode_internal() throw std::runtime_error("Invalid deployment mode"); } } - return getenv("DDS_SESSION_ID") != nullptr ? DeploymentMode::OnlineDDS : (getenv("OCC_CONTROL_PORT") != nullptr ? DeploymentMode::OnlineECS : (getenv("ALIEN_JOB_ID") != nullptr ? DeploymentMode::Grid : (getenv("ALICE_O2_FST") ? DeploymentMode::FST : (DeploymentMode::Local)))); + return getenv("DDS_SESSION_ID") != nullptr ? DeploymentMode::OnlineDDS : (getenv("OCC_CONTROL_PORT") != nullptr ? DeploymentMode::OnlineECS : (getenv("ALIEN_PROC_ID") != nullptr ? DeploymentMode::Grid : (getenv("ALICE_O2_FST") ? DeploymentMode::FST : (DeploymentMode::Local)))); } DeploymentMode DefaultsHelpers::deploymentMode() diff --git a/Framework/Core/src/DeviceSpecHelpers.cxx b/Framework/Core/src/DeviceSpecHelpers.cxx index 2041dc5a38a09..f11f60171e451 100644 --- a/Framework/Core/src/DeviceSpecHelpers.cxx +++ b/Framework/Core/src/DeviceSpecHelpers.cxx @@ -1482,8 +1482,8 @@ void DeviceSpecHelpers::prepareArguments(bool defaultQuiet, bool defaultStopped, haveSessionArg = haveSessionArg || varmap.count("session") != 0; useDefaultWS = useDefaultWS && ((varmap.count("driver-client-backend") == 0) || varmap["driver-client-backend"].as() == "ws://"); - auto processRawChannelConfig = [&tmpArgs](const std::string& conf) { - std::stringstream ss(conf); + auto processRawChannelConfig = [&tmpArgs, &spec](const std::string& conf) { + std::stringstream ss(reworkTimeslicePlaceholder(conf, spec)); std::string token; while (std::getline(ss, token, ';')) { // split to tokens, trim spaces and add each non-empty one with channel-config options token.erase(token.begin(), std::find_if(token.begin(), token.end(), [](int ch) { return !std::isspace(ch); })); @@ -1667,7 +1667,7 @@ bool DeviceSpecHelpers::hasLabel(DeviceSpec const& spec, char const* label) return std::find_if(spec.labels.begin(), spec.labels.end(), sameLabel) != spec.labels.end(); } -std::string DeviceSpecHelpers::reworkEnv(std::string const& str, DeviceSpec const& spec) +std::string DeviceSpecHelpers::reworkTimeslicePlaceholder(std::string const& str, DeviceSpec const& spec) { // find all the possible timeslice variables, extract N and replace // the variable with the value of spec.inputTimesliceId + N. diff --git a/Framework/Core/src/DeviceSpecHelpers.h b/Framework/Core/src/DeviceSpecHelpers.h index 5a036db50618c..1fb1866ae2dc3 100644 --- a/Framework/Core/src/DeviceSpecHelpers.h +++ b/Framework/Core/src/DeviceSpecHelpers.h @@ -131,7 +131,7 @@ struct DeviceSpecHelpers { /// Rework the environment string /// * Substitute {timeslice} with the actual value of the timeslice. - static std::string reworkEnv(std::string const& str, DeviceSpec const& spec); + static std::string reworkTimeslicePlaceholder(std::string const& str, DeviceSpec const& spec); /// This takes the list of preprocessed edges of a graph /// and creates Devices and Channels which are related diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index 9dc709bf5befc..4d9d4c11df49f 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -31,6 +31,7 @@ #include "Framework/Monitoring.h" #include "Headers/DataHeader.h" #include "Headers/Stack.h" +#include "DecongestionService.h" #include "CommonConstants/LHCConstants.h" #include "./DeviceSpecHelpers.h" @@ -38,6 +39,7 @@ #include #include +#include #include #include #include @@ -50,7 +52,6 @@ namespace o2::framework { - using DataHeader = o2::header::DataHeader; std::string formatExternalChannelConfiguration(InputChannelSpec const& spec) @@ -202,17 +203,218 @@ void appendForSending(fair::mq::Device& device, o2::header::Stack&& headerStack, InjectorFunction o2DataModelAdaptor(OutputSpec const& spec, uint64_t startTime, uint64_t /*step*/) { - return [spec](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + return [spec](TimingInfo&, ServiceRegistryRef const& ref, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) -> bool { + auto* device = ref.get().device(); for (int i = 0; i < parts.Size() / 2; ++i) { auto dh = o2::header::get(parts.At(i * 2)->GetData()); DataProcessingHeader dph{newTimesliceId, 0}; o2::header::Stack headerStack{*dh, dph}; - sendOnChannel(device, std::move(headerStack), std::move(parts.At(i * 2 + 1)), spec, channelRetriever); + sendOnChannel(*device, std::move(headerStack), std::move(parts.At(i * 2 + 1)), spec, channelRetriever); } + return parts.Size() > 0; }; } +auto getFinalIndex(DataHeader const& dh, size_t msgidx) -> size_t +{ + size_t finalBlockIndex = 0; + if (dh.splitPayloadParts > 0 && dh.splitPayloadParts == dh.splitPayloadIndex) { + // this is indicating a sequence of payloads following the header + // FIXME: we will probably also set the DataHeader version + // Current position + number of parts + 1 (for the header) + finalBlockIndex = msgidx + dh.splitPayloadParts + 1; + } else { + // We can consider the next splitPayloadParts as one block of messages pairs + // because we are guaranteed they are all the same. + // If splitPayloadParts = 0, we assume that means there is only one (header, payload) + // pair. + finalBlockIndex = msgidx + (dh.splitPayloadParts > 0 ? dh.splitPayloadParts : 1) * 2; + } + assert(finalBlockIndex >= msgidx + 2); + return finalBlockIndex; +}; + +void injectMissingData(fair::mq::Device& device, fair::mq::Parts& parts, std::vector const& routes, bool doInjectMissingData, unsigned int doPrintSizes) +{ + // Check for missing data. + static std::vector present; + static std::vector dataSizes; + static std::vector showSize; + present.clear(); + present.resize(routes.size(), false); + dataSizes.clear(); + dataSizes.resize(routes.size(), 0); + showSize.clear(); + showSize.resize(routes.size(), false); + + static std::vector unmatchedDescriptions; + unmatchedDescriptions.clear(); + DataProcessingHeader const* dph = nullptr; + DataHeader const* firstDH = nullptr; + bool hassih = false; + + // Do not check anything which has DISTSUBTIMEFRAME in it. + size_t expectedDataSpecs = 0; + for (size_t pi = 0; pi < present.size(); ++pi) { + auto& spec = routes[pi].matcher; + if (DataSpecUtils::asConcreteDataTypeMatcher(spec).description == header::DataDescription("DISTSUBTIMEFRAME")) { + present[pi] = true; + continue; + } + if (routes[pi].timeslice == 0) { + ++expectedDataSpecs; + } + } + + size_t foundDataSpecs = 0; + for (int msgidx = 0; msgidx < parts.Size(); msgidx += 2) { + bool allFound = true; + int addToSize = -1; + const auto dh = o2::header::get(parts.At(msgidx)->GetData()); + auto const sih = o2::header::get(parts.At(msgidx)->GetData()); + if (sih != nullptr) { + hassih = true; + continue; + } + if (parts.At(msgidx).get() == nullptr) { + LOG(error) << "unexpected nullptr found. Skipping message pair."; + continue; + } + if (!dh) { + LOG(error) << "data on input " << msgidx << " does not follow the O2 data model, DataHeader missing"; + if (msgidx > 0) { + --msgidx; + } + continue; + } + if (firstDH == nullptr) { + firstDH = dh; + if (doPrintSizes && firstDH->tfCounter % doPrintSizes != 0) { + doPrintSizes = 0; + } + } + // Copy the DataProcessingHeader from the first message. + if (dph == nullptr) { + dph = o2::header::get(parts.At(msgidx)->GetData()); + for (size_t pi = 0; pi < present.size(); ++pi) { + if (routes[pi].timeslice != (dph->startTime % routes[pi].maxTimeslices)) { + present[pi] = true; + } + } + } + for (size_t pi = 0; pi < present.size(); ++pi) { + if (present[pi] && !doPrintSizes) { + continue; + } + // Consider uninvolved pipelines as present. + if (routes[pi].timeslice != (dph->startTime % routes[pi].maxTimeslices)) { + present[pi] = true; + continue; + } + allFound = false; + auto& spec = routes[pi].matcher; + OutputSpec query{dh->dataOrigin, dh->dataDescription, dh->subSpecification}; + if (DataSpecUtils::match(spec, query)) { + if (!present[pi]) { + ++foundDataSpecs; + present[pi] = true; + showSize[pi] = true; + } + addToSize = pi; + break; + } + } + int msgidxLast = getFinalIndex(*dh, msgidx); + if (addToSize >= 0) { + int increment = (dh->splitPayloadParts > 0 && dh->splitPayloadParts == dh->splitPayloadIndex) ? 1 : 2; + for (int msgidx2 = msgidx + 1; msgidx2 < msgidxLast; msgidx2 += increment) { + dataSizes[addToSize] += parts.At(msgidx2)->GetSize(); + } + } + // Skip the rest of the block of messages. We subtract 2 because above we increment by 2. + msgidx = msgidxLast - 2; + if (allFound && !doPrintSizes) { + return; + } + } + + for (size_t pi = 0; pi < present.size(); ++pi) { + if (!present[pi]) { + showSize[pi] = true; + unmatchedDescriptions.push_back(pi); + } + } + + if (firstDH && doPrintSizes) { + std::string sizes = ""; + size_t totalSize = 0; + for (size_t pi = 0; pi < present.size(); ++pi) { + if (showSize[pi]) { + totalSize += dataSizes[pi]; + auto& spec = routes[pi].matcher; + sizes += DataSpecUtils::describe(spec) + fmt::format(":{} ", fmt::group_digits(dataSizes[pi])); + } + } + LOGP(important, "RAW {} size report:{}- Total:{}", firstDH->tfCounter, sizes, fmt::group_digits(totalSize)); + } + + if (!doInjectMissingData) { + return; + } + + if (unmatchedDescriptions.size() > 0) { + if (hassih) { + if (firstDH) { + LOG(error) << "Received an EndOfStream message together with data. This should not happen."; + } + LOG(detail) << "This is an End Of Stream message. Not injecting anything."; + return; + } + if (firstDH == nullptr) { + LOG(error) << "Input proxy received incomplete data without any data header. This should not happen! Cannot inject missing data as requsted."; + return; + } + if (dph == nullptr) { + LOG(error) << "Input proxy received incomplete data without any data processing header. This should happen! Cannot inject missing data as requsted."; + return; + } + std::string missing = ""; + for (auto mi : unmatchedDescriptions) { + auto& spec = routes[mi].matcher; + missing += " " + DataSpecUtils::describe(spec); + // If we have a ConcreteDataMatcher, we can create a message with the correct header. + // If we have a ConcreteDataTypeMatcher, we use 0xdeadbeef as subSpecification. + ConcreteDataTypeMatcher concrete = DataSpecUtils::asConcreteDataTypeMatcher(spec); + auto subSpec = DataSpecUtils::getOptionalSubSpec(spec); + if (subSpec == std::nullopt) { + *subSpec = 0xDEADBEEF; + } + o2::header::DataHeader dh{*firstDH}; + dh.dataOrigin = concrete.origin; + dh.dataDescription = concrete.description; + dh.subSpecification = *subSpec; + dh.payloadSize = 0; + dh.splitPayloadParts = 0; + dh.splitPayloadIndex = 0; + dh.payloadSerializationMethod = header::gSerializationMethodNone; + + auto& channelName = routes[mi].channel; + auto& channelInfo = device.GetChannel(channelName); + auto channelAlloc = o2::pmr::getTransportAllocator(channelInfo.Transport()); + auto headerMessage = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, *dph}); + parts.AddPart(std::move(headerMessage)); + // add empty payload message + parts.AddPart(device.NewMessageFor(channelName, 0, 0)); + } + static int maxWarn = 10; // Correct would be o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef, but Framework does not depend on CommonUtils..., but not so critical since receives will send correct number of DEADBEEF messages + static int contDeadBeef = 0; + if (++contDeadBeef <= maxWarn) { + LOGP(alarm, "Found {}/{} data specs, missing data specs: {}, injecting 0xDEADBEEF{}", foundDataSpecs, expectedDataSpecs, missing, contDeadBeef == maxWarn ? " - disabling alarm now to stop flooding the log" : ""); + } + } +} + InjectorFunction dplModelAdaptor(std::vector const& filterSpecs, DPLModelAdapterConfig config) { bool throwOnUnmatchedInputs = config.throwOnUnmatchedInputs; @@ -249,10 +451,11 @@ InjectorFunction dplModelAdaptor(std::vector const& filterSpecs, DPL std::string descriptions; }; - return [filterSpecs = std::move(filterSpecs), throwOnUnmatchedInputs, droppedDataSpecs = std::make_shared()](TimingInfo& timingInfo, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + return [filterSpecs = std::move(filterSpecs), throwOnUnmatchedInputs, droppedDataSpecs = std::make_shared()](TimingInfo& timingInfo, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { // FIXME: this in not thread safe, but better than an alloc of a map per message... std::unordered_map outputs; std::vector unmatchedDescriptions; + auto* device = services.get().device(); static bool override_creation_env = getenv("DPL_RAWPROXY_OVERRIDE_ORBITRESET"); bool override_creation = false; @@ -262,7 +465,7 @@ InjectorFunction dplModelAdaptor(std::vector const& filterSpecs, DPL creationVal = creationValBase; override_creation = true; } else { - auto orbitResetTimeUrl = device.fConfig->GetProperty("orbit-reset-time", "ccdb://CTP/Calib/OrbitResetTime"); + auto orbitResetTimeUrl = device->fConfig->GetProperty("orbit-reset-time", "ccdb://CTP/Calib/OrbitResetTime"); char* err = nullptr; creationVal = std::strtoll(orbitResetTimeUrl.c_str(), &err, 10); if (err && *err == 0 && creationVal) { @@ -316,18 +519,7 @@ InjectorFunction dplModelAdaptor(std::vector const& filterSpecs, DPL break; } } - if (dh->splitPayloadParts > 0 && dh->splitPayloadParts == dh->splitPayloadIndex) { - // this is indicating a sequence of payloads following the header - // FIXME: we will probably also set the DataHeader version - finalBlockIndex = msgidx + dh->splitPayloadParts + 1; - } else { - // We can consider the next splitPayloadParts as one block of messages pairs - // because we are guaranteed they are all the same. - // If splitPayloadParts = 0, we assume that means there is only one (header, payload) - // pair. - finalBlockIndex = msgidx + (dh->splitPayloadParts > 0 ? dh->splitPayloadParts : 1) * 2; - } - assert(finalBlockIndex >= msgidx + 2); + finalBlockIndex = getFinalIndex(*dh, msgidx); if (finalBlockIndex > parts.Size()) { // TODO error handling // LOGP(error, "DataHeader::splitPayloadParts invalid"); @@ -361,11 +553,13 @@ InjectorFunction dplModelAdaptor(std::vector const& filterSpecs, DPL } } // end of loop over parts + bool didSendParts = false; for (auto& [channelName, channelParts] : outputs) { if (channelParts.Size() == 0) { continue; } - sendOnChannel(device, channelParts, channelName, newTimesliceId); + didSendParts = true; + sendOnChannel(*device, channelParts, channelName, newTimesliceId); } if (not unmatchedDescriptions.empty()) { if (throwOnUnmatchedInputs) { @@ -389,14 +583,15 @@ InjectorFunction dplModelAdaptor(std::vector const& filterSpecs, DPL } } } - return; + return didSendParts; }; } InjectorFunction incrementalConverter(OutputSpec const& spec, o2::header::SerializationMethod method, uint64_t startTime, uint64_t step) { auto timesliceId = std::make_shared(startTime); - return [timesliceId, spec, step, method](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { + return [timesliceId, spec, step, method](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { + auto* device = services.get().device(); // We iterate on all the parts and we send them two by two, // adding the appropriate O2 header. for (int i = 0; i < parts.Size(); ++i) { @@ -418,8 +613,9 @@ InjectorFunction incrementalConverter(OutputSpec const& spec, o2::header::Serial // we have to move the incoming data o2::header::Stack headerStack{dh, dph}; - sendOnChannel(device, std::move(headerStack), std::move(parts.At(i)), spec, channelRetriever); + sendOnChannel(*device, std::move(headerStack), std::move(parts.At(i)), spec, channelRetriever); } + return parts.Size(); }; } @@ -428,7 +624,9 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, char const* defaultChannelConfig, InjectorFunction converter, uint64_t minSHM, - bool sendTFcounter) + bool sendTFcounter, + bool doInjectMissingData, + unsigned int doPrintSizes) { DataProcessorSpec spec; spec.name = strdup(name); @@ -440,7 +638,7 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, // The Init method will register a new "Out of band" channel and // attach an OnData to it which is responsible for converting incoming // messages into DPL messages. - spec.algorithm = AlgorithmSpec{[converter, minSHM, deviceName = spec.name, sendTFcounter](InitContext& ctx) { + spec.algorithm = AlgorithmSpec{[converter, minSHM, deviceName = spec.name, sendTFcounter, doInjectMissingData, doPrintSizes](InitContext& ctx) { auto* device = ctx.services().get().device(); // make a copy of the output routes and pass to the lambda by move auto outputRoutes = ctx.services().get().spec().outputs; @@ -467,11 +665,12 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, for (auto& channel : channels) { LOGP(detail, "Injecting channel '{}' into DPL configuration", channel); // Converter should pump messages + auto& channelPtr = services.get().device()->GetChannel(channel, 0); deviceState.inputChannelInfos.push_back(InputChannelInfo{ .state = InputChannelState::Running, .hasPendingEvents = false, .readPolled = false, - .channel = nullptr, + .channel = &channelPtr, .id = {ChannelIndex::INVALID}, .channelType = ChannelAccountingType::RAWFMQ, }); @@ -490,14 +689,39 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, } // We keep track of whether or not all channels have seen a new state. std::vector lastNewStatePending(deviceState.inputChannelInfos.size(), false); + uv_update_time(deviceState.loop); + auto start = uv_now(deviceState.loop); // Continue iterating until all channels have seen a new state. - while (std::all_of(lastNewStatePending.begin(), lastNewStatePending.end(), [](bool b) { return b; })) { + while (std::all_of(lastNewStatePending.begin(), lastNewStatePending.end(), [](bool b) { return b; }) != true) { + if (uv_now(deviceState.loop) - start > 5000) { + LOGP(info, "Timeout while draining messages, going to next state anyway."); + break; + } fair::mq::Parts parts; for (size_t ci = 0; ci < deviceState.inputChannelInfos.size(); ++ci) { auto& info = deviceState.inputChannelInfos[ci]; + // We only care about rawfmq channels. + if (info.channelType != ChannelAccountingType::RAWFMQ) { + lastNewStatePending[ci] = true; + continue; + } + // This means we have not set things up yet. I.e. the first iteration from + // ready to run has not happened yet. + if (info.channel == nullptr) { + lastNewStatePending[ci] = true; + continue; + } info.channel->Receive(parts, 10); - lastNewStatePending[ci] = device->NewStatePending(); + // Handle both cases of state changes: + // + // - The state has been changed from the outside and FairMQ knows about it. + // - The state has been changed from the GUI, and deviceState.nextFairMQState knows about it. + // + // This latter case is probably better handled from DPL itself, after all it's fair to + // assume we need to switch state as soon as the GUI notifies us. + // For now we keep it here to avoid side effects. + lastNewStatePending[ci] = device->NewStatePending() || (deviceState.nextFairMQState.empty() == false); if (parts.Size() == 0) { continue; } @@ -511,6 +735,8 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, info.readPolled = true; } } + // Keep state transitions going also when running with the standalone GUI. + uv_run(deviceState.loop, UV_RUN_NOWAIT); } }; @@ -536,12 +762,14 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, return count; }; - auto dataHandler = [device, converter, + // Data handler for incoming data. Must return true if it sent any data. + auto dataHandler = [converter, doInjectMissingData, doPrintSizes, outputRoutes = std::move(outputRoutes), control = &ctx.services().get(), deviceState = &ctx.services().get(), timesliceIndex = &ctx.services().get(), - outputChannels = std::move(outputChannels)](TimingInfo& timingInfo, fair::mq::Parts& inputs, int, size_t ci, bool newRun) { + outputChannels = std::move(outputChannels)](ServiceRegistryRef ref, TimingInfo& timingInfo, fair::mq::Parts& inputs, int, size_t ci, bool newRun) -> bool { + auto* device = ref.get().device(); // pass a copy of the outputRoutes auto channelRetriever = [&outputRoutes](OutputSpec const& query, DataProcessingHeader::StartTime timeslice) -> std::string { for (auto& route : outputRoutes) { @@ -556,17 +784,20 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, std::string const& channel = channels[ci]; // we buffer the condition since the converter will forward messages by move int nEos = countEoS(inputs); - numberOfEoS[ci] += nEos; if (newRun) { std::fill(numberOfEoS.begin(), numberOfEoS.end(), 0); std::fill(eosPeersCount.begin(), eosPeersCount.end(), 0); } + numberOfEoS[ci] += nEos; if (numberOfEoS[ci]) { eosPeersCount[ci] = std::max(eosPeersCount[ci], device->GetNumberOfConnectedPeers(channel)); } // For reference, the oldest possible timeframe passed as newTimesliceId here comes from LifetimeHelpers::enumDrivenCreation() bool shouldstop = false; - converter(timingInfo, *device, inputs, channelRetriever, timesliceIndex->getOldestPossibleOutput().timeslice.value, shouldstop); + if (doInjectMissingData) { + injectMissingData(*device, inputs, outputRoutes, doInjectMissingData, doPrintSizes); + } + bool didSendParts = converter(timingInfo, ref, inputs, channelRetriever, timesliceIndex->getOldestPossibleOutput().timeslice.value, shouldstop); // If we have enough EoS messages, we can stop the device // Notice that this has a number of failure modes: @@ -574,9 +805,19 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, // * If a connection sends two EoS. // * If a connection sends an end of stream closes and another one opens. // Finally, if we didn't receive an EoS this time, out counting of the connected peers is off, so the best thing we can do is delay the EoS reporting - bool everyEoS = shouldstop || (numberOfEoS[ci] >= eosPeersCount[ci] && nEos); + bool everyEoS = shouldstop; + if (!shouldstop && nEos) { + everyEoS = true; + for (unsigned int i = 0; i < numberOfEoS.size(); i++) { + if (numberOfEoS[i] < eosPeersCount[i]) { + everyEoS = false; + break; + } + } + } if (everyEoS) { + LOG(info) << "Received (on channel " << ci << ") " << numberOfEoS[ci] << " end-of-stream from " << eosPeersCount[ci] << " peers, forwarding end-of-stream (shouldstop " << (int)shouldstop << ", nEos " << nEos << ", newRun " << (int)newRun << ")"; // Mark all input channels as closed for (auto& info : deviceState->inputChannelInfos) { info.state = InputChannelState::Completed; @@ -585,6 +826,7 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, std::fill(eosPeersCount.begin(), eosPeersCount.end(), 0); control->endOfStream(); } + return didSendParts; }; auto runHandler = [dataHandler, minSHM, sendTFcounter](ProcessingContext& ctx) { @@ -597,6 +839,7 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, inStopTransition = true; } + bool didSendParts = false; for (size_t ci = 0; ci < channels.size(); ++ci) { std::string const& channel = channels[ci]; int waitTime = channels.size() == 1 ? -1 : 1; @@ -613,11 +856,13 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, auto const dh = o2::header::get(parts.At(0)->GetData()); auto& timingInfo = ctx.services().get(); if (dh != nullptr) { - if (currentRunNumber != -1 && dh->runNumber != currentRunNumber) { + if (currentRunNumber != -1 && dh->runNumber != 0 && dh->runNumber != currentRunNumber) { newRun = true; inStopTransition = false; } - currentRunNumber = dh->runNumber; + if (currentRunNumber == -1 || dh->runNumber != 0) { + currentRunNumber = dh->runNumber; + } timingInfo.runNumber = dh->runNumber; timingInfo.firstTForbit = dh->firstTForbit; timingInfo.tfCounter = dh->tfCounter; @@ -628,7 +873,7 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, timingInfo.creation = dph->creation; } if (!inStopTransition) { - dataHandler(timingInfo, parts, 0, ci, newRun); + didSendParts |= dataHandler(ctx.services(), timingInfo, parts, 0, ci, newRun); } if (sendTFcounter) { ctx.services().get().send(o2::monitoring::Metric{(uint64_t)timingInfo.tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); @@ -640,6 +885,15 @@ DataProcessorSpec specifyExternalFairMQDeviceProxy(char const* name, waitTime = 0; } } + // In case we did not send any part at all, we need to rewind by one + // to avoid creating extra timeslices. + auto& decongestion = ctx.services().get(); + decongestion.nextEnumerationTimesliceRewinded = !didSendParts; + if (didSendParts) { + ctx.services().get().fakeDispatch(); + } else { + decongestion.nextEnumerationTimeslice -= 1; + } }; return runHandler; diff --git a/Framework/Core/src/LifetimeHelpers.cxx b/Framework/Core/src/LifetimeHelpers.cxx index eb6fb0984736f..1aa53fa0493ca 100644 --- a/Framework/Core/src/LifetimeHelpers.cxx +++ b/Framework/Core/src/LifetimeHelpers.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "DecongestionService.h" #include "Framework/DataProcessingHeader.h" #include "Framework/InputSpec.h" #include "Framework/LifetimeHelpers.h" @@ -58,36 +59,42 @@ size_t getCurrentTime() ExpirationHandler::Creator LifetimeHelpers::dataDrivenCreation() { - return [](ChannelIndex, TimesliceIndex&) -> TimesliceSlot { + return [](ServiceRegistryRef, ChannelIndex) -> TimesliceSlot { return {TimesliceSlot::ANY}; }; } ExpirationHandler::Creator LifetimeHelpers::enumDrivenCreation(size_t start, size_t end, size_t step, size_t inputTimeslice, size_t maxInputTimeslices, size_t maxRepetitions) { - auto last = std::make_shared(start + inputTimeslice * step); + size_t firstTimeslice = start + inputTimeslice * step; auto repetition = std::make_shared(0); - return [end, step, last, maxInputTimeslices, maxRepetitions, repetition](ChannelIndex channelIndex, TimesliceIndex& index) -> TimesliceSlot { + return [end, step, firstTimeslice, maxInputTimeslices, maxRepetitions, repetition](ServiceRegistryRef services, ChannelIndex channelIndex) -> TimesliceSlot { + auto& index = services.get(); + auto& decongestion = services.get(); + if (decongestion.nextEnumerationTimeslice == 0) { + decongestion.nextEnumerationTimeslice = firstTimeslice; + } + for (size_t si = 0; si < index.size(); si++) { - if (*last > end) { + if (decongestion.nextEnumerationTimeslice > end) { LOGP(debug, "Last greater than end"); return TimesliceSlot{TimesliceSlot::INVALID}; } auto slot = TimesliceSlot{si}; if (index.isValid(slot) == false) { - TimesliceId timestamp{*last}; + TimesliceId timestamp{decongestion.nextEnumerationTimeslice}; *repetition += 1; if (*repetition % maxRepetitions == 0) { - *last += step * maxInputTimeslices; + decongestion.nextEnumerationTimeslice += step * maxInputTimeslices; } LOGP(debug, "Associating timestamp {} to slot {}", timestamp.value, slot.index); index.associate(timestamp, slot); // We know that next association will bring in last // so we can state this will be the latest possible input for the channel // associated with this. - LOG(debug) << "Oldest possible input is " << *last; - auto newOldest = index.setOldestPossibleInput({*last}, channelIndex); + LOG(debug) << "Oldest possible input is " << decongestion.nextEnumerationTimeslice; + [[maybe_unused]] auto newOldest = index.setOldestPossibleInput({decongestion.nextEnumerationTimeslice}, channelIndex); index.updateOldestPossibleOutput(); return slot; } @@ -100,10 +107,9 @@ ExpirationHandler::Creator LifetimeHelpers::enumDrivenCreation(size_t start, siz ExpirationHandler::Creator LifetimeHelpers::timeDrivenCreation(std::vector periods, std::vector intervals, std::function hasTimerFired, std::function updateTimerPeriod) { - std::shared_ptr last = std::make_shared(0); std::shared_ptr stablePeriods = std::make_shared(false); // FIXME: should create timeslices when period expires.... - return [last, stablePeriods, periods, intervals, hasTimerFired, updateTimerPeriod](ChannelIndex channelIndex, TimesliceIndex& index) mutable -> TimesliceSlot { + return [stablePeriods, periods, intervals, hasTimerFired, updateTimerPeriod](ServiceRegistryRef services, ChannelIndex channelIndex) mutable -> TimesliceSlot { // We start with a random offset to avoid all the devices // send their first message at the same time, bring down // the QC machine. @@ -113,13 +119,16 @@ ExpirationHandler::Creator LifetimeHelpers::timeDrivenCreation(std::vector(); + auto& decongestion = services.get(); + bool timerHasFired = hasTimerFired(); - if (*last == 0ULL || (index.didReceiveData() == false && timerHasFired)) { + if (decongestion.nextEnumerationTimeslice == 0ULL || (index.didReceiveData() == false && timerHasFired)) { std::random_device r; std::default_random_engine e1(r()); std::uniform_int_distribution dist(0, periods.front().count() * 0.9); auto randomizedPeriodUs = static_cast(dist(e1) + periods.front().count() * 0.1); - *last = getCurrentTime() - randomizedPeriodUs; + decongestion.nextEnumerationTimeslice = getCurrentTime() - randomizedPeriodUs; updateTimerPeriod(randomizedPeriodUs / 1000, randomizedPeriodUs / 1000); *stablePeriods = false; LOG(debug) << "Timer updated to a randomized period of " << randomizedPeriodUs << "us"; @@ -130,7 +139,7 @@ ExpirationHandler::Creator LifetimeHelpers::timeDrivenCreation(std::vector TimesliceSlot { + return [requestedLoopReason, &state](ServiceRegistryRef services, ChannelIndex) -> TimesliceSlot { /// Not the expected loop reason, return an invalid slot. + auto& index = services.get(); if ((state.loopReason & requestedLoopReason) == 0) { LOGP(debug, "No expiration due to a loop event. Requested: {:b}, reported: {:b}, matching: {:b}", requestedLoopReason, diff --git a/Framework/Core/src/PropertyTreeHelpers.cxx b/Framework/Core/src/PropertyTreeHelpers.cxx index 48bb4803b18b4..cf862b78e77b5 100644 --- a/Framework/Core/src/PropertyTreeHelpers.cxx +++ b/Framework/Core/src/PropertyTreeHelpers.cxx @@ -106,6 +106,9 @@ void PropertyTreeHelpers::populateDefaults(std::vector const& s case VariantType::LabeledArrayDouble: pt.put_child(key, labeledArrayToBranch(spec.defaultValue.get>())); break; + case VariantType::LabeledArrayString: + pt.put_child(key, labeledArrayToBranch(spec.defaultValue.get>())); + break; case VariantType::Unknown: case VariantType::Empty: default: @@ -320,6 +323,14 @@ void PropertyTreeHelpers::populate(std::vector const& schema, pt.put_child(key, labeledArrayToBranch(std::move(v))); } }; break; + case VariantType::LabeledArrayString: { + auto v = labeledArrayFromBranch(it.value()); + if (!replaceLabels(v, spec.defaultValue.get>())) { + pt.put_child(key, *it); + } else { + pt.put_child(key, labeledArrayToBranch(std::move(v))); + } + }; break; case VariantType::Unknown: case VariantType::Empty: default: diff --git a/Framework/Core/src/ReadoutAdapter.cxx b/Framework/Core/src/ReadoutAdapter.cxx deleted file mode 100644 index 9b0c96fcad6ba..0000000000000 --- a/Framework/Core/src/ReadoutAdapter.cxx +++ /dev/null @@ -1,46 +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 "Framework/ReadoutAdapter.h" -#include "Framework/DataProcessingHeader.h" -#include "Framework/DataSpecUtils.h" -#include "Headers/DataHeader.h" - -namespace o2 -{ -namespace framework -{ - -using DataHeader = o2::header::DataHeader; - -InjectorFunction readoutAdapter(OutputSpec const& spec) -{ - return [spec](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { - for (size_t i = 0; i < parts.Size(); ++i) { - DataHeader dh; - // FIXME: this will have to change and extract the actual subspec from - // the data. - ConcreteDataMatcher concrete = DataSpecUtils::asConcreteDataMatcher(spec); - dh.dataOrigin = concrete.origin; - dh.dataDescription = concrete.description; - dh.subSpecification = concrete.subSpec; - dh.payloadSize = parts.At(i)->GetSize(); - dh.payloadSerializationMethod = o2::header::gSerializationMethodNone; - - DataProcessingHeader dph{newTimesliceId, 0}; - o2::header::Stack headerStack{dh, dph}; - sendOnChannel(device, std::move(headerStack), std::move(parts.At(i)), spec, channelRetriever); - } - }; -} - -} // namespace framework -} // namespace o2 diff --git a/Framework/Core/src/StreamContext.cxx b/Framework/Core/src/StreamContext.cxx index e11e658f38558..c7f28a3dbde1a 100644 --- a/Framework/Core/src/StreamContext.cxx +++ b/Framework/Core/src/StreamContext.cxx @@ -35,6 +35,17 @@ void StreamContext::preProcessingCallbacks(ProcessingContext& pcx) } } +/// Invoke callbacks to be executed after every process method invokation +void StreamContext::finaliseOutputsCallbacks(ProcessingContext& pcx) +{ + for (auto& handle : finaliseOutputsHandles) { + LOG(debug) << "Invoking finaliseOutputsCallbacks for " << handle.service; + assert(handle.service); + assert(handle.callback); + handle.callback(pcx, handle.service); + } +} + /// Invoke callbacks to be executed after every process method invokation void StreamContext::postProcessingCallbacks(ProcessingContext& pcx) { diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index ce56385f71dd4..cd9c3913de228 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -588,7 +588,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } if (enumCandidate != -1) { auto& dp = workflow[enumCandidate]; - dp.outputs.push_back(OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); + DataSpecUtils::updateOutputList(dp.outputs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); ccdbBackend.inputs.push_back(InputSpec{"tfn", dstf, Lifetime::Timeframe}); } else if (timerCandidate != -1) { auto& dp = workflow[timerCandidate]; @@ -635,7 +635,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } if (enumCandidate != -1) { auto& dp = workflow[enumCandidate]; - dp.outputs.push_back(OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); + DataSpecUtils::updateOutputList(dp.outputs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); ccdbBackend.inputs.push_back(InputSpec{"tfn", dstf, Lifetime::Timeframe}); } else if (timerCandidate != -1) { auto& dp = workflow[timerCandidate]; diff --git a/Framework/Core/src/WorkflowSerializationHelpers.cxx b/Framework/Core/src/WorkflowSerializationHelpers.cxx index 3a33d4a14a734..ac182a27a70c5 100644 --- a/Framework/Core/src/WorkflowSerializationHelpers.cxx +++ b/Framework/Core/src/WorkflowSerializationHelpers.cxx @@ -507,6 +507,9 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler, case VariantType::LabeledArrayDouble: opt = std::make_unique(optionName, optionType, VariantJSONHelpers::read(is), HelpString{optionHelp}, optionKind); break; + case VariantType::LabeledArrayString: + opt = std::make_unique(optionName, optionType, VariantJSONHelpers::read(is), HelpString{optionHelp}, optionKind); + break; case VariantType::Dict: opt = std::make_unique(optionName, optionType, emptyDict(), HelpString{optionHelp}, optionKind); break; @@ -1191,6 +1194,7 @@ void WorkflowSerializationHelpers::dump(std::ostream& out, case VariantType::LabeledArrayInt: case VariantType::LabeledArrayFloat: case VariantType::LabeledArrayDouble: + case VariantType::LabeledArrayString: case VariantType::Dict: VariantJSONHelpers::write(oss, option.defaultValue); break; diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 8651d25829963..1d9619112d08b 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -681,7 +681,7 @@ void spawnDevice(uv_loop_t* loop, } } for (auto& env : execution.environ) { - putenv(strdup(DeviceSpecHelpers::reworkEnv(env, spec).data())); + putenv(strdup(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec).data())); } execvp(execution.args[0], execution.args.data()); } @@ -1800,6 +1800,9 @@ int runStateMachine(DataProcessorSpecs const& workflow, case VariantType::LabeledArrayDouble: option.defaultValue = reg->get>(name); break; + case VariantType::LabeledArrayString: + option.defaultValue = reg->get>(name); + break; default: break; } diff --git a/Framework/Core/test/benchmark_ExternalFairMQDeviceProxies.cxx b/Framework/Core/test/benchmark_ExternalFairMQDeviceProxies.cxx index 839c19874ea21..bdb4deb443021 100644 --- a/Framework/Core/test/benchmark_ExternalFairMQDeviceProxies.cxx +++ b/Framework/Core/test/benchmark_ExternalFairMQDeviceProxies.cxx @@ -469,21 +469,22 @@ std::vector defineDataProcessing(ConfigContext const& config) // reads the messages from the output proxy via the out-of-band channel // converter callback for the external FairMQ device proxy ProcessorSpec generator - auto converter = [](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& inputs, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { + InjectorFunction converter = [](TimingInfo&, ServiceRegistryRef const& ref, fair::mq::Parts& inputs, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) -> bool { + auto* device = ref.get().device(); ASSERT_ERROR(inputs.Size() >= 2); if (inputs.Size() < 2) { - return; + return false; } int msgidx = 0; auto dh = o2::header::get(inputs.At(msgidx)->GetData()); if (!dh) { LOG(error) << "data on input " << msgidx << " does not follow the O2 data model, DataHeader missing"; - return; + return false; } auto dph = o2::header::get(inputs.At(msgidx)->GetData()); if (!dph) { LOG(error) << "data on input " << msgidx << " does not follow the O2 data model, DataProcessingHeader missing"; - return; + return false; } // Note: we want to run both the output and input proxy in the same workflow and thus we need // different data identifiers and change the data origin in the forwarding @@ -496,10 +497,10 @@ std::vector defineDataProcessing(ConfigContext const& config) ASSERT_ERROR(!isData || !channelName.empty()); LOG(debug) << "using channel '" << channelName << "' for " << DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}); if (channelName.empty()) { - return; + return false; } // make a copy of the header message, get the data header and change origin - auto outHeaderMessage = device.NewMessageFor(channelName, 0, inputs.At(msgidx)->GetSize()); + auto outHeaderMessage = device->NewMessageFor(channelName, 0, inputs.At(msgidx)->GetSize()); memcpy(outHeaderMessage->GetData(), inputs.At(msgidx)->GetData(), inputs.At(msgidx)->GetSize()); // this we obviously need to fix in the get API, const'ness of the returned header pointer // should depend on const'ness of the buffer @@ -509,7 +510,8 @@ std::vector defineDataProcessing(ConfigContext const& config) output.AddPart(std::move(outHeaderMessage)); output.AddPart(std::move(inputs.At(msgidx + 1))); LOG(debug) << "sending " << DataSpecUtils::describe(OutputSpec{odh->dataOrigin, odh->dataDescription, odh->subSpecification}); - o2::framework::sendOnChannel(device, output, channelName, (size_t)-1); + o2::framework::sendOnChannel(*device, output, channelName, (size_t)-1); + return output.Size() > 0; }; // we use the same spec to build the configuration string, ideally we would have some helpers diff --git a/Framework/Core/test/test_ASoA.cxx b/Framework/Core/test/test_ASoA.cxx index ffa6b4ea5a086..e6a537dab3dc9 100644 --- a/Framework/Core/test/test_ASoA.cxx +++ b/Framework/Core/test/test_ASoA.cxx @@ -10,10 +10,8 @@ // or submit itself to any jurisdiction. #include "Framework/ASoA.h" -#include "Framework/ASoAHelpers.h" #include "Framework/Expressions.h" #include "Framework/AnalysisHelpers.h" -#include "Framework/ExpressionHelpers.h" #include "gandiva/tree_expr_builder.h" #include "arrow/status.h" #include "gandiva/filter.h" @@ -871,22 +869,23 @@ TEST_CASE("TestAdvancedIndices") int references[] = {19, 2, 0, 13, 4, 6, 5, 5, 11, 9, 3, 8, 16, 14, 1, 18, 12, 18, 2, 7}; int slice[2] = {-1, -1}; std::vector pset; - std::array withSlices = {3, 13, 19}; + std::array withSlices = {3, 6, 13, 19}; + std::array, 4> bounds = {std::pair{1, 5}, std::pair{3, 3}, std::pair{11, 11}, std::pair{10, 18}}; std::array withSets = {0, 1, 13, 14}; - int sizes[] = {3, 1, 5, 4}; - int c1 = 0; - int c2 = 0; + unsigned int sizes[] = {3, 1, 5, 4}; + unsigned int c1 = 0; + unsigned int c2 = 0; for (auto i = 0; i < 20; ++i) { pset.clear(); slice[0] = -1; slice[1] = -1; if (c1 < withSlices.size() && i == withSlices[c1]) { - slice[0] = i - 2; - slice[1] = i - 1; + slice[0] = bounds[c1].first; + slice[1] = bounds[c1].second; ++c1; } if (c2 < withSets.size() && i == withSets[c2]) { - for (auto z = 0; z < sizes[c2]; ++z) { + for (auto z = 0U; z < sizes[c2]; ++z) { pset.push_back(i + 1 + z); } ++c2; @@ -908,10 +907,12 @@ TEST_CASE("TestAdvancedIndices") auto ops = p.pointSeq_as(); if (i == withSlices[c1]) { auto it = ops.begin(); - REQUIRE(ops.size() == 2); - REQUIRE(it.globalIndex() == i - 2); - ++it; - REQUIRE(it.globalIndex() == i - 1); + REQUIRE(ops.size() == bounds[c1].second - bounds[c1].first + 1); + REQUIRE(it.globalIndex() == bounds[c1].first); + for (auto j = 1; j < ops.size(); ++j) { + ++it; + } + REQUIRE(it.globalIndex() == bounds[c1].second); ++c1; } else { REQUIRE(ops.size() == 0); @@ -1130,3 +1131,53 @@ TEST_CASE("TestArrayColumns") } } } + +namespace o2::aod +{ +namespace table +{ +DECLARE_SOA_COLUMN(One, one, int); +DECLARE_SOA_COLUMN(Two, two, float); +DECLARE_SOA_COLUMN(Three, three, double); +DECLARE_SOA_COLUMN(Four, four, int[2]); +DECLARE_SOA_DYNAMIC_COLUMN(Five, five, [](const int in[2]) -> float { return (float)in[0] / (float)in[1]; }); +} // namespace table +DECLARE_SOA_TABLE(MixTest, "AOD", "MIXTST", + table::One, table::Two, table::Three, table::Four, + table::Five); +} // namespace o2::aod +TEST_CASE("TestCombinedGetter") +{ + TableBuilder b; + auto writer = b.cursor(); + int f[2]; + for (auto i = 0; i < 20; ++i) { + f[0] = i; + f[1] = i + 1; + writer(0, i, o2::constants::math::PI * i, o2::constants::math::Almost0 * i, f); + } + auto t = b.finalize(); + o2::aod::MixTest mt{t}; + auto count = 0; + for (auto const& row : mt) { + auto features1 = row.getValues(); + auto features2 = row.getValues(); + auto features3 = row.getValues>(); + auto b1 = std::is_same_v, decltype(features1)>; + REQUIRE(b1); + auto b2 = std::is_same_v, decltype(features2)>; + REQUIRE(b2); + auto b3 = std::is_same_v, decltype(features3)>; + REQUIRE(b3); + REQUIRE(features1[0] == (float)count); + REQUIRE(features1[1] == (float)(o2::constants::math::Almost0 * count)); + + REQUIRE(features2[0] == (double)count); + REQUIRE(features2[1] == (double)(o2::constants::math::PI * count)); + REQUIRE(features2[2] == (double)(o2::constants::math::Almost0 * count)); + + REQUIRE(features3[0] == (float)(o2::constants::math::PI * count)); + REQUIRE(features3[1] == (float)((float)count / (float)(count + 1))); + ++count; + } +} diff --git a/Framework/Core/test/test_AnalysisTask.cxx b/Framework/Core/test/test_AnalysisTask.cxx index b07c6a9afa0d4..30e58735257ed 100644 --- a/Framework/Core/test/test_AnalysisTask.cxx +++ b/Framework/Core/test/test_AnalysisTask.cxx @@ -164,7 +164,7 @@ TEST_CASE("AdaptorCompilation") REQUIRE(task2.inputs.size() == 10); REQUIRE(task2.inputs[1].binding == "TracksExtension"); REQUIRE(task2.inputs[2].binding == "Tracks"); - REQUIRE(task2.inputs[3].binding == "TracksExtraExtension"); + REQUIRE(task2.inputs[3].binding == "TracksExtra_000Extension"); REQUIRE(task2.inputs[4].binding == "TracksExtra"); REQUIRE(task2.inputs[5].binding == "TracksCovExtension"); REQUIRE(task2.inputs[6].binding == "TracksCov"); diff --git a/Framework/Core/test/test_DataAllocator.cxx b/Framework/Core/test/test_DataAllocator.cxx index 5536eb1c07684..d880da72d7cb4 100644 --- a/Framework/Core/test/test_DataAllocator.cxx +++ b/Framework/Core/test/test_DataAllocator.cxx @@ -31,6 +31,7 @@ #include #include #include +#include #include // std::declval #include @@ -76,6 +77,8 @@ DataProcessorSpec getSourceSpec() o2::test::TriviallyCopyable a(42, 23, 0xdead); o2::test::Polymorphic b(0xbeef); std::vector c{{0xaffe}, {0xd00f}}; + std::deque testDequePayload{10, 20, 30}; + // class TriviallyCopyable is both messageable and has a dictionary, the default // picked by the framework is no serialization test::MetaHeader meta1{42}; @@ -88,6 +91,8 @@ DataProcessorSpec getSourceSpec() pc.outputs().snapshot(Output{"TST", "ROOTNONTOBJECT", 0, Lifetime::Timeframe}, b); // vector of ROOT serializable class pc.outputs().snapshot(Output{"TST", "ROOTVECTOR", 0, Lifetime::Timeframe}, c); + // deque of simple types + pc.outputs().snapshot(Output{"TST", "DEQUE", 0, Lifetime::Timeframe}, testDequePayload); // likewise, passed anonymously with char type and class name o2::framework::ROOTSerialized d(*((char*)&c), "vector"); pc.outputs().snapshot(Output{"TST", "ROOTSERLZDVEC", 0, Lifetime::Timeframe}, d); @@ -174,6 +179,7 @@ DataProcessorSpec getSourceSpec() OutputSpec{"TST", "MSGBLEROOTSRLZ", 0, Lifetime::Timeframe}, OutputSpec{"TST", "ROOTNONTOBJECT", 0, Lifetime::Timeframe}, OutputSpec{"TST", "ROOTVECTOR", 0, Lifetime::Timeframe}, + OutputSpec{"TST", "DEQUE", 0, Lifetime::Timeframe}, OutputSpec{"TST", "ROOTSERLZDVEC", 0, Lifetime::Timeframe}, OutputSpec{"TST", "ROOTSERLZDVEC2", 0, Lifetime::Timeframe}, OutputSpec{"TST", "PMRTESTVECTOR", 0, Lifetime::Timeframe}, @@ -317,6 +323,12 @@ DataProcessorSpec getSinkSpec() ASSERT_ERROR(object15[0] == o2::test::Polymorphic{0xacdc}); ASSERT_ERROR(object15[1] == o2::test::Polymorphic{0xbeef}); + LOG(info) << "extracting deque to vector from input16"; + auto object16 = pc.inputs().get>("input16"); + LOG(info) << "object16.size() = " << object16.size() << std::endl; + ASSERT_ERROR(object16.size() == 3); + ASSERT_ERROR(object16[0] == 10 && object16[1] == 20 && object16[2] == 30); + LOG(info) << "extracting PMR vector"; auto pmrspan = pc.inputs().get>("inputPMR"); ASSERT_ERROR((pmrspan[0] == o2::test::TriviallyCopyable{1, 2, 3})); @@ -351,6 +363,7 @@ DataProcessorSpec getSinkSpec() InputSpec{"input13", "TST", "MAKETOBJECT", 0, Lifetime::Timeframe}, InputSpec{"input14", "TST", "ROOTSERLZBLOBJ", 0, Lifetime::Timeframe}, InputSpec{"input15", "TST", "ROOTSERLZBLVECT", 0, Lifetime::Timeframe}, + InputSpec{"input16", "TST", "DEQUE", 0, Lifetime::Timeframe}, InputSpec{"inputPMR", "TST", "PMRTESTVECTOR", 0, Lifetime::Timeframe}, InputSpec{"inputPODvector", "TST", "PODVECTOR", 0, Lifetime::Timeframe}, InputSpec{"inputMP", ConcreteDataTypeMatcher{"TST", "MULTIPARTS"}, Lifetime::Timeframe}}, diff --git a/Framework/Core/test/test_DataDescriptorMatcher.cxx b/Framework/Core/test/test_DataDescriptorMatcher.cxx index e83b3d2dc06a1..8b74f7d9f6b6b 100644 --- a/Framework/Core/test/test_DataDescriptorMatcher.cxx +++ b/Framework/Core/test/test_DataDescriptorMatcher.cxx @@ -669,3 +669,27 @@ TEST_CASE("DataQuery") REQUIRE(result5[0].metadata[2].name == "key3"); REQUIRE(result5[0].metadata[2].defaultValue.get() == "value3"); } + +// Make sure that 10 and 1 subspect are matched differently + +TEST_CASE("MatchSubspec") +{ + DataHeader header0; + header0.dataOrigin = "EMC"; + header0.dataDescription = "CELLSTRGR"; + header0.subSpecification = 10; + VariableContext context; + + DataDescriptorMatcher matcher{ + DataDescriptorMatcher::Op::And, + OriginValueMatcher{"EMC"}, + std::make_unique( + DataDescriptorMatcher::Op::And, + DescriptionValueMatcher{"CELLSTRGR"}, + std::make_unique( + DataDescriptorMatcher::Op::And, + SubSpecificationTypeValueMatcher{1}, + ConstantValueMatcher{true}))}; + + REQUIRE(matcher.match(header0, context) == false); +} diff --git a/Framework/Core/test/test_DeviceSpecHelpers.cxx b/Framework/Core/test/test_DeviceSpecHelpers.cxx index 982536879b18c..762c2df6f82aa 100644 --- a/Framework/Core/test/test_DeviceSpecHelpers.cxx +++ b/Framework/Core/test/test_DeviceSpecHelpers.cxx @@ -356,19 +356,19 @@ TEST_CASE("CheckReworkingEnv") { DeviceSpec spec{.inputTimesliceId = 1}; std::string env = "FOO={timeslice0}"; - REQUIRE(DeviceSpecHelpers::reworkEnv(env, spec) == "FOO=1"); + REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "FOO=1"); env = "FOO={timeslice0} BAR={timeslice4}"; - REQUIRE(DeviceSpecHelpers::reworkEnv(env, spec) == "FOO=1 BAR=5"); + REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "FOO=1 BAR=5"); env = "FOO={timeslice0} BAR={timeslice4} BAZ={timeslice5}"; - REQUIRE(DeviceSpecHelpers::reworkEnv(env, spec) == "FOO=1 BAR=5 BAZ=6"); + REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "FOO=1 BAR=5 BAZ=6"); env = ""; - REQUIRE(DeviceSpecHelpers::reworkEnv(env, spec) == ""); + REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == ""); env = "Plottigat"; - REQUIRE(DeviceSpecHelpers::reworkEnv(env, spec) == "Plottigat"); + REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "Plottigat"); env = "{timeslice}"; - REQUIRE(DeviceSpecHelpers::reworkEnv(env, spec) == "{timeslice}"); + REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "{timeslice}"); env = "{timeslicepluto"; - REQUIRE(DeviceSpecHelpers::reworkEnv(env, spec) == "{timeslicepluto"); + REQUIRE(DeviceSpecHelpers::reworkTimeslicePlaceholder(env, spec) == "{timeslicepluto"); } } // namespace o2::framework diff --git a/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx b/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx index bc65094ff5696..c105a9d0f662c 100644 --- a/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx +++ b/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx @@ -176,7 +176,7 @@ std::vector defineDataProcessing(ConfigContext const& config) // the compute callback of the producer auto producerCallback = [nRolls, channelName, proxyMode, counter = std::make_shared()](DataAllocator& outputs, ControlService& control, RawDeviceService& rds) { int data = *counter; - //outputs.make(OutputRef{"data", 0}) = data; + // outputs.make(OutputRef{"data", 0}) = data; fair::mq::Device& device = *(rds.device()); auto transport = device.GetChannel(*channelName, 0).Transport(); @@ -327,14 +327,14 @@ std::vector defineDataProcessing(ConfigContext const& config) Inputs checkerInputs; if (proxyMode != ProxyMode::All) { checkerInputs.emplace_back(InputSpec{"datain", ConcreteDataTypeMatcher{"TST", "DATA"}, Lifetime::Timeframe}); - //for (unsigned int i = 0; i < pState->nChannels; i++) { - // checkerInputs.emplace_back(InputSpec{{"datain"}, "TST", "DATA", i, Lifetime::Timeframe}); - //} + // for (unsigned int i = 0; i < pState->nChannels; i++) { + // checkerInputs.emplace_back(InputSpec{{"datain"}, "TST", "DATA", i, Lifetime::Timeframe}); + // } } else { checkerInputs.emplace_back(InputSpec{"datain", ConcreteDataTypeMatcher{"PRX", "DATA"}, Lifetime::Timeframe}); - //for (unsigned int i = 0; i < pState->nChannels; i++) { - // checkerInputs.emplace_back(InputSpec{{"datain"}, "PRX", "DATA", i, Lifetime::Timeframe}); - //} + // for (unsigned int i = 0; i < pState->nChannels; i++) { + // checkerInputs.emplace_back(InputSpec{{"datain"}, "PRX", "DATA", i, Lifetime::Timeframe}); + // } } if (proxyMode != ProxyMode::OnlyOutput) { // the checker is not added if the input proxy is skipped @@ -349,21 +349,22 @@ std::vector defineDataProcessing(ConfigContext const& config) // reads the messages from the output proxy via the out-of-band channel // converter callback for the external FairMQ device proxy ProcessorSpec generator - auto converter = [](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& inputs, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) { + InjectorFunction converter = [](TimingInfo&, ServiceRegistryRef const& services, fair::mq::Parts& inputs, ChannelRetriever channelRetriever, size_t newTimesliceId, bool&) -> bool { + auto* device = services.get().device(); ASSERT_ERROR(inputs.Size() >= 2); if (inputs.Size() < 2) { - return; + return false; } int msgidx = 0; auto dh = o2::header::get(inputs.At(msgidx)->GetData()); if (!dh) { LOG(error) << "data on input " << msgidx << " does not follow the O2 data model, DataHeader missing"; - return; + return false; } auto dph = o2::header::get(inputs.At(msgidx)->GetData()); if (!dph) { LOG(error) << "data on input " << msgidx << " does not follow the O2 data model, DataProcessingHeader missing"; - return; + return false; } // Note: we want to run both the output and input proxy in the same workflow and thus we need // different data identifiers and change the data origin in the forwarding @@ -376,7 +377,7 @@ std::vector defineDataProcessing(ConfigContext const& config) ASSERT_ERROR(!isData || !channelName.empty()); LOG(debug) << "using channel '" << channelName << "' for " << DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}); if (channelName.empty()) { - return; + return false; } fair::mq::Parts output; for (; msgidx < inputs.Size(); ++msgidx) { @@ -389,7 +390,7 @@ std::vector defineDataProcessing(ConfigContext const& config) dh->splitPayloadParts, dh->splitPayloadIndex); // make a copy of the header message, get the data header and change origin - auto outHeaderMessage = device.NewMessageFor(channelName, 0, inputs.At(msgidx)->GetSize()); + auto outHeaderMessage = device->NewMessageFor(channelName, 0, inputs.At(msgidx)->GetSize()); memcpy(outHeaderMessage->GetData(), inputs.At(msgidx)->GetData(), inputs.At(msgidx)->GetSize()); // this we obviously need to fix in the get API, const'ness of the returned header pointer // should depend on const'ness of the buffer @@ -400,8 +401,8 @@ std::vector defineDataProcessing(ConfigContext const& config) output.AddPart(std::move(inputs.At(msgidx))); } } - o2::framework::sendOnChannel(device, output, channelName, (size_t)-1); - return; + o2::framework::sendOnChannel(*device, output, channelName, (size_t)-1); + return output.Size() != 0; }; // we use the same spec to build the configuration string, ideally we would have some helpers diff --git a/Framework/Core/test/test_Variants.cxx b/Framework/Core/test/test_Variants.cxx index a5753e26925b3..d774fe160885b 100644 --- a/Framework/Core/test/test_Variants.cxx +++ b/Framework/Core/test/test_Variants.cxx @@ -218,14 +218,20 @@ TEST_CASE("Array2DTest") TEST_CASE("LabeledArrayTest") { float m[3][4] = {{0.1, 0.2, 0.3, 0.4}, {0.5, 0.6, 0.7, 0.8}, {0.9, 1.0, 1.1, 1.2}}; + std::string mS[3][4] = {{"a", "b", "c", "d"}, {"e", "f", "g", "h"}, {"i", "l", "m", "n"}}; std::string xl[] = {"c1", "c2", "c3", "c4"}; std::string yl[] = {"r1", "r2", "r3"}; LabeledArray laf{&m[0][0], 3, 4, {"r1", "r2", "r3"}, {"c1", "c2", "c3", "c4"}}; + LabeledArray las{&mS[0][0], 3, 4, {"r1", "r2", "r3"}, {"c1", "c2", "c3", "c4"}}; for (auto i = 0u; i < 3; ++i) { for (auto j = 0u; j < 4; ++j) { REQUIRE(laf.get(yl[i].c_str(), xl[j].c_str()) == laf.get(i, j)); REQUIRE(laf.get(i, xl[j].c_str()) == laf.get(i, j)); REQUIRE(laf.get(yl[i].c_str(), j) == laf.get(i, j)); + + REQUIRE(las.get(yl[i].c_str(), xl[j].c_str()) == las.get(i, j)); + REQUIRE(las.get(i, xl[j].c_str()) == las.get(i, j)); + REQUIRE(las.get(yl[i].c_str(), j) == las.get(i, j)); } } } diff --git a/Framework/Core/test/unittest_DataSpecUtils.cxx b/Framework/Core/test/unittest_DataSpecUtils.cxx index 4ae6f3b0ff832..e6b2f4a22c018 100644 --- a/Framework/Core/test/unittest_DataSpecUtils.cxx +++ b/Framework/Core/test/unittest_DataSpecUtils.cxx @@ -460,3 +460,33 @@ TEST_CASE("optionalConcreteDataMatcherFrom") REQUIRE(DataSpecUtils::asOptionalConcreteDataMatcher(ConcreteDataMatcher{"ITS", "RAWDATA", 0}) == ConcreteDataMatcher{"ITS", "RAWDATA", 0}); REQUIRE(DataSpecUtils::asOptionalConcreteDataMatcher(ConcreteDataTypeMatcher{"ITS", "RAWDATA"}) == std::nullopt); } + +// Testcase for DataSpecUtils::updateOutputList +TEST_CASE("UpdateOutputList") +{ + // Empty vector of output specs should simply add the new spec + std::vector outputSpecs; + DataSpecUtils::updateOutputList(outputSpecs, {"TST", "DATA1", 0}); + CHECK(outputSpecs.size() == 1); + + // Adding the same spec again should not change the size + DataSpecUtils::updateOutputList(outputSpecs, {"TST", "DATA1", 0}); + CHECK(outputSpecs.size() == 1); + + // Adding a different spec should increase the size + DataSpecUtils::updateOutputList(outputSpecs, {"TST", "DATA2", 0}); + CHECK(outputSpecs.size() == 2); + + // Adding again the same spec again should not change the size + DataSpecUtils::updateOutputList(outputSpecs, {"TST", "DATA1", 0}); + CHECK(outputSpecs.size() == 2); + // Check with something real.. + ConcreteDataMatcher dstf{"FLP", "DISTSUBTIMEFRAME", 0xccdb}; + DataSpecUtils::updateOutputList(outputSpecs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); + CHECK(outputSpecs.size() == 3); + DataSpecUtils::updateOutputList(outputSpecs, OutputSpec{{"ccdb-diststf"}, dstf, Lifetime::Timeframe}); + CHECK(outputSpecs.size() == 3); + // Label does not matter + DataSpecUtils::updateOutputList(outputSpecs, OutputSpec{{"ccdb2-diststf"}, dstf, Lifetime::Timeframe}); + CHECK(outputSpecs.size() == 3); +} diff --git a/Framework/Foundation/3rdparty/catch2/catch_amalgamated.cxx b/Framework/Foundation/3rdparty/catch2/catch_amalgamated.cxx index 0e12bd1438af3..eba3f00ac4868 100644 --- a/Framework/Foundation/3rdparty/catch2/catch_amalgamated.cxx +++ b/Framework/Foundation/3rdparty/catch2/catch_amalgamated.cxx @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.1 -// Generated: 2023-01-29 22:55:05.183536 +// Catch v3.4.0 +// Generated: 2023-07-13 13:23:09.554273 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -60,6 +60,7 @@ namespace Catch { + #include namespace Catch { @@ -88,7 +89,7 @@ namespace Catch { #include #include -#include +#include #include @@ -96,117 +97,172 @@ namespace Catch { #include #endif -namespace { +namespace Catch { + namespace Benchmark { + namespace Detail { + namespace { + + template + static sample + resample( URng& rng, + unsigned int resamples, + std::vector::const_iterator first, + std::vector::const_iterator last, + Estimator& estimator ) { + auto n = static_cast( last - first ); + std::uniform_int_distribution dist( 0, + n - 1 ); + + sample out; + out.reserve( resamples ); + // We allocate the vector outside the loop to avoid realloc + // per resample + std::vector resampled; + resampled.reserve( n ); + for ( size_t i = 0; i < resamples; ++i ) { + resampled.clear(); + for ( size_t s = 0; s < n; ++s ) { + resampled.push_back( + first[static_cast( + dist( rng ) )] ); + } + const auto estimate = + estimator( resampled.begin(), resampled.end() ); + out.push_back( estimate ); + } + std::sort( out.begin(), out.end() ); + return out; + } -using Catch::Benchmark::Detail::sample; - - template - sample resample(URng& rng, unsigned int resamples, std::vector::iterator first, std::vector::iterator last, Estimator& estimator) { - auto n = static_cast(last - first); - std::uniform_int_distribution dist(0, n - 1); - - sample out; - out.reserve(resamples); - std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] { - std::vector resampled; - resampled.reserve(n); - std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast(dist(rng))]; }); - return estimator(resampled.begin(), resampled.end()); - }); - std::sort(out.begin(), out.end()); - return out; - } - - - double erf_inv(double x) { - // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 - double w, p; - - w = -log((1.0 - x) * (1.0 + x)); - - if (w < 6.250000) { - w = w - 3.125000; - p = -3.6444120640178196996e-21; - p = -1.685059138182016589e-19 + p * w; - p = 1.2858480715256400167e-18 + p * w; - p = 1.115787767802518096e-17 + p * w; - p = -1.333171662854620906e-16 + p * w; - p = 2.0972767875968561637e-17 + p * w; - p = 6.6376381343583238325e-15 + p * w; - p = -4.0545662729752068639e-14 + p * w; - p = -8.1519341976054721522e-14 + p * w; - p = 2.6335093153082322977e-12 + p * w; - p = -1.2975133253453532498e-11 + p * w; - p = -5.4154120542946279317e-11 + p * w; - p = 1.051212273321532285e-09 + p * w; - p = -4.1126339803469836976e-09 + p * w; - p = -2.9070369957882005086e-08 + p * w; - p = 4.2347877827932403518e-07 + p * w; - p = -1.3654692000834678645e-06 + p * w; - p = -1.3882523362786468719e-05 + p * w; - p = 0.0001867342080340571352 + p * w; - p = -0.00074070253416626697512 + p * w; - p = -0.0060336708714301490533 + p * w; - p = 0.24015818242558961693 + p * w; - p = 1.6536545626831027356 + p * w; - } else if (w < 16.000000) { - w = sqrt(w) - 3.250000; - p = 2.2137376921775787049e-09; - p = 9.0756561938885390979e-08 + p * w; - p = -2.7517406297064545428e-07 + p * w; - p = 1.8239629214389227755e-08 + p * w; - p = 1.5027403968909827627e-06 + p * w; - p = -4.013867526981545969e-06 + p * w; - p = 2.9234449089955446044e-06 + p * w; - p = 1.2475304481671778723e-05 + p * w; - p = -4.7318229009055733981e-05 + p * w; - p = 6.8284851459573175448e-05 + p * w; - p = 2.4031110387097893999e-05 + p * w; - p = -0.0003550375203628474796 + p * w; - p = 0.00095328937973738049703 + p * w; - p = -0.0016882755560235047313 + p * w; - p = 0.0024914420961078508066 + p * w; - p = -0.0037512085075692412107 + p * w; - p = 0.005370914553590063617 + p * w; - p = 1.0052589676941592334 + p * w; - p = 3.0838856104922207635 + p * w; - } else { - w = sqrt(w) - 5.000000; - p = -2.7109920616438573243e-11; - p = -2.5556418169965252055e-10 + p * w; - p = 1.5076572693500548083e-09 + p * w; - p = -3.7894654401267369937e-09 + p * w; - p = 7.6157012080783393804e-09 + p * w; - p = -1.4960026627149240478e-08 + p * w; - p = 2.9147953450901080826e-08 + p * w; - p = -6.7711997758452339498e-08 + p * w; - p = 2.2900482228026654717e-07 + p * w; - p = -9.9298272942317002539e-07 + p * w; - p = 4.5260625972231537039e-06 + p * w; - p = -1.9681778105531670567e-05 + p * w; - p = 7.5995277030017761139e-05 + p * w; - p = -0.00021503011930044477347 + p * w; - p = -0.00013871931833623122026 + p * w; - p = 1.0103004648645343977 + p * w; - p = 4.8499064014085844221 + p * w; - } - return p * x; - } - - double standard_deviation(std::vector::iterator first, std::vector::iterator last) { - auto m = Catch::Benchmark::Detail::mean(first, last); - double variance = std::accumulate( first, - last, - 0., - [m]( double a, double b ) { - double diff = b - m; - return a + diff * diff; - } ) / - ( last - first ); - return std::sqrt( variance ); - } + static double outlier_variance( Estimate mean, + Estimate stddev, + int n ) { + double sb = stddev.point; + double mn = mean.point / n; + double mg_min = mn / 2.; + double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) ); + double sg2 = sg * sg; + double sb2 = sb * sb; + + auto c_max = [n, mn, sb2, sg2]( double x ) -> double { + double k = mn - x; + double d = k * k; + double nd = n * d; + double k0 = -n * nd; + double k1 = sb2 - n * sg2 + nd; + double det = k1 * k1 - 4 * sg2 * k0; + return static_cast( -2. * k0 / + ( k1 + std::sqrt( det ) ) ); + }; + + auto var_out = [n, sb2, sg2]( double c ) { + double nc = n - c; + return ( nc / n ) * ( sb2 - nc * sg2 ); + }; + + return (std::min)( var_out( 1 ), + var_out( + (std::min)( c_max( 0. ), + c_max( mg_min ) ) ) ) / + sb2; + } -} + static double erf_inv( double x ) { + // Code accompanying the article "Approximating the erfinv + // function" in GPU Computing Gems, Volume 2 + double w, p; + + w = -log( ( 1.0 - x ) * ( 1.0 + x ) ); + + if ( w < 6.250000 ) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p * w; + p = 1.2858480715256400167e-18 + p * w; + p = 1.115787767802518096e-17 + p * w; + p = -1.333171662854620906e-16 + p * w; + p = 2.0972767875968561637e-17 + p * w; + p = 6.6376381343583238325e-15 + p * w; + p = -4.0545662729752068639e-14 + p * w; + p = -8.1519341976054721522e-14 + p * w; + p = 2.6335093153082322977e-12 + p * w; + p = -1.2975133253453532498e-11 + p * w; + p = -5.4154120542946279317e-11 + p * w; + p = 1.051212273321532285e-09 + p * w; + p = -4.1126339803469836976e-09 + p * w; + p = -2.9070369957882005086e-08 + p * w; + p = 4.2347877827932403518e-07 + p * w; + p = -1.3654692000834678645e-06 + p * w; + p = -1.3882523362786468719e-05 + p * w; + p = 0.0001867342080340571352 + p * w; + p = -0.00074070253416626697512 + p * w; + p = -0.0060336708714301490533 + p * w; + p = 0.24015818242558961693 + p * w; + p = 1.6536545626831027356 + p * w; + } else if ( w < 16.000000 ) { + w = sqrt( w ) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p * w; + p = -2.7517406297064545428e-07 + p * w; + p = 1.8239629214389227755e-08 + p * w; + p = 1.5027403968909827627e-06 + p * w; + p = -4.013867526981545969e-06 + p * w; + p = 2.9234449089955446044e-06 + p * w; + p = 1.2475304481671778723e-05 + p * w; + p = -4.7318229009055733981e-05 + p * w; + p = 6.8284851459573175448e-05 + p * w; + p = 2.4031110387097893999e-05 + p * w; + p = -0.0003550375203628474796 + p * w; + p = 0.00095328937973738049703 + p * w; + p = -0.0016882755560235047313 + p * w; + p = 0.0024914420961078508066 + p * w; + p = -0.0037512085075692412107 + p * w; + p = 0.005370914553590063617 + p * w; + p = 1.0052589676941592334 + p * w; + p = 3.0838856104922207635 + p * w; + } else { + w = sqrt( w ) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p * w; + p = 1.5076572693500548083e-09 + p * w; + p = -3.7894654401267369937e-09 + p * w; + p = 7.6157012080783393804e-09 + p * w; + p = -1.4960026627149240478e-08 + p * w; + p = 2.9147953450901080826e-08 + p * w; + p = -6.7711997758452339498e-08 + p * w; + p = 2.2900482228026654717e-07 + p * w; + p = -9.9298272942317002539e-07 + p * w; + p = 4.5260625972231537039e-06 + p * w; + p = -1.9681778105531670567e-05 + p * w; + p = 7.5995277030017761139e-05 + p * w; + p = -0.00021503011930044477347 + p * w; + p = -0.00013871931833623122026 + p * w; + p = 1.0103004648645343977 + p * w; + p = 4.8499064014085844221 + p * w; + } + return p * x; + } + + static double + standard_deviation( std::vector::const_iterator first, + std::vector::const_iterator last ) { + auto m = Catch::Benchmark::Detail::mean( first, last ); + double variance = + std::accumulate( first, + last, + 0., + [m]( double a, double b ) { + double diff = b - m; + return a + diff * diff; + } ) / + ( last - first ); + return std::sqrt( variance ); + } + + } // namespace + } // namespace Detail + } // namespace Benchmark +} // namespace Catch namespace Catch { namespace Benchmark { @@ -236,6 +292,47 @@ namespace Catch { return xj + g * (xj1 - xj); } + OutlierClassification + classify_outliers( std::vector::const_iterator first, + std::vector::const_iterator last ) { + std::vector copy( first, last ); + + auto q1 = weighted_average_quantile( 1, 4, copy.begin(), copy.end() ); + auto q3 = weighted_average_quantile( 3, 4, copy.begin(), copy.end() ); + auto iqr = q3 - q1; + auto los = q1 - ( iqr * 3. ); + auto lom = q1 - ( iqr * 1.5 ); + auto him = q3 + ( iqr * 1.5 ); + auto his = q3 + ( iqr * 3. ); + + OutlierClassification o; + for ( ; first != last; ++first ) { + const double t = *first; + if ( t < los ) { + ++o.low_severe; + } else if ( t < lom ) { + ++o.low_mild; + } else if ( t > his ) { + ++o.high_severe; + } else if ( t > him ) { + ++o.high_mild; + } + ++o.samples_seen; + } + return o; + } + + double mean( std::vector::const_iterator first, + std::vector::const_iterator last ) { + auto count = last - first; + double sum = 0.; + while (first != last) { + sum += *first; + ++first; + } + return sum / static_cast(count); + } + double erfc_inv(double x) { return erf_inv(1.0 - x); @@ -257,35 +354,10 @@ namespace Catch { return result; } - - double outlier_variance(Estimate mean, Estimate stddev, int n) { - double sb = stddev.point; - double mn = mean.point / n; - double mg_min = mn / 2.; - double sg = (std::min)(mg_min / 4., sb / std::sqrt(n)); - double sg2 = sg * sg; - double sb2 = sb * sb; - - auto c_max = [n, mn, sb2, sg2](double x) -> double { - double k = mn - x; - double d = k * k; - double nd = n * d; - double k0 = -n * nd; - double k1 = sb2 - n * sg2 + nd; - double det = k1 * k1 - 4 * sg2 * k0; - return static_cast(-2. * k0 / (k1 + std::sqrt(det))); - }; - - auto var_out = [n, sb2, sg2](double c) { - double nc = n - c; - return (nc / n) * (sb2 - nc * sg2); - }; - - return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2; - } - - - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last) { + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + std::vector::iterator first, + std::vector::iterator last) { CATCH_INTERNAL_START_WARNINGS_SUPPRESSION CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS static std::random_device entropy; @@ -293,11 +365,12 @@ namespace Catch { auto n = static_cast(last - first); // seriously, one can't use integral types without hell in C++ - auto mean = &Detail::mean::iterator>; + auto mean = &Detail::mean; auto stddev = &standard_deviation; #if defined(CATCH_CONFIG_USE_ASYNC) - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { + auto Estimate = [=](double(*f)(std::vector::const_iterator, + std::vector::const_iterator)) { auto seed = entropy(); return std::async(std::launch::async, [=] { std::mt19937 rng(seed); @@ -312,7 +385,8 @@ namespace Catch { auto mean_estimate = mean_future.get(); auto stddev_estimate = stddev_future.get(); #else - auto Estimate = [=](double(*f)(std::vector::iterator, std::vector::iterator)) { + auto Estimate = [=](double(*f)(std::vector::const_iterator, + std::vector::const_iterator)) { auto seed = entropy(); std::mt19937 rng(seed); auto resampled = resample(rng, n_resamples, first, last, f); @@ -469,16 +543,15 @@ namespace Catch { } std::string AssertionResult::getExpressionInMacro() const { - std::string expr; - if( m_info.macroName.empty() ) - expr = static_cast(m_info.capturedExpression); - else { - expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); - expr += m_info.macroName; - expr += "( "; - expr += m_info.capturedExpression; - expr += " )"; + if ( m_info.macroName.empty() ) { + return static_cast( m_info.capturedExpression ); } + std::string expr; + expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 ); + expr += m_info.macroName; + expr += "( "; + expr += m_info.capturedExpression; + expr += " )"; return expr; } @@ -597,7 +670,7 @@ namespace Catch { elem = trim(elem); } - // Insert the default reporter if user hasn't asked for a specfic one + // Insert the default reporter if user hasn't asked for a specific one if ( m_data.reporterSpecifications.empty() ) { m_data.reporterSpecifications.push_back( { #if defined( CATCH_CONFIG_DEFAULT_REPORTER ) @@ -776,7 +849,11 @@ namespace Catch { } - Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) { + Capturer::Capturer( StringRef macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType resultType, + StringRef names ): + m_resultCapture( getResultCapture() ) { auto trimmed = [&] (size_t start, size_t end) { while (names[start] == ',' || isspace(static_cast(names[start]))) { ++start; @@ -853,6 +930,8 @@ namespace Catch { +#include + namespace Catch { namespace { @@ -863,7 +942,7 @@ namespace Catch { public: // IRegistryHub RegistryHub() = default; - IReporterRegistry const& getReporterRegistry() const override { + ReporterRegistry const& getReporterRegistry() const override { return m_reporterRegistry; } ITestCaseRegistry const& getTestCaseRegistry() const override { @@ -939,6 +1018,7 @@ namespace Catch { #include #include +#include #include #include @@ -1421,12 +1501,20 @@ namespace Catch { for (size_t idx = 0; idx < originalTags.size(); ++idx) { auto c = originalTags[idx]; if (c == '[') { - assert(!inTag); + CATCH_ENFORCE( + !inTag, + "Found '[' inside a tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = true; tagStart = idx; } if (c == ']') { - assert(inTag); + CATCH_ENFORCE( + inTag, + "Found unmatched ']' while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + inTag = false; tagEnd = idx; assert(tagStart < tagEnd); @@ -1435,7 +1523,11 @@ namespace Catch { // it over to backing storage and actually reference the // backing storage in the saved tags StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1); - CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed"); + CATCH_ENFORCE( !tagStr.empty(), + "Found an empty tag while registering test case '" + << _nameAndTags.name << "' at " + << _lineInfo ); + enforceNotReservedTag(tagStr, lineInfo); properties |= parseSpecialTag(tagStr); // When copying a tag to the backing storage, we need to @@ -1449,8 +1541,12 @@ namespace Catch { // the tags. internalAppendTag(tagStr); } - (void)inTag; // Silence "set-but-unused" warning in release mode. } + CATCH_ENFORCE( !inTag, + "Found an unclosed tag while registering test case '" + << _nameAndTags.name << "' at " << _lineInfo ); + + // Add [.] if relevant if (isHidden()) { internalAppendTag("."_sr); @@ -1626,16 +1722,18 @@ namespace Catch { return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } ); } - TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const - { - Matches matches( m_filters.size() ); - std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){ + TestSpec::Matches TestSpec::matchesByFilter( std::vector const& testCases, IConfig const& config ) const { + Matches matches; + matches.reserve( m_filters.size() ); + for ( auto const& filter : m_filters ) { std::vector currentMatches; - for( auto const& test : testCases ) - if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) ) + for ( auto const& test : testCases ) + if ( isThrowSafe( test, config ) && + filter.matches( test.getTestCaseInfo() ) ) currentMatches.emplace_back( &test ); - return FilterMatch{ extractFilterName(filter), currentMatches }; - } ); + matches.push_back( + FilterMatch{ extractFilterName( filter ), currentMatches } ); + } return matches; } @@ -1992,6 +2090,19 @@ namespace Catch { } + + +namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ) { + getMutableRegistryHub().registerTranslator( + CATCH_MOVE( translator ) ); + } + } // namespace Detail +} // namespace Catch + + #include namespace Catch { @@ -2022,7 +2133,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 3, 1, "", 0 ); + static Version version( 3, 4, 0, "", 0 ); return version; } @@ -2173,8 +2284,6 @@ namespace Catch { infoMessages( _infoMessages ), totals( _totals ) { - assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression; - if( assertionResult.hasMessage() ) { // Copy message into messages list. // !TBD This should have been done earlier, somewhere @@ -2198,13 +2307,13 @@ namespace Catch { TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, + std::string&& _stdOut, + std::string&& _stdErr, bool _aborting ) : testInfo( &_testInfo ), totals( _totals ), - stdOut( _stdOut ), - stdErr( _stdErr ), + stdOut( CATCH_MOVE(_stdOut) ), + stdErr( CATCH_MOVE(_stdErr) ), aborting( _aborting ) {} @@ -2233,14 +2342,6 @@ namespace Catch { namespace Catch { - IReporterRegistry::~IReporterRegistry() = default; -} - - - - -namespace Catch { - ITestInvoker::~ITestInvoker() = default; ITestCaseRegistry::~ITestCaseRegistry() = default; } @@ -2255,7 +2356,9 @@ namespace Catch { ResultDisposition::Flags resultDisposition ) : m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition }, m_resultCapture( getResultCapture() ) - {} + { + m_resultCapture.notifyAssertionStarted( m_assertionInfo ); + } void AssertionHandler::handleExpr( ITransientExpression const& expr ) { m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction ); @@ -2269,7 +2372,7 @@ namespace Catch { } void AssertionHandler::complete() { - setCompleted(); + m_completed = true; if( m_reaction.shouldDebugBreak ) { // If you find your debugger stopping you here then go one level up on the @@ -2282,16 +2385,9 @@ namespace Catch { throw_test_failure_exception(); } if ( m_reaction.shouldSkip ) { -#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) - throw Catch::TestSkipException(); -#else - CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); -#endif + throw_test_skip_exception(); } } - void AssertionHandler::setCompleted() { - m_completed = true; - } void AssertionHandler::handleUnexpectedInflightException() { m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); @@ -2918,7 +3014,7 @@ namespace Catch { auto const& reporterSpec = *parsed; - IReporterRegistry::FactoryMap const& factories = + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); auto result = factories.find( reporterSpec.name() ); @@ -3156,7 +3252,7 @@ namespace Catch { namespace { //! A do-nothing implementation of colour, used as fallback for unknown //! platforms, and when the user asks to deactivate all colours. - class NoColourImpl : public ColourImpl { + class NoColourImpl final : public ColourImpl { public: NoColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3174,7 +3270,7 @@ namespace Catch { namespace Catch { namespace { - class Win32ColourImpl : public ColourImpl { + class Win32ColourImpl final : public ColourImpl { public: Win32ColourImpl(IStream* stream): ColourImpl(stream) { @@ -3240,7 +3336,7 @@ namespace { namespace Catch { namespace { - class ANSIColourImpl : public ColourImpl { + class ANSIColourImpl final : public ColourImpl { public: ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {} @@ -3356,49 +3452,27 @@ namespace Catch { namespace Catch { - class Context : public IMutableContext, private Detail::NonCopyable { - - public: // IContext - IResultCapture* getResultCapture() override { - return m_resultCapture; - } - - IConfig const* getConfig() const override { - return m_config; - } - - ~Context() override; - - public: // IMutableContext - void setResultCapture( IResultCapture* resultCapture ) override { - m_resultCapture = resultCapture; - } - void setConfig( IConfig const* config ) override { - m_config = config; - } - - friend IMutableContext& getCurrentMutableContext(); - - private: - IConfig const* m_config = nullptr; - IResultCapture* m_resultCapture = nullptr; - }; - - IMutableContext *IMutableContext::currentContext = nullptr; + Context* Context::currentContext = nullptr; - void IMutableContext::createContext() - { + void cleanUpContext() { + delete Context::currentContext; + Context::currentContext = nullptr; + } + void Context::createContext() { currentContext = new Context(); } - void cleanUpContext() { - delete IMutableContext::currentContext; - IMutableContext::currentContext = nullptr; + Context& getCurrentMutableContext() { + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; + } + + void Context::setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; } - IContext::~IContext() = default; - IMutableContext::~IMutableContext() = default; - Context::~Context() = default; + void Context::setConfig( IConfig const* config ) { m_config = config; } SimplePcg32& sharedRng() { static SimplePcg32 s_rng; @@ -3681,8 +3755,24 @@ namespace Catch { +#include + namespace Catch { + namespace { + static std::string tryTranslators( + std::vector< + Detail::unique_ptr> const& translators ) { + if ( translators.empty() ) { + std::rethrow_exception( std::current_exception() ); + } else { + return translators[0]->translate( translators.begin() + 1, + translators.end() ); + } + } + + } + ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { } @@ -3707,7 +3797,7 @@ namespace Catch { // First we try user-registered translators. If none of them can // handle the exception, it will be rethrown handled by our defaults. try { - return tryTranslators(); + return tryTranslators(m_translators); } // To avoid having to handle TFE explicitly everywhere, we just // rethrow it so that it goes back up the caller. @@ -3731,25 +3821,12 @@ namespace Catch { } } - std::string ExceptionTranslatorRegistry::tryTranslators() const { - if (m_translators.empty()) { - std::rethrow_exception(std::current_exception()); - } else { - return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end()); - } - } - #else // ^^ Exceptions are enabled // Exceptions are disabled vv std::string ExceptionTranslatorRegistry::translateActiveException() const { CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); } - - std::string ExceptionTranslatorRegistry::tryTranslators() const { - CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!"); - } #endif - } @@ -4054,7 +4131,7 @@ namespace Catch { namespace Detail { namespace { template - class StreamBufImpl : public std::streambuf { + class StreamBufImpl final : public std::streambuf { char data[bufferSize]; WriterF m_writer; @@ -4102,7 +4179,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class FileStream : public IStream { + class FileStream final : public IStream { std::ofstream m_ofs; public: FileStream( std::string const& filename ) { @@ -4119,7 +4196,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class CoutStream : public IStream { + class CoutStream final : public IStream { std::ostream m_os; public: // Store the streambuf from cout up-front because @@ -4148,7 +4225,7 @@ namespace Detail { /////////////////////////////////////////////////////////////////////////// - class DebugOutStream : public IStream { + class DebugOutStream final : public IStream { Detail::unique_ptr> m_streamBuf; std::ostream m_os; public: @@ -4278,7 +4355,7 @@ namespace Catch { void listReporters(IEventListener& reporter) { std::vector descriptions; - IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + auto const& factories = getRegistryHub().getReporterRegistry().getFactories(); descriptions.reserve(factories.size()); for (auto const& fac : factories) { descriptions.push_back({ fac.first, fac.second->getDescription() }); @@ -4697,49 +4774,71 @@ namespace Catch { namespace Catch { + struct ReporterRegistry::ReporterRegistryImpl { + std::vector> listeners; + std::map + factories; + }; - ReporterRegistry::ReporterRegistry() { + ReporterRegistry::ReporterRegistry(): + m_impl( Detail::make_unique() ) { // Because it is impossible to move out of initializer list, // we have to add the elements manually - m_factories["Automake"] = Detail::make_unique>(); - m_factories["compact"] = Detail::make_unique>(); - m_factories["console"] = Detail::make_unique>(); - m_factories["JUnit"] = Detail::make_unique>(); - m_factories["SonarQube"] = Detail::make_unique>(); - m_factories["TAP"] = Detail::make_unique>(); - m_factories["TeamCity"] = Detail::make_unique>(); - m_factories["XML"] = Detail::make_unique>(); + m_impl->factories["Automake"] = + Detail::make_unique>(); + m_impl->factories["compact"] = + Detail::make_unique>(); + m_impl->factories["console"] = + Detail::make_unique>(); + m_impl->factories["JUnit"] = + Detail::make_unique>(); + m_impl->factories["SonarQube"] = + Detail::make_unique>(); + m_impl->factories["TAP"] = + Detail::make_unique>(); + m_impl->factories["TeamCity"] = + Detail::make_unique>(); + m_impl->factories["XML"] = + Detail::make_unique>(); } ReporterRegistry::~ReporterRegistry() = default; - - IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const { - auto it = m_factories.find( name ); - if( it == m_factories.end() ) - return nullptr; - return it->second->create( CATCH_MOVE(config) ); + IEventListenerPtr + ReporterRegistry::create( std::string const& name, + ReporterConfig&& config ) const { + auto it = m_impl->factories.find( name ); + if ( it == m_impl->factories.end() ) return nullptr; + return it->second->create( CATCH_MOVE( config ) ); } - void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) { + void ReporterRegistry::registerReporter( std::string const& name, + IReporterFactoryPtr factory ) { CATCH_ENFORCE( name.find( "::" ) == name.npos, - "'::' is not allowed in reporter name: '" + name + '\'' ); - auto ret = m_factories.emplace(name, CATCH_MOVE(factory)); - CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" ); + "'::' is not allowed in reporter name: '" + name + + '\'' ); + auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) ); + CATCH_ENFORCE( ret.second, + "reporter using '" + name + + "' as name was already registered" ); } void ReporterRegistry::registerListener( Detail::unique_ptr factory ) { - m_listeners.push_back( CATCH_MOVE(factory) ); + m_impl->listeners.push_back( CATCH_MOVE( factory ) ); } - IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const { - return m_factories; - } - IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const { - return m_listeners; + std::map const& + ReporterRegistry::getFactories() const { + return m_impl->factories; } -} + std::vector> const& + ReporterRegistry::getListeners() const { + return m_impl->listeners; + } +} // namespace Catch @@ -4755,9 +4854,9 @@ namespace Catch { }; kvPair splitKVPair(StringRef kvString) { - auto splitPos = static_cast( std::distance( - kvString.begin(), - std::find( kvString.begin(), kvString.end(), '=' ) ) ); + auto splitPos = static_cast( + std::find( kvString.begin(), kvString.end(), '=' ) - + kvString.begin() ); return { kvString.substr( 0, splitPos ), kvString.substr( splitPos + 1, kvString.size() ) }; @@ -4989,146 +5088,152 @@ namespace Catch { namespace Catch { namespace Generators { - struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker { - GeneratorBasePtr m_generator; + namespace { + struct GeneratorTracker final : TestCaseTracking::TrackerBase, + IGeneratorTracker { + GeneratorBasePtr m_generator; + + GeneratorTracker( + TestCaseTracking::NameAndLocation&& nameAndLocation, + TrackerContext& ctx, + ITracker* parent ): + TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {} + ~GeneratorTracker() override = default; + + static GeneratorTracker* + acquire( TrackerContext& ctx, + TestCaseTracking::NameAndLocationRef const& + nameAndLocation ) { + GeneratorTracker* tracker; + + ITracker& currentTracker = ctx.currentTracker(); + // Under specific circumstances, the generator we want + // to acquire is also the current tracker. If this is + // the case, we have to avoid looking through current + // tracker's children, and instead return the current + // tracker. + // A case where this check is important is e.g. + // for (int i = 0; i < 5; ++i) { + // int n = GENERATE(1, 2); + // } + // + // without it, the code above creates 5 nested generators. + if ( currentTracker.nameAndLocation() == nameAndLocation ) { + auto thisTracker = currentTracker.parent()->findChild( + nameAndLocation ); + assert( thisTracker ); + assert( thisTracker->isGeneratorTracker() ); + tracker = static_cast( thisTracker ); + } else if ( ITracker* childTracker = + currentTracker.findChild( + nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isGeneratorTracker() ); + tracker = + static_cast( childTracker ); + } else { + return nullptr; + } - GeneratorTracker( TestCaseTracking::NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) - : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ) - {} - ~GeneratorTracker() override; - - static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocationRef nameAndLocation ) { - GeneratorTracker* tracker; - - ITracker& currentTracker = ctx.currentTracker(); - // Under specific circumstances, the generator we want - // to acquire is also the current tracker. If this is - // the case, we have to avoid looking through current - // tracker's children, and instead return the current - // tracker. - // A case where this check is important is e.g. - // for (int i = 0; i < 5; ++i) { - // int n = GENERATE(1, 2); - // } - // - // without it, the code above creates 5 nested generators. - if ( currentTracker.nameAndLocation() == nameAndLocation ) { - auto thisTracker = - currentTracker.parent()->findChild( nameAndLocation ); - assert( thisTracker ); - assert( thisTracker->isGeneratorTracker() ); - tracker = static_cast( thisTracker ); - } else if ( ITracker* childTracker = - currentTracker.findChild( nameAndLocation ) ) { - assert( childTracker ); - assert( childTracker->isGeneratorTracker() ); - tracker = static_cast( childTracker ); - } else { - return nullptr; - } + if ( !tracker->isComplete() ) { tracker->open(); } - if( !tracker->isComplete() ) { - tracker->open(); + return tracker; } - return tracker; - } - - // TrackerBase interface - bool isGeneratorTracker() const override { return true; } - auto hasGenerator() const -> bool override { - return !!m_generator; - } - void close() override { - TrackerBase::close(); - // If a generator has a child (it is followed by a section) - // and none of its children have started, then we must wait - // until later to start consuming its values. - // This catches cases where `GENERATE` is placed between two - // `SECTION`s. - // **The check for m_children.empty cannot be removed**. - // doing so would break `GENERATE` _not_ followed by `SECTION`s. - const bool should_wait_for_child = [&]() { - // No children -> nobody to wait for - if ( m_children.empty() ) { - return false; - } - // If at least one child started executing, don't wait - if ( std::find_if( - m_children.begin(), - m_children.end(), - []( TestCaseTracking::ITrackerPtr const& tracker ) { - return tracker->hasStarted(); - } ) != m_children.end() ) { - return false; - } - - // No children have started. We need to check if they _can_ - // start, and thus we should wait for them, or they cannot - // start (due to filters), and we shouldn't wait for them - ITracker* parent = m_parent; - // This is safe: there is always at least one section - // tracker in a test case tracking tree - while ( !parent->isSectionTracker() ) { - parent = parent->parent(); - } - assert( parent && - "Missing root (test case) level section" ); - - auto const& parentSection = - static_cast( *parent ); - auto const& filters = parentSection.getFilters(); - // No filters -> no restrictions on running sections - if ( filters.empty() ) { - return true; - } + // TrackerBase interface + bool isGeneratorTracker() const override { return true; } + auto hasGenerator() const -> bool override { + return !!m_generator; + } + void close() override { + TrackerBase::close(); + // If a generator has a child (it is followed by a section) + // and none of its children have started, then we must wait + // until later to start consuming its values. + // This catches cases where `GENERATE` is placed between two + // `SECTION`s. + // **The check for m_children.empty cannot be removed**. + // doing so would break `GENERATE` _not_ followed by + // `SECTION`s. + const bool should_wait_for_child = [&]() { + // No children -> nobody to wait for + if ( m_children.empty() ) { return false; } + // If at least one child started executing, don't wait + if ( std::find_if( + m_children.begin(), + m_children.end(), + []( TestCaseTracking::ITrackerPtr const& + tracker ) { + return tracker->hasStarted(); + } ) != m_children.end() ) { + return false; + } - for ( auto const& child : m_children ) { - if ( child->isSectionTracker() && - std::find( - filters.begin(), - filters.end(), - static_cast( *child ) - .trimmedName() ) != filters.end() ) { - return true; + // No children have started. We need to check if they + // _can_ start, and thus we should wait for them, or + // they cannot start (due to filters), and we shouldn't + // wait for them + ITracker* parent = m_parent; + // This is safe: there is always at least one section + // tracker in a test case tracking tree + while ( !parent->isSectionTracker() ) { + parent = parent->parent(); + } + assert( parent && + "Missing root (test case) level section" ); + + auto const& parentSection = + static_cast( *parent ); + auto const& filters = parentSection.getFilters(); + // No filters -> no restrictions on running sections + if ( filters.empty() ) { return true; } + + for ( auto const& child : m_children ) { + if ( child->isSectionTracker() && + std::find( filters.begin(), + filters.end(), + static_cast( + *child ) + .trimmedName() ) != + filters.end() ) { + return true; + } } + return false; + }(); + + // This check is a bit tricky, because m_generator->next() + // has a side-effect, where it consumes generator's current + // value, but we do not want to invoke the side-effect if + // this generator is still waiting for any child to start. + assert( m_generator && "Tracker without generator" ); + if ( should_wait_for_child || + ( m_runState == CompletedSuccessfully && + m_generator->countedNext() ) ) { + m_children.clear(); + m_runState = Executing; } - return false; - }(); - - // This check is a bit tricky, because m_generator->next() - // has a side-effect, where it consumes generator's current - // value, but we do not want to invoke the side-effect if - // this generator is still waiting for any child to start. - assert( m_generator && "Tracker without generator" ); - if ( should_wait_for_child || - ( m_runState == CompletedSuccessfully && - m_generator->countedNext() ) ) { - m_children.clear(); - m_runState = Executing; } - } - // IGeneratorTracker interface - auto getGenerator() const -> GeneratorBasePtr const& override { - return m_generator; - } - void setGenerator( GeneratorBasePtr&& generator ) override { - m_generator = CATCH_MOVE( generator ); - } - }; - GeneratorTracker::~GeneratorTracker() = default; + // IGeneratorTracker interface + auto getGenerator() const -> GeneratorBasePtr const& override { + return m_generator; + } + void setGenerator( GeneratorBasePtr&& generator ) override { + m_generator = CATCH_MOVE( generator ); + } + }; + } // namespace } RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter) : m_runInfo(_config->name()), - m_context(getCurrentMutableContext()), m_config(_config), m_reporter(CATCH_MOVE(reporter)), m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal }, m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions ) { - m_context.setResultCapture(this); + getCurrentMutableContext().setResultCapture( this ); m_reporter->testRunStarting(m_runInfo); } @@ -5139,13 +5244,8 @@ namespace Catch { Totals RunContext::runTest(TestCaseHandle const& testCase) { const Totals prevTotals = m_totals; - std::string redirectedCout; - std::string redirectedCerr; - auto const& testInfo = testCase.getTestCaseInfo(); - m_reporter->testCaseStarting(testInfo); - m_activeTestCase = &testCase; @@ -5187,6 +5287,8 @@ namespace Catch { seedRng( *m_config ); uint64_t testRuns = 0; + std::string redirectedCout; + std::string redirectedCerr; do { m_trackerContext.startCycle(); m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo)); @@ -5200,7 +5302,7 @@ namespace Catch { redirectedCerr += oneRunCerr; const auto singleRunTotals = m_totals.delta(beforeRunTotals); - auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting()); + auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting()); m_reporter->testCasePartialEnded(statsForOneRun, testRuns); ++testRuns; @@ -5215,8 +5317,8 @@ namespace Catch { m_totals.testCases += deltaTotals.testCases; m_reporter->testCaseEnded(TestCaseStats(testInfo, deltaTotals, - redirectedCout, - redirectedCerr, + CATCH_MOVE(redirectedCout), + CATCH_MOVE(redirectedCerr), aborting())); m_activeTestCase = nullptr; @@ -5226,7 +5328,7 @@ namespace Catch { } - void RunContext::assertionEnded(AssertionResult const & result) { + void RunContext::assertionEnded(AssertionResult&& result) { if (result.getResultType() == ResultWas::Ok) { m_totals.assertions.passed++; m_lastAssertionPassed = true; @@ -5248,19 +5350,26 @@ namespace Catch { m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)); - if (result.getResultType() != ResultWas::Warning) + if ( result.getResultType() != ResultWas::Warning ) { m_messageScopes.clear(); + } // Reset working state resetAssertionInfo(); - m_lastResult = result; + m_lastResult = CATCH_MOVE( result ); } void RunContext::resetAssertionInfo() { m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; } - bool RunContext::sectionStarted(StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts & assertions) { + void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { + m_reporter->assertionStarting( info ); + } + + bool RunContext::sectionStarted( StringRef sectionName, + SourceLineInfo const& sectionLineInfo, + Counts& assertions ) { ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocationRef( @@ -5398,7 +5507,7 @@ namespace Catch { tempResult.message = static_cast(message); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); - assertionEnded(result); + assertionEnded(CATCH_MOVE(result) ); handleUnfinishedSections(); @@ -5520,8 +5629,6 @@ namespace Catch { ITransientExpression const& expr, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - bool negated = isFalseTest( info.resultDisposition ); bool result = expr.getResult() != negated; @@ -5550,7 +5657,7 @@ namespace Catch { AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; assertionResult.m_resultData.lazyExpression.m_transientExpression = expr; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); } void RunContext::handleMessage( @@ -5559,16 +5666,16 @@ namespace Catch { StringRef message, AssertionReaction& reaction ) { - m_reporter->assertionStarting( info ); - m_lastAssertionInfo = info; AssertionResultData data( resultType, LazyExpression( false ) ); data.message = static_cast(message); AssertionResult assertionResult{ m_lastAssertionInfo, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if ( !assertionResult.isOk() ) { + + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } else if ( resultType == ResultWas::ExplicitSkip ) { // TODO: Need to handle this explicitly, as ExplicitSkip is @@ -5585,15 +5692,15 @@ namespace Catch { void RunContext::handleUnexpectedInflightException( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) { m_lastAssertionInfo = info; AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = message; + data.message = CATCH_MOVE(message); AssertionResult assertionResult{ info, CATCH_MOVE(data) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); populateReaction( reaction ); } @@ -5605,12 +5712,13 @@ namespace Catch { void RunContext::handleIncomplete( AssertionInfo const& info ) { + using namespace std::string_literals; m_lastAssertionInfo = info; AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) ); - data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"; + data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); + assertionEnded( CATCH_MOVE(assertionResult) ); } void RunContext::handleNonExpr( AssertionInfo const &info, @@ -5621,10 +5729,10 @@ namespace Catch { AssertionResultData data( resultType, LazyExpression( false ) ); AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; - assertionEnded( assertionResult ); - if( !assertionResult.isOk() ) - populateReaction( reaction ); + const auto isOk = assertionResult.isOk(); + assertionEnded( CATCH_MOVE(assertionResult) ); + if ( !isOk ) { populateReaction( reaction ); } } @@ -5663,7 +5771,7 @@ namespace Catch { Section::Section( SourceLineInfo const& _lineInfo, StringRef _name, const char* const ): - m_info( { "invalid", static_cast(-1) }, "" ), + m_info( { "invalid", static_cast( -1 ) }, std::string{} ), m_sectionIncluded( getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) { // We delay initialization the SectionInfo member until we know @@ -5793,7 +5901,6 @@ namespace Catch { -#include #include #include #include @@ -5817,9 +5924,9 @@ namespace Catch { return s.find( infix ) != std::string::npos; } void toLowerInPlace( std::string& s ) { - std::transform( s.begin(), s.end(), s.begin(), []( char c ) { - return toLower( c ); - } ); + for ( char& c : s ) { + c = toLower( c ); + } } std::string toLower( std::string const& s ) { std::string lc = s; @@ -5900,10 +6007,6 @@ namespace Catch { : StringRef( rawChars, std::strlen(rawChars) ) {} - auto StringRef::operator == ( StringRef other ) const noexcept -> bool { - return m_size == other.m_size - && (std::memcmp( m_start, other.m_start, m_size ) == 0); - } bool StringRef::operator<(StringRef rhs) const noexcept { if (m_size < rhs.m_size) { @@ -6037,6 +6140,38 @@ namespace Catch { namespace Catch { + namespace { + static void enforceNoDuplicateTestCases( + std::vector const& tests ) { + auto testInfoCmp = []( TestCaseInfo const* lhs, + TestCaseInfo const* rhs ) { + return *lhs < *rhs; + }; + std::set seenTests( + testInfoCmp ); + for ( auto const& test : tests ) { + const auto infoPtr = &test.getTestCaseInfo(); + const auto prev = seenTests.insert( infoPtr ); + CATCH_ENFORCE( prev.second, + "error: test case \"" + << infoPtr->name << "\", with tags \"" + << infoPtr->tagsAsString() + << "\" already defined.\n" + << "\tFirst seen at " + << ( *prev.first )->lineInfo << "\n" + << "\tRedefined at " << infoPtr->lineInfo ); + } + } + + static bool matchTest( TestCaseHandle const& testCase, + TestSpec const& testSpec, + IConfig const& config ) { + return testSpec.matches( testCase.getTestCaseInfo() ) && + isThrowSafe( testCase, config ); + } + + } // end unnamed namespace + std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { switch (config.runOrder()) { case TestRunOrder::Declared: @@ -6093,29 +6228,6 @@ namespace Catch { return !testCase.getTestCaseInfo().throws() || config.allowThrows(); } - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ) { - return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config ); - } - - void - enforceNoDuplicateTestCases( std::vector const& tests ) { - auto testInfoCmp = []( TestCaseInfo const* lhs, - TestCaseInfo const* rhs ) { - return *lhs < *rhs; - }; - std::set seenTests(testInfoCmp); - for ( auto const& test : tests ) { - const auto infoPtr = &test.getTestCaseInfo(); - const auto prev = seenTests.insert( infoPtr ); - CATCH_ENFORCE( - prev.second, - "error: test case \"" << infoPtr->name << "\", with tags \"" - << infoPtr->tagsAsString() << "\" already defined.\n" - << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n" - << "\tRedefined at " << infoPtr->lineInfo ); - } - } - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { std::vector filtered; filtered.reserve( testCases.size() ); @@ -6156,13 +6268,6 @@ namespace Catch { return m_sortedFunctions; } - - - /////////////////////////////////////////////////////////////////////////// - void TestInvokerAsFunction::invoke() const { - m_testAsFunction(); - } - } // end namespace Catch @@ -6195,12 +6300,17 @@ namespace TestCaseTracking { m_children.push_back( CATCH_MOVE(child) ); } - ITracker* ITracker::findChild( NameAndLocationRef nameAndLocation ) { + ITracker* ITracker::findChild( NameAndLocationRef const& nameAndLocation ) { auto it = std::find_if( m_children.begin(), m_children.end(), [&nameAndLocation]( ITrackerPtr const& tracker ) { - return tracker->nameAndLocation() == nameAndLocation; + auto const& tnameAndLoc = tracker->nameAndLocation(); + if ( tnameAndLoc.location.line != + nameAndLocation.location.line ) { + return false; + } + return tnameAndLoc == nameAndLocation; } ); return ( it != m_children.end() ) ? it->get() : nullptr; } @@ -6208,10 +6318,6 @@ namespace TestCaseTracking { bool ITracker::isSectionTracker() const { return false; } bool ITracker::isGeneratorTracker() const { return false; } - bool ITracker::isSuccessfullyCompleted() const { - return m_runState == CompletedSuccessfully; - } - bool ITracker::isOpen() const { return m_runState != NotStarted && !isComplete(); } @@ -6238,16 +6344,6 @@ namespace TestCaseTracking { return *m_rootTracker; } - void TrackerContext::endRun() { - m_rootTracker.reset(); - m_currentTracker = nullptr; - m_runState = NotStarted; - } - - void TrackerContext::startCycle() { - m_currentTracker = m_rootTracker.get(); - m_runState = Executing; - } void TrackerContext::completeCycle() { m_runState = CompletedCycle; } @@ -6255,9 +6351,6 @@ namespace TestCaseTracking { bool TrackerContext::completedCycle() const { return m_runState == CompletedCycle; } - ITracker& TrackerContext::currentTracker() { - return *m_currentTracker; - } void TrackerContext::setCurrentTracker( ITracker* tracker ) { m_currentTracker = tracker; } @@ -6326,7 +6419,7 @@ namespace TestCaseTracking { SectionTracker::SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ) : TrackerBase( CATCH_MOVE(nameAndLocation), ctx, parent ), - m_trimmed_name(trim(ITracker::nameAndLocation().name)) + m_trimmed_name(trim(StringRef(ITracker::nameAndLocation().name))) { if( parent ) { while ( !parent->isSectionTracker() ) { @@ -6351,7 +6444,7 @@ namespace TestCaseTracking { bool SectionTracker::isSectionTracker() const { return true; } - SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef nameAndLocation ) { + SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ) { SectionTracker* tracker; ITracker& currentTracker = ctx.currentTracker(); @@ -6395,10 +6488,6 @@ namespace TestCaseTracking { m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() ); } - std::vector const& SectionTracker::getFilters() const { - return m_filters; - } - StringRef SectionTracker::trimmedName() const { return m_trimmed_name; } @@ -6424,6 +6513,14 @@ namespace Catch { #endif } + void throw_test_skip_exception() { +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + throw Catch::TestSkipException(); +#else + CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" ); +#endif + } + } // namespace Catch @@ -6432,9 +6529,10 @@ namespace Catch { #include namespace Catch { + ITestInvoker::~ITestInvoker() = default; namespace { - StringRef extractClassName( StringRef classOrMethodName ) { + static StringRef extractClassName( StringRef classOrMethodName ) { if ( !startsWith( classOrMethodName, '&' ) ) { return classOrMethodName; } @@ -6461,6 +6559,18 @@ namespace Catch { static_cast( startIdx ), static_cast( classNameSize ) ); } + + class TestInvokerAsFunction final : public ITestInvoker { + using TestType = void ( * )(); + TestType m_testAsFunction; + + public: + TestInvokerAsFunction( TestType testAsFunction ) noexcept: + m_testAsFunction( testAsFunction ) {} + + void invoke() const override { m_testAsFunction(); } + }; + } // namespace Detail::unique_ptr makeTestInvoker( void(*testAsFunction)() ) { @@ -6697,10 +6807,8 @@ namespace Catch { token.erase(token.begin()); if (m_exclusion) { m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(".", m_substring)); - m_currentFilter.m_forbidden.emplace_back(Detail::make_unique(token, m_substring)); } else { m_currentFilter.m_required.emplace_back(Detail::make_unique(".", m_substring)); - m_currentFilter.m_required.emplace_back(Detail::make_unique(token, m_substring)); } } if (m_exclusion) { @@ -7643,7 +7751,19 @@ WithinRelMatcher WithinRel(float target) { } -} // namespace Matchers + +bool IsNaNMatcher::match( double const& matchee ) const { + return std::isnan( matchee ); +} + +std::string IsNaNMatcher::describe() const { + using namespace std::string_literals; + return "is NaN"s; +} + +IsNaNMatcher IsNaN() { return IsNaNMatcher(); } + + } // namespace Matchers } // namespace Catch @@ -8059,7 +8179,7 @@ class AssertionPrinter { return; const auto itEnd = messages.cend(); - const auto N = static_cast(std::distance(itMessage, itEnd)); + const auto N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -8080,7 +8200,7 @@ class AssertionPrinter { private: std::ostream& stream; AssertionResult const& result; - std::vector messages; + std::vector const& messages; std::vector::const_iterator itMessage; bool printInfoMessages; ColourImpl* colourImpl; @@ -8174,7 +8294,6 @@ class ConsoleAssertionPrinter { stats(_stats), result(_stats.assertionResult), colour(Colour::None), - message(result.getMessage()), messages(_stats.infoMessages), colourImpl(colourImpl_), printInfoMessages(_printInfoMessages) { @@ -8183,10 +8302,10 @@ class ConsoleAssertionPrinter { colour = Colour::Success; passOrFail = "PASSED"_sr; //if( result.hasMessage() ) - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; + if (messages.size() == 1) + messageLabel = "with message"_sr; + if (messages.size() > 1) + messageLabel = "with messages"_sr; break; case ResultWas::ExpressionFailed: if (result.isOk()) { @@ -8196,51 +8315,57 @@ class ConsoleAssertionPrinter { colour = Colour::Error; passOrFail = "FAILED"_sr; } - if (_stats.infoMessages.size() == 1) - messageLabel = "with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "with messages"; + if (messages.size() == 1) + messageLabel = "with message"_sr; + if (messages.size() > 1) + messageLabel = "with messages"_sr; break; case ResultWas::ThrewException: colour = Colour::Error; passOrFail = "FAILED"_sr; - messageLabel = "due to unexpected exception with "; - if (_stats.infoMessages.size() == 1) - messageLabel += "message"; - if (_stats.infoMessages.size() > 1) - messageLabel += "messages"; + // todo switch + switch (messages.size()) { case 0: + messageLabel = "due to unexpected exception with "_sr; + break; + case 1: + messageLabel = "due to unexpected exception with message"_sr; + break; + default: + messageLabel = "due to unexpected exception with messages"_sr; + break; + } break; case ResultWas::FatalErrorCondition: colour = Colour::Error; passOrFail = "FAILED"_sr; - messageLabel = "due to a fatal error condition"; + messageLabel = "due to a fatal error condition"_sr; break; case ResultWas::DidntThrowException: colour = Colour::Error; passOrFail = "FAILED"_sr; - messageLabel = "because no exception was thrown where one was expected"; + messageLabel = "because no exception was thrown where one was expected"_sr; break; case ResultWas::Info: - messageLabel = "info"; + messageLabel = "info"_sr; break; case ResultWas::Warning: - messageLabel = "warning"; + messageLabel = "warning"_sr; break; case ResultWas::ExplicitFailure: passOrFail = "FAILED"_sr; colour = Colour::Error; - if (_stats.infoMessages.size() == 1) - messageLabel = "explicitly with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "explicitly with messages"; + if (messages.size() == 1) + messageLabel = "explicitly with message"_sr; + if (messages.size() > 1) + messageLabel = "explicitly with messages"_sr; break; case ResultWas::ExplicitSkip: colour = Colour::Skip; passOrFail = "SKIPPED"_sr; - if (_stats.infoMessages.size() == 1) - messageLabel = "explicitly with message"; - if (_stats.infoMessages.size() > 1) - messageLabel = "explicitly with messages"; + if (messages.size() == 1) + messageLabel = "explicitly with message"_sr; + if (messages.size() > 1) + messageLabel = "explicitly with messages"_sr; break; // These cases are here to prevent compiler warnings case ResultWas::Unknown: @@ -8304,9 +8429,8 @@ class ConsoleAssertionPrinter { AssertionResult const& result; Colour::Code colour; StringRef passOrFail; - std::string messageLabel; - std::string message; - std::vector messages; + StringRef messageLabel; + std::vector const& messages; ColourImpl* colourImpl; bool printInfoMessages; }; @@ -9308,6 +9432,8 @@ namespace Catch { gmtime_s(&timeInfo, &rawtime); #elif defined (CATCH_PLATFORM_PLAYSTATION) gmtime_s(&rawtime, &timeInfo); +#elif defined (__IAR_SYSTEMS_ICC__) + timeInfo = *std::gmtime(&rawtime); #else gmtime_r(&rawtime, &timeInfo); #endif @@ -9568,7 +9694,7 @@ namespace Catch { } } - if( !result.getMessage().empty() ) + if( result.hasMessage() ) rss << result.getMessage() << '\n'; for( auto const& msg : stats.infoMessages ) if( msg.type == ResultWas::Info ) @@ -9687,7 +9813,6 @@ namespace Catch { } } - // The return value indicates if the messages buffer should be cleared: void MultiReporter::assertionEnded( AssertionStats const& assertionStats ) { const bool reportByDefault = assertionStats.assertionResult.getResultType() != ResultWas::Ok || @@ -9790,6 +9915,11 @@ namespace Catch { } } + void registerListenerImpl( Detail::unique_ptr listenerFactory ) { + getMutableRegistryHub().registerListener( CATCH_MOVE(listenerFactory) ); + } + + } // namespace Detail } // namespace Catch @@ -9929,7 +10059,7 @@ namespace Catch { } } - if (!result.getMessage().empty()) + if (result.hasMessage()) textRss << result.getMessage() << '\n'; for (auto const& msg : stats.infoMessages) @@ -9963,7 +10093,6 @@ namespace Catch { #include -#include #include namespace Catch { @@ -10114,7 +10243,7 @@ namespace Catch { // using messages.end() directly (or auto) yields compilation error: std::vector::const_iterator itEnd = messages.end(); - const std::size_t N = static_cast(std::distance(itMessage, itEnd)); + const std::size_t N = static_cast(itEnd - itMessage); stream << colourImpl->guardColour( colour ) << " with " << pluralise( N, "message"_sr ) << ':'; @@ -10133,7 +10262,7 @@ namespace Catch { private: std::ostream& stream; AssertionResult const& result; - std::vector messages; + std::vector const& messages; std::vector::const_iterator itMessage; bool printInfoMessages; std::size_t counter; @@ -10386,7 +10515,7 @@ namespace Catch { m_xml.startElement("Catch2TestRun") .writeAttribute("name"_sr, m_config->name()) .writeAttribute("rng-seed"_sr, m_config->rngSeed()) - .writeAttribute("xml-format-version"_sr, 2) + .writeAttribute("xml-format-version"_sr, 3) .writeAttribute("catch2-version"_sr, libraryVersion()); if ( m_config->testSpec().hasFilters() ) { m_xml.writeAttribute( "filters"_sr, m_config->testSpec() ); @@ -10396,7 +10525,7 @@ namespace Catch { void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) { StreamingReporterBase::testCaseStarting(testInfo); m_xml.startElement( "TestCase" ) - .writeAttribute( "name"_sr, trim( testInfo.name ) ) + .writeAttribute( "name"_sr, trim( StringRef(testInfo.name) ) ) .writeAttribute( "tags"_sr, testInfo.tagsAsString() ); writeSourceInfo( testInfo.lineInfo ); @@ -10410,7 +10539,7 @@ namespace Catch { StreamingReporterBase::sectionStarting( sectionInfo ); if( m_sectionDepth++ > 0 ) { m_xml.startElement( "Section" ) - .writeAttribute( "name"_sr, trim( sectionInfo.name ) ); + .writeAttribute( "name"_sr, trim( StringRef(sectionInfo.name) ) ); writeSourceInfo( sectionInfo.lineInfo ); m_xml.ensureTagClosed(); } @@ -10428,11 +10557,13 @@ namespace Catch { // Print any info messages in tags. for( auto const& msg : assertionStats.infoMessages ) { if( msg.type == ResultWas::Info && includeResults ) { - m_xml.scopedElement( "Info" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Info" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } else if ( msg.type == ResultWas::Warning ) { - m_xml.scopedElement( "Warning" ) - .writeText( msg.message ); + auto t = m_xml.scopedElement( "Warning" ); + writeSourceInfo( msg.lineInfo ); + t.writeText( msg.message ); } } } @@ -10524,11 +10655,10 @@ namespace Catch { if ( m_config->showDurations() == ShowDurations::Always ) e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() ); - if( !testCaseStats.stdOut.empty() ) - m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline ); + m_xml.scopedElement( "StdOut" ).writeText( trim( StringRef(testCaseStats.stdOut) ), XmlFormatting::Newline ); if( !testCaseStats.stdErr.empty() ) - m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline ); + m_xml.scopedElement( "StdErr" ).writeText( trim( StringRef(testCaseStats.stdErr) ), XmlFormatting::Newline ); m_xml.endElement(); } diff --git a/Framework/Foundation/3rdparty/catch2/catch_amalgamated.hpp b/Framework/Foundation/3rdparty/catch2/catch_amalgamated.hpp index 88f2dd912c83c..694fe1e4d81b3 100644 --- a/Framework/Foundation/3rdparty/catch2/catch_amalgamated.hpp +++ b/Framework/Foundation/3rdparty/catch2/catch_amalgamated.hpp @@ -5,8 +5,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.3.1 -// Generated: 2023-01-29 22:55:03.856079 +// Catch v3.4.0 +// Generated: 2023-07-13 13:23:07.577770 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -59,233 +59,6 @@ -#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED -#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED - - - -#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED -#define CATCH_NONCOPYABLE_HPP_INCLUDED - -namespace Catch { - namespace Detail { - - //! Deriving classes become noncopyable and nonmovable - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable&& ) = delete; - NonCopyable& operator=( NonCopyable const& ) = delete; - NonCopyable& operator=( NonCopyable&& ) = delete; - - protected: - NonCopyable() noexcept = default; - }; - - } // namespace Detail -} // namespace Catch - -#endif // CATCH_NONCOPYABLE_HPP_INCLUDED - - -#ifndef CATCH_STRINGREF_HPP_INCLUDED -#define CATCH_STRINGREF_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef other ) const noexcept -> bool; - auto operator != (StringRef other) const noexcept -> bool { - return !(*this == other); - } - - constexpr auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - bool operator<(StringRef rhs) const noexcept; - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, start + size()). - // If start > size(), then the substring is empty. - constexpr StringRef substr(size_type start, size_type length) const noexcept { - if (start < m_size) { - const auto shortened_size = m_size - start; - return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); - } else { - return StringRef(); - } - } - - // Returns the current start pointer. May not be null-terminated. - constexpr char const* data() const noexcept { - return m_start; - } - - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - - - friend std::string& operator += (std::string& lhs, StringRef sr); - friend std::ostream& operator << (std::ostream& os, StringRef sr); - friend std::string operator+(StringRef lhs, StringRef rhs); - - /** - * Provides a three-way comparison with rhs - * - * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive - * number if lhs > rhs - */ - int compare( StringRef rhs ) const; - }; - - - constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -#endif // CATCH_STRINGREF_HPP_INCLUDED - -#include -#include -#include -#include - -namespace Catch { - - enum class Verbosity { - Quiet = 0, - Normal, - High - }; - - struct WarnAbout { enum What { - Nothing = 0x00, - //! A test case or leaf section did not run any assertions - NoAssertions = 0x01, - //! A command line test spec matched no test cases - UnmatchedTestSpec = 0x02, - }; }; - - enum class ShowDurations { - DefaultForReporter, - Always, - Never - }; - enum class TestRunOrder { - Declared, - LexicographicallySorted, - Randomized - }; - enum class ColourMode : std::uint8_t { - //! Let Catch2 pick implementation based on platform detection - PlatformDefault, - //! Use ANSI colour code escapes - ANSI, - //! Use Win32 console colour API - Win32, - //! Don't use any colour - None - }; - struct WaitForKeypress { enum When { - Never, - BeforeStart = 1, - BeforeExit = 2, - BeforeStartAndExit = BeforeStart | BeforeExit - }; }; - - class TestSpec; - class IStream; - - class IConfig : public Detail::NonCopyable { - public: - virtual ~IConfig(); - - virtual bool allowThrows() const = 0; - virtual StringRef name() const = 0; - virtual bool includeSuccessfulResults() const = 0; - virtual bool shouldDebugBreak() const = 0; - virtual bool warnAboutMissingAssertions() const = 0; - virtual bool warnAboutUnmatchedTestSpecs() const = 0; - virtual bool zeroTestsCountAsSuccess() const = 0; - virtual int abortAfter() const = 0; - virtual bool showInvisibles() const = 0; - virtual ShowDurations showDurations() const = 0; - virtual double minDuration() const = 0; - virtual TestSpec const& testSpec() const = 0; - virtual bool hasTestFilters() const = 0; - virtual std::vector const& getTestsOrTags() const = 0; - virtual TestRunOrder runOrder() const = 0; - virtual uint32_t rngSeed() const = 0; - virtual unsigned int shardCount() const = 0; - virtual unsigned int shardIndex() const = 0; - virtual ColourMode defaultColourMode() const = 0; - virtual std::vector const& getSectionsToRun() const = 0; - virtual Verbosity verbosity() const = 0; - - virtual bool skipBenchmarks() const = 0; - virtual bool benchmarkNoAnalysis() const = 0; - virtual unsigned int benchmarkSamples() const = 0; - virtual double benchmarkConfidenceInterval() const = 0; - virtual unsigned int benchmarkResamples() const = 0; - virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; - }; -} - -#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED - - #ifndef CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED #define CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED @@ -352,7 +125,7 @@ namespace Catch { // Only GCC compiler should be used in this block, so other compilers trying to // mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) @@ -361,16 +134,28 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ + _Pragma( "GCC diagnostic ignored \"-Wunused-result\"" ) + # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" ) # define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ _Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "GCC diagnostic ignored \"-Wshadow\"" ) + # define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) #endif +#if defined(__NVCOMPILER) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" ) +#endif + #if defined(__CUDACC__) && !defined(__clang__) # ifdef __NVCC_DIAG_PRAGMA_SUPPORT__ // New pragmas introduced in CUDA 11.5+ @@ -433,6 +218,9 @@ namespace Catch { # define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \ _Pragma( "clang diagnostic ignored \"-Wcomma\"" ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wshadow\"" ) + #endif // __clang__ @@ -670,6 +458,9 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS #endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT +#endif #if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS #endif @@ -679,6 +470,16 @@ namespace Catch { #if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) # define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS #endif +#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS +#endif +#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS ) +# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS +#endif + // The goal of this macro is to avoid evaluation of the arguments, but // still have the compiler warn on problems inside... @@ -692,13 +493,6 @@ namespace Catch { # undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS #endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS -#endif #if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) #define CATCH_TRY if ((true)) @@ -744,38 +538,31 @@ namespace Catch { class IResultCapture; class IConfig; - class IContext { - public: - virtual ~IContext(); // = default + class Context { + IConfig const* m_config = nullptr; + IResultCapture* m_resultCapture = nullptr; - virtual IResultCapture* getResultCapture() = 0; - virtual IConfig const* getConfig() const = 0; - }; + CATCH_EXPORT static Context* currentContext; + friend Context& getCurrentMutableContext(); + friend Context const& getCurrentContext(); + static void createContext(); + friend void cleanUpContext(); - class IMutableContext : public IContext { public: - ~IMutableContext() override; // = default - virtual void setResultCapture( IResultCapture* resultCapture ) = 0; - virtual void setConfig( IConfig const* config ) = 0; - - private: - CATCH_EXPORT static IMutableContext* currentContext; - friend IMutableContext& getCurrentMutableContext(); - friend void cleanUpContext(); - static void createContext(); + IResultCapture* getResultCapture() const { return m_resultCapture; } + IConfig const* getConfig() const { return m_config; } + void setResultCapture( IResultCapture* resultCapture ); + void setConfig( IConfig const* config ); }; - inline IMutableContext& getCurrentMutableContext() - { - if( !IMutableContext::currentContext ) - IMutableContext::createContext(); - // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) - return *IMutableContext::currentContext; - } + Context& getCurrentMutableContext(); - inline IContext& getCurrentContext() - { - return getCurrentMutableContext(); + inline Context const& getCurrentContext() { + // We duplicate the logic from `getCurrentMutableContext` here, + // to avoid paying the call overhead in debug mode. + if ( !Context::currentContext ) { Context::createContext(); } + // NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn) + return *Context::currentContext; } void cleanUpContext(); @@ -787,18 +574,8 @@ namespace Catch { #endif // CATCH_CONTEXT_HPP_INCLUDED -#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED -#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED - - - -#ifndef CATCH_SECTION_INFO_HPP_INCLUDED -#define CATCH_SECTION_INFO_HPP_INCLUDED - - - -#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED -#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED +#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED +#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED #include @@ -811,110 +588,199 @@ namespace Catch { #endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED -#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED -#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED - -#include -#include +#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED +#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED namespace Catch { - struct SourceLineInfo { + //! Used to signal that an assertion macro failed + struct TestFailureException{}; + //! Used to signal that the remainder of a test should be skipped + struct TestSkipException {}; - SourceLineInfo() = delete; - constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: - file( _file ), - line( _line ) - {} + /** + * Outlines throwing of `TestFailureException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_failure_exception(); - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; + /** + * Outlines throwing of `TestSkipException` into a single TU + * + * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. + */ + [[noreturn]] void throw_test_skip_exception(); - char const* file; - std::size_t line; +} // namespace Catch - friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); - }; -} +#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) -#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED +#define CATCH_UNIQUE_NAME_HPP_INCLUDED -#ifndef CATCH_TOTALS_HPP_INCLUDED -#define CATCH_TOTALS_HPP_INCLUDED -#include -namespace Catch { +/** \file + * Wrapper for the CONFIG configuration option + * + * When generating internal unique names, there are two options. Either + * we mix in the current line number, or mix in an incrementing number. + * We prefer the latter, using `__COUNTER__`, but users might want to + * use the former. + */ - struct Counts { - Counts operator - ( Counts const& other ) const; - Counts& operator += ( Counts const& other ); +#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define CATCH_CONFIG_COUNTER_HPP_INCLUDED - std::uint64_t total() const; - bool allPassed() const; - bool allOk() const; - std::uint64_t passed = 0; - std::uint64_t failed = 0; - std::uint64_t failedButOk = 0; - std::uint64_t skipped = 0; - }; +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif - struct Totals { +#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ + !defined( CATCH_CONFIG_NO_COUNTER ) && \ + !defined( CATCH_CONFIG_COUNTER ) +# define CATCH_CONFIG_COUNTER +#endif - Totals operator - ( Totals const& other ) const; - Totals& operator += ( Totals const& other ); - Totals delta( Totals const& prevTotals ) const; +#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif - Counts assertions; - Counts testCases; - }; -} +#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED -#endif // CATCH_TOTALS_HPP_INCLUDED +#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED +#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED + +#include +#include + + + +#ifndef CATCH_STRINGREF_HPP_INCLUDED +#define CATCH_STRINGREF_HPP_INCLUDED + +#include #include +#include +#include + +#include namespace Catch { - struct SectionInfo { - // The last argument is ignored, so that people can write - // SECTION("ShortName", "Proper description that is long") and - // still use the `-c` flag comfortably. - SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, - const char* const = nullptr ): - name(CATCH_MOVE(_name)), - lineInfo(_lineInfo) - {} + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; - std::string name; - SourceLineInfo lineInfo; - }; + private: + static constexpr char const* const s_empty = ""; - struct SectionEndInfo { - SectionInfo sectionInfo; - Counts prevAssertions; - double durationInSeconds; - }; + char const* m_start = s_empty; + size_type m_size = 0; -} // end namespace Catch + public: // construction + constexpr StringRef() noexcept = default; -#endif // CATCH_SECTION_INFO_HPP_INCLUDED + StringRef( char const* rawChars ) noexcept; + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} -#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED -#define CATCH_ASSERTION_RESULT_HPP_INCLUDED + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef other ) const noexcept -> bool { + return m_size == other.m_size + && (std::memcmp( m_start, other.m_start, m_size ) == 0); + } + auto operator != (StringRef other) const noexcept -> bool { + return !(*this == other); + } + constexpr auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } -#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED -#define CATCH_ASSERTION_INFO_HPP_INCLUDED + bool operator<(StringRef rhs) const noexcept; + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, start + size()). + // If start > size(), then the substring is empty. + constexpr StringRef substr(size_type start, size_type length) const noexcept { + if (start < m_size) { + const auto shortened_size = m_size - start; + return StringRef(m_start + start, (shortened_size < length) ? shortened_size : length); + } else { + return StringRef(); + } + } + + // Returns the current start pointer. May not be null-terminated. + constexpr char const* data() const noexcept { + return m_start; + } + + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + + + friend std::string& operator += (std::string& lhs, StringRef sr); + friend std::ostream& operator << (std::ostream& os, StringRef sr); + friend std::string operator+(StringRef lhs, StringRef rhs); + + /** + * Provides a three-way comparison with rhs + * + * Returns negative number if lhs < rhs, 0 if lhs == rhs, and a positive + * number if lhs > rhs + */ + int compare( StringRef rhs ) const; + }; + + + constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch +constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +#endif // CATCH_STRINGREF_HPP_INCLUDED #ifndef CATCH_RESULT_TYPE_HPP_INCLUDED @@ -968,120 +834,12 @@ namespace Catch { #endif // CATCH_RESULT_TYPE_HPP_INCLUDED -namespace Catch { - - struct AssertionInfo { - // AssertionInfo() = delete; - - StringRef macroName; - SourceLineInfo lineInfo; - StringRef capturedExpression; - ResultDisposition::Flags resultDisposition; - }; - -} // end namespace Catch - -#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED +#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED +#define CATCH_UNIQUE_PTR_HPP_INCLUDED -#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED -#define CATCH_LAZY_EXPR_HPP_INCLUDED - -#include - -namespace Catch { - - class ITransientExpression; - - class LazyExpression { - friend class AssertionHandler; - friend struct AssertionStats; - friend class RunContext; - - ITransientExpression const* m_transientExpression = nullptr; - bool m_isNegated; - public: - LazyExpression( bool isNegated ): - m_isNegated(isNegated) - {} - LazyExpression(LazyExpression const& other) = default; - LazyExpression& operator = ( LazyExpression const& ) = delete; - - explicit operator bool() const { - return m_transientExpression != nullptr; - } - - friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; - }; - -} // namespace Catch - -#endif // CATCH_LAZY_EXPR_HPP_INCLUDED - -#include - -namespace Catch { - - struct AssertionResultData - { - AssertionResultData() = delete; - - AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - - std::string message; - mutable std::string reconstructedExpression; - LazyExpression lazyExpression; - ResultWas::OfType resultType; - - std::string reconstructExpression() const; - }; - - class AssertionResult { - public: - AssertionResult() = delete; - AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); - - bool isOk() const; - bool succeeded() const; - ResultWas::OfType getResultType() const; - bool hasExpression() const; - bool hasMessage() const; - std::string getExpression() const; - std::string getExpressionInMacro() const; - bool hasExpandedExpression() const; - std::string getExpandedExpression() const; - StringRef getMessage() const; - SourceLineInfo getSourceInfo() const; - StringRef getTestMacroName() const; - - //protected: - AssertionInfo m_info; - AssertionResultData m_resultData; - }; - -} // end namespace Catch - -#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED - - -#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED -#define CATCH_MESSAGE_INFO_HPP_INCLUDED - - - -#ifndef CATCH_INTERFACES_CAPTURE_HPP_INCLUDED -#define CATCH_INTERFACES_CAPTURE_HPP_INCLUDED - -#include -#include - - - -#ifndef CATCH_UNIQUE_PTR_HPP_INCLUDED -#define CATCH_UNIQUE_PTR_HPP_INCLUDED - -#include -#include +#include +#include namespace Catch { @@ -1188,6 +946,24 @@ namespace Detail { #endif // CATCH_UNIQUE_PTR_HPP_INCLUDED + +#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + +#include + +namespace Catch { + + // We cannot forward declare the type with default template argument + // multiple times, so it is split out into a separate header so that + // we can prevent multiple declarations in dependees + template > + struct BenchmarkStats; + +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED + namespace Catch { class AssertionResult; @@ -1204,8 +980,6 @@ namespace Catch { class IGeneratorTracker; struct BenchmarkInfo; - template > - struct BenchmarkStats; namespace Generators { class GeneratorUntypedBase; @@ -1217,6 +991,7 @@ namespace Catch { public: virtual ~IResultCapture(); + virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0; virtual bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) = 0; @@ -1257,7 +1032,7 @@ namespace Catch { AssertionReaction& reaction ) = 0; virtual void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) = 0; virtual void handleIncomplete ( AssertionInfo const& info ) = 0; @@ -1282,179 +1057,242 @@ namespace Catch { #endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED -#include - -namespace Catch { - - struct MessageInfo { - MessageInfo( StringRef _macroName, - SourceLineInfo const& _lineInfo, - ResultWas::OfType _type ); - - StringRef macroName; - std::string message; - SourceLineInfo lineInfo; - ResultWas::OfType type; - unsigned int sequence; - - bool operator == (MessageInfo const& other) const { - return sequence == other.sequence; - } - bool operator < (MessageInfo const& other) const { - return sequence < other.sequence; - } - private: - static unsigned int globalCount; - }; - -} // end namespace Catch -#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED +#ifndef CATCH_INTERFACES_CONFIG_HPP_INCLUDED +#define CATCH_INTERFACES_CONFIG_HPP_INCLUDED -// Adapted from donated nonius code. -#ifndef CATCH_ESTIMATE_HPP_INCLUDED -#define CATCH_ESTIMATE_HPP_INCLUDED +#ifndef CATCH_NONCOPYABLE_HPP_INCLUDED +#define CATCH_NONCOPYABLE_HPP_INCLUDED namespace Catch { - namespace Benchmark { - template - struct Estimate { - Duration point; - Duration lower_bound; - Duration upper_bound; - double confidence_interval; - - template - operator Estimate() const { - return { point, lower_bound, upper_bound, confidence_interval }; - } - }; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_ESTIMATE_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED -#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED + namespace Detail { -namespace Catch { - namespace Benchmark { - struct OutlierClassification { - int samples_seen = 0; - int low_severe = 0; // more than 3 times IQR below Q1 - int low_mild = 0; // 1.5 to 3 times IQR below Q1 - int high_mild = 0; // 1.5 to 3 times IQR above Q3 - int high_severe = 0; // more than 3 times IQR above Q3 + //! Deriving classes become noncopyable and nonmovable + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable&& ) = delete; + NonCopyable& operator=( NonCopyable const& ) = delete; + NonCopyable& operator=( NonCopyable&& ) = delete; - int total() const { - return low_severe + low_mild + high_mild + high_severe; - } + protected: + NonCopyable() noexcept = default; }; - } // namespace Benchmark -} // namespace Catch -#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED + } // namespace Detail +} // namespace Catch +#endif // CATCH_NONCOPYABLE_HPP_INCLUDED -#include +#include +#include #include #include -#include namespace Catch { - struct ReporterDescription; - struct ListenerDescription; - struct TagInfo; - struct TestCaseInfo; - class TestCaseHandle; - class IConfig; - class IStream; - enum class ColourMode : std::uint8_t; - - struct ReporterConfig { - ReporterConfig( IConfig const* _fullConfig, - Detail::unique_ptr _stream, - ColourMode colourMode, - std::map customOptions ); - - ReporterConfig( ReporterConfig&& ) = default; - ReporterConfig& operator=( ReporterConfig&& ) = default; - ~ReporterConfig(); // = default - - Detail::unique_ptr takeStream() &&; - IConfig const* fullConfig() const; - ColourMode colourMode() const; - std::map const& customOptions() const; - - private: - Detail::unique_ptr m_stream; - IConfig const* m_fullConfig; - ColourMode m_colourMode; - std::map m_customOptions; - }; - - struct TestRunInfo { - constexpr TestRunInfo(StringRef _name) : name(_name) {} - StringRef name; + enum class Verbosity { + Quiet = 0, + Normal, + High }; - struct AssertionStats { - AssertionStats( AssertionResult const& _assertionResult, - std::vector const& _infoMessages, - Totals const& _totals ); - - AssertionStats( AssertionStats const& ) = default; - AssertionStats( AssertionStats && ) = default; - AssertionStats& operator = ( AssertionStats const& ) = delete; - AssertionStats& operator = ( AssertionStats && ) = delete; + struct WarnAbout { enum What { + Nothing = 0x00, + //! A test case or leaf section did not run any assertions + NoAssertions = 0x01, + //! A command line test spec matched no test cases + UnmatchedTestSpec = 0x02, + }; }; - AssertionResult assertionResult; - std::vector infoMessages; - Totals totals; + enum class ShowDurations { + DefaultForReporter, + Always, + Never }; - - struct SectionStats { - SectionStats( SectionInfo&& _sectionInfo, - Counts const& _assertions, - double _durationInSeconds, - bool _missingAssertions ); - - SectionInfo sectionInfo; - Counts assertions; - double durationInSeconds; - bool missingAssertions; + enum class TestRunOrder { + Declared, + LexicographicallySorted, + Randomized + }; + enum class ColourMode : std::uint8_t { + //! Let Catch2 pick implementation based on platform detection + PlatformDefault, + //! Use ANSI colour code escapes + ANSI, + //! Use Win32 console colour API + Win32, + //! Don't use any colour + None }; + struct WaitForKeypress { enum When { + Never, + BeforeStart = 1, + BeforeExit = 2, + BeforeStartAndExit = BeforeStart | BeforeExit + }; }; - struct TestCaseStats { - TestCaseStats( TestCaseInfo const& _testInfo, - Totals const& _totals, - std::string const& _stdOut, - std::string const& _stdErr, - bool _aborting ); + class TestSpec; + class IStream; - TestCaseInfo const * testInfo; - Totals totals; - std::string stdOut; - std::string stdErr; - bool aborting; + class IConfig : public Detail::NonCopyable { + public: + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual StringRef name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual bool warnAboutUnmatchedTestSpecs() const = 0; + virtual bool zeroTestsCountAsSuccess() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations showDurations() const = 0; + virtual double minDuration() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual bool hasTestFilters() const = 0; + virtual std::vector const& getTestsOrTags() const = 0; + virtual TestRunOrder runOrder() const = 0; + virtual uint32_t rngSeed() const = 0; + virtual unsigned int shardCount() const = 0; + virtual unsigned int shardIndex() const = 0; + virtual ColourMode defaultColourMode() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + virtual Verbosity verbosity() const = 0; + + virtual bool skipBenchmarks() const = 0; + virtual bool benchmarkNoAnalysis() const = 0; + virtual unsigned int benchmarkSamples() const = 0; + virtual double benchmarkConfidenceInterval() const = 0; + virtual unsigned int benchmarkResamples() const = 0; + virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0; }; +} - struct TestRunStats { - TestRunStats( TestRunInfo const& _runInfo, - Totals const& _totals, - bool _aborting ); +#endif // CATCH_INTERFACES_CONFIG_HPP_INCLUDED - TestRunInfo runInfo; - Totals totals; - bool aborting; + +#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED +#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED + + +#include + +namespace Catch { + + class TestCaseHandle; + struct TestCaseInfo; + class ITestCaseRegistry; + class IExceptionTranslatorRegistry; + class IExceptionTranslator; + class ReporterRegistry; + class IReporterFactory; + class ITagAliasRegistry; + class ITestInvoker; + class IMutableEnumValuesRegistry; + struct SourceLineInfo; + + class StartupExceptionRegistry; + class EventListenerFactory; + + using IReporterFactoryPtr = Detail::unique_ptr; + + class IRegistryHub { + public: + virtual ~IRegistryHub(); // = default + + virtual ReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; + virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; + + + virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; + }; + + class IMutableRegistryHub { + public: + virtual ~IMutableRegistryHub(); // = default + virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; + virtual void registerListener( Detail::unique_ptr factory ) = 0; + virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; + virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; + virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; + virtual void registerStartupException() noexcept = 0; + virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; }; + IRegistryHub const& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED + + +#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED +#define CATCH_BENCHMARK_STATS_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_ESTIMATE_HPP_INCLUDED +#define CATCH_ESTIMATE_HPP_INCLUDED + +namespace Catch { + namespace Benchmark { + template + struct Estimate { + Duration point; + Duration lower_bound; + Duration upper_bound; + double confidence_interval; + + template + operator Estimate() const { + return { point, lower_bound, upper_bound, confidence_interval }; + } + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ESTIMATE_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED +#define CATCH_OUTLIER_CLASSIFICATION_HPP_INCLUDED + +namespace Catch { + namespace Benchmark { + struct OutlierClassification { + int samples_seen = 0; + int low_severe = 0; // more than 3 times IQR below Q1 + int low_mild = 0; // 1.5 to 3 times IQR below Q1 + int high_mild = 0; // 1.5 to 3 times IQR above Q3 + int high_severe = 0; // more than 3 times IQR above Q3 + + int total() const { + return low_severe + low_mild + high_mild + high_severe; + } + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_OUTLIERS_CLASSIFICATION_HPP_INCLUDED +// The fwd decl & default specialization needs to be seen by VS2017 before +// BenchmarkStats itself, or VS2017 will report compilation error. + +#include +#include + +namespace Catch { struct BenchmarkInfo { std::string name; @@ -1494,169 +1332,10 @@ namespace Catch { } }; - //! By setting up its preferences, a reporter can modify Catch2's behaviour - //! in some regards, e.g. it can request Catch2 to capture writes to - //! stdout/stderr during test execution, and pass them to the reporter. - struct ReporterPreferences { - //! Catch2 should redirect writes to stdout and pass them to the - //! reporter - bool shouldRedirectStdOut = false; - //! Catch2 should call `Reporter::assertionEnded` even for passing - //! assertions - bool shouldReportAllAssertions = false; - }; - - /** - * The common base for all reporters and event listeners - * - * Implementing classes must also implement: - * - * //! User-friendly description of the reporter/listener type - * static std::string getDescription() - * - * Generally shouldn't be derived from by users of Catch2 directly, - * instead they should derive from one of the utility bases that - * derive from this class. - */ - class IEventListener { - protected: - //! Derived classes can set up their preferences here - ReporterPreferences m_preferences; - //! The test run's config as filled in from CLI and defaults - IConfig const* m_config; - - public: - IEventListener( IConfig const* config ): m_config( config ) {} - - virtual ~IEventListener(); // = default; - - // Implementing class must also provide the following static methods: - // static std::string getDescription(); - - ReporterPreferences const& getPreferences() const { - return m_preferences; - } - - //! Called when no test cases match provided test spec - virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0; - //! Called for all invalid test specs from the cli - virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0; - - /** - * Called once in a testing run before tests are started - * - * Not called if tests won't be run (e.g. only listing will happen) - */ - virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; - - //! Called _once_ for each TEST_CASE, no matter how many times it is entered - virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; - //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) - virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0; - //! Called when a `SECTION` is being entered. Not called for skipped sections - virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; - - //! Called when user-code is being probed before the actual benchmark runs - virtual void benchmarkPreparing( StringRef benchmarkName ) = 0; - //! Called after probe but before the user-code is being benchmarked - virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0; - //! Called with the benchmark results if benchmark successfully finishes - virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0; - //! Called if running the benchmarks fails for any reason - virtual void benchmarkFailed( StringRef benchmarkName ) = 0; - - //! Called before assertion success/failure is evaluated - virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; - - //! Called after assertion was fully evaluated - virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; - - //! Called after a `SECTION` has finished running - virtual void sectionEnded( SectionStats const& sectionStats ) = 0; - //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) - virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0; - //! Called _once_ for each TEST_CASE, no matter how many times it is entered - virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; - /** - * Called once after all tests in a testing run are finished - * - * Not called if tests weren't run (e.g. only listings happened) - */ - virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; - - /** - * Called with test cases that are skipped due to the test run aborting. - * NOT called for test cases that are explicitly skipped using the `SKIP` macro. - * - * Deprecated - will be removed in the next major release. - */ - virtual void skipTest( TestCaseInfo const& testInfo ) = 0; - - //! Called if a fatal error (signal/structured exception) occured - virtual void fatalErrorEncountered( StringRef error ) = 0; - - //! Writes out information about provided reporters using reporter-specific format - virtual void listReporters(std::vector const& descriptions) = 0; - //! Writes out the provided listeners descriptions using reporter-specific format - virtual void listListeners(std::vector const& descriptions) = 0; - //! Writes out information about provided tests using reporter-specific format - virtual void listTests(std::vector const& tests) = 0; - //! Writes out information about the provided tags using reporter-specific format - virtual void listTags(std::vector const& tags) = 0; - }; - using IEventListenerPtr = Detail::unique_ptr; - -} // end namespace Catch - -#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED - - -#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED -#define CATCH_UNIQUE_NAME_HPP_INCLUDED - - - - -/** \file - * Wrapper for the CONFIG configuration option - * - * When generating internal unique names, there are two options. Either - * we mix in the current line number, or mix in an incrementing number. - * We prefer the latter, using `__COUNTER__`, but users might want to - * use the former. - */ - -#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED -#define CATCH_CONFIG_COUNTER_HPP_INCLUDED - -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -#if defined( CATCH_INTERNAL_CONFIG_COUNTER ) && \ - !defined( CATCH_CONFIG_NO_COUNTER ) && \ - !defined( CATCH_CONFIG_COUNTER ) -# define CATCH_CONFIG_COUNTER -#endif - - -#endif // CATCH_CONFIG_COUNTER_HPP_INCLUDED -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_CHRONOMETER_HPP_INCLUDED -#define CATCH_CHRONOMETER_HPP_INCLUDED +} // end namespace Catch + +#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED // Adapted from donated nonius code. @@ -1693,12 +1372,63 @@ namespace Catch { #endif // CATCH_CLOCK_HPP_INCLUDED +// Adapted from donated nonius code. + +#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED +#define CATCH_ENVIRONMENT_HPP_INCLUDED + + +namespace Catch { + namespace Benchmark { + template + struct EnvironmentEstimate { + Duration mean; + OutlierClassification outliers; + + template + operator EnvironmentEstimate() const { + return { mean, outliers }; + } + }; + template + struct Environment { + using clock_type = Clock; + EnvironmentEstimate> clock_resolution; + EnvironmentEstimate> clock_cost; + }; + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_ENVIRONMENT_HPP_INCLUDED + + +// Adapted from donated nonius code. + +#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED +#define CATCH_EXECUTION_PLAN_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED +#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED + + + +// Adapted from donated nonius code. + +#ifndef CATCH_CHRONOMETER_HPP_INCLUDED +#define CATCH_CHRONOMETER_HPP_INCLUDED + + + // Adapted from donated nonius code. #ifndef CATCH_OPTIMIZER_HPP_INCLUDED #define CATCH_OPTIMIZER_HPP_INCLUDED -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) # include // atomic_thread_fence #endif @@ -1719,16 +1449,23 @@ namespace Catch { namespace Detail { inline void optimizer_barrier() { keep_memory(); } } // namespace Detail -#elif defined(_MSC_VER) +#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__) +#if defined(_MSVC_VER) #pragma optimize("", off) +#elif defined(__IAR_SYSTEMS_ICC__) +// For IAR the pragma only affects the following function +#pragma optimize=disable +#endif template inline void keep_memory(T* p) { // thanks @milleniumbug *reinterpret_cast(p) = *reinterpret_cast(p); } // TODO equivalent keep_memory() +#if defined(_MSVC_VER) #pragma optimize("", on) +#endif namespace Detail { inline void optimizer_barrier() { @@ -1758,36 +1495,6 @@ namespace Catch { #endif // CATCH_OPTIMIZER_HPP_INCLUDED -// Adapted from donated nonius code. - -#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED -#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED - - - -#ifndef CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED -#define CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED - -namespace Catch { - - //! Used to signal that an assertion macro failed - struct TestFailureException{}; - - /** - * Outlines throwing of `TestFailureException` into a single TU - * - * Also handles `CATCH_CONFIG_DISABLE_EXCEPTIONS` for callers. - */ - [[noreturn]] void throw_test_failure_exception(); - - //! Used to signal that the remainder of a test should be skipped - struct TestSkipException{}; - -} // namespace Catch - -#endif // CATCH_TEST_FAILURE_EXCEPTION_HPP_INCLUDED - - #ifndef CATCH_META_HPP_INCLUDED #define CATCH_META_HPP_INCLUDED @@ -1829,112 +1536,6 @@ namespace mpl_{ #endif // CATCH_META_HPP_INCLUDED - -#ifndef CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED -#define CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - - -#include - -namespace Catch { - - class TestCaseHandle; - struct TestCaseInfo; - class ITestCaseRegistry; - class IExceptionTranslatorRegistry; - class IExceptionTranslator; - class IReporterRegistry; - class IReporterFactory; - class ITagAliasRegistry; - class ITestInvoker; - class IMutableEnumValuesRegistry; - struct SourceLineInfo; - - class StartupExceptionRegistry; - class EventListenerFactory; - - using IReporterFactoryPtr = Detail::unique_ptr; - - class IRegistryHub { - public: - virtual ~IRegistryHub(); // = default - - virtual IReporterRegistry const& getReporterRegistry() const = 0; - virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; - virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0; - virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0; - - - virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0; - }; - - class IMutableRegistryHub { - public: - virtual ~IMutableRegistryHub(); // = default - virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0; - virtual void registerListener( Detail::unique_ptr factory ) = 0; - virtual void registerTest(Detail::unique_ptr&& testInfo, Detail::unique_ptr&& invoker) = 0; - virtual void registerTranslator( Detail::unique_ptr&& translator ) = 0; - virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0; - virtual void registerStartupException() noexcept = 0; - virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0; - }; - - IRegistryHub const& getRegistryHub(); - IMutableRegistryHub& getMutableRegistryHub(); - void cleanUp(); - std::string translateActiveException(); - -} - -#endif // CATCH_INTERFACES_REGISTRY_HUB_HPP_INCLUDED - -#include - -namespace Catch { - namespace Benchmark { - namespace Detail { - template - struct CompleteType { using type = T; }; - template <> - struct CompleteType { struct type {}; }; - - template - using CompleteType_t = typename CompleteType::type; - - template - struct CompleteInvoker { - template - static Result invoke(Fun&& fun, Args&&... args) { - return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); - } - }; - template <> - struct CompleteInvoker { - template - static CompleteType_t invoke(Fun&& fun, Args&&... args) { - CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); - return {}; - } - }; - - // invoke and not return void :( - template - CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { - return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); - } - - } // namespace Detail - - template - Detail::CompleteType_t> user_code(Fun&& fun) { - return Detail::complete_invoke(CATCH_FORWARD(fun)); - } - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED - namespace Catch { namespace Benchmark { namespace Detail { @@ -1957,85 +1558,41 @@ namespace Catch { TimePoint started; TimePoint finished; }; - } // namespace Detail - - struct Chronometer { - public: - template - void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable()); } - - int runs() const { return repeats; } - - Chronometer(Detail::ChronometerConcept& meter, int repeats_) - : impl(&meter) - , repeats(repeats_) {} - - private: - template - void measure(Fun&& fun, std::false_type) { - measure([&fun](int) { return fun(); }, std::true_type()); - } - - template - void measure(Fun&& fun, std::true_type) { - Detail::optimizer_barrier(); - impl->start(); - for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); - impl->finish(); - Detail::optimizer_barrier(); - } - - Detail::ChronometerConcept* impl; - int repeats; - }; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_CHRONOMETER_HPP_INCLUDED - - -// Adapted from donated nonius code. - -#ifndef CATCH_ENVIRONMENT_HPP_INCLUDED -#define CATCH_ENVIRONMENT_HPP_INCLUDED - - -namespace Catch { - namespace Benchmark { - template - struct EnvironmentEstimate { - Duration mean; - OutlierClassification outliers; - - template - operator EnvironmentEstimate() const { - return { mean, outliers }; - } - }; - template - struct Environment { - using clock_type = Clock; - EnvironmentEstimate> clock_resolution; - EnvironmentEstimate> clock_cost; - }; - } // namespace Benchmark -} // namespace Catch - -#endif // CATCH_ENVIRONMENT_HPP_INCLUDED - + } // namespace Detail -// Adapted from donated nonius code. + struct Chronometer { + public: + template + void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable()); } -#ifndef CATCH_EXECUTION_PLAN_HPP_INCLUDED -#define CATCH_EXECUTION_PLAN_HPP_INCLUDED + int runs() const { return repeats; } + Chronometer(Detail::ChronometerConcept& meter, int repeats_) + : impl(&meter) + , repeats(repeats_) {} + private: + template + void measure(Fun&& fun, std::false_type) { + measure([&fun](int) { return fun(); }, std::true_type()); + } -// Adapted from donated nonius code. + template + void measure(Fun&& fun, std::true_type) { + Detail::optimizer_barrier(); + impl->start(); + for (int i = 0; i < repeats; ++i) invoke_deoptimized(fun, i); + impl->finish(); + Detail::optimizer_barrier(); + } -#ifndef CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED -#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED + Detail::ChronometerConcept* impl; + int repeats; + }; + } // namespace Benchmark +} // namespace Catch +#endif // CATCH_CHRONOMETER_HPP_INCLUDED #include @@ -2173,6 +1730,57 @@ namespace Catch { +// Adapted from donated nonius code. + +#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED +#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + +namespace Catch { + namespace Benchmark { + namespace Detail { + template + struct CompleteType { using type = T; }; + template <> + struct CompleteType { struct type {}; }; + + template + using CompleteType_t = typename CompleteType::type; + + template + struct CompleteInvoker { + template + static Result invoke(Fun&& fun, Args&&... args) { + return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + } + }; + template <> + struct CompleteInvoker { + template + static CompleteType_t invoke(Fun&& fun, Args&&... args) { + CATCH_FORWARD(fun)(CATCH_FORWARD(args)...); + return {}; + } + }; + + // invoke and not return void :( + template + CompleteType_t> complete_invoke(Fun&& fun, Args&&... args) { + return CompleteInvoker>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...); + } + + } // namespace Detail + + template + Detail::CompleteType_t> user_code(Fun&& fun) { + return Detail::complete_invoke(CATCH_FORWARD(fun)); + } + } // namespace Benchmark +} // namespace Catch + +#endif // CATCH_COMPLETE_INVOKE_HPP_INCLUDED + + // Adapted from donated nonius code. #ifndef CATCH_TIMING_HPP_INCLUDED @@ -2259,8 +1867,7 @@ namespace Catch { #endif // CATCH_RUN_FOR_AT_LEAST_HPP_INCLUDED -#include -#include +#include namespace Catch { namespace Benchmark { @@ -2283,14 +1890,17 @@ namespace Catch { Detail::run_for_at_least(std::chrono::duration_cast>(warmup_time), warmup_iterations, Detail::repeat(now{})); std::vector> times; - times.reserve(cfg.benchmarkSamples()); - std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] { + const auto num_samples = cfg.benchmarkSamples(); + times.reserve( num_samples ); + for ( size_t i = 0; i < num_samples; ++i ) { Detail::ChronometerModel model; - this->benchmark(Chronometer(model, iterations_per_sample)); + this->benchmark( Chronometer( model, iterations_per_sample ) ); auto sample_time = model.elapsed() - env.clock_cost.mean; - if (sample_time < FloatDuration::zero()) sample_time = FloatDuration::zero(); - return sample_time / iterations_per_sample; - }); + if ( sample_time < FloatDuration::zero() ) { + sample_time = FloatDuration::zero(); + } + times.push_back(sample_time / iterations_per_sample); + } return times; } }; @@ -2315,8 +1925,6 @@ namespace Catch { #include #include -#include -#include #include namespace Catch { @@ -2330,39 +1938,17 @@ namespace Catch { double weighted_average_quantile(int k, int q, std::vector::iterator first, std::vector::iterator last); - template - OutlierClassification classify_outliers(Iterator first, Iterator last) { - std::vector copy(first, last); - - auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end()); - auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end()); - auto iqr = q3 - q1; - auto los = q1 - (iqr * 3.); - auto lom = q1 - (iqr * 1.5); - auto him = q3 + (iqr * 1.5); - auto his = q3 + (iqr * 3.); - - OutlierClassification o; - for (; first != last; ++first) { - auto&& t = *first; - if (t < los) ++o.low_severe; - else if (t < lom) ++o.low_mild; - else if (t > his) ++o.high_severe; - else if (t > him) ++o.high_mild; - ++o.samples_seen; - } - return o; - } + OutlierClassification + classify_outliers( std::vector::const_iterator first, + std::vector::const_iterator last ); - template - double mean(Iterator first, Iterator last) { - auto count = last - first; - double sum = std::accumulate(first, last, 0.); - return sum / static_cast(count); - } + double mean( std::vector::const_iterator first, + std::vector::const_iterator last ); - template - sample jackknife(Estimator&& estimator, Iterator first, Iterator last) { + template + sample jackknife(Estimator&& estimator, + std::vector::iterator first, + std::vector::iterator last) { auto n = static_cast(last - first); auto second = first; ++second; @@ -2385,8 +1971,12 @@ namespace Catch { double normal_quantile(double p); - template - Estimate bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { + template + Estimate bootstrap( double confidence_level, + std::vector::iterator first, + std::vector::iterator last, + sample const& resample, + Estimator&& estimator ) { auto n_samples = last - first; double point = estimator(first, last); @@ -2395,13 +1985,13 @@ namespace Catch { sample jack = jackknife(estimator, first, last); double jack_mean = mean(jack.begin(), jack.end()); - double sum_squares, sum_cubes; - std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair sqcb, double x) -> std::pair { - auto d = jack_mean - x; - auto d2 = d * d; - auto d3 = d2 * d; - return { sqcb.first + d2, sqcb.second + d3 }; - }); + double sum_squares = 0, sum_cubes = 0; + for (double x : jack) { + auto difference = jack_mean - x; + auto square = difference * difference; + auto cube = square * difference; + sum_squares += square; sum_cubes += cube; + } double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5)); long n = static_cast(resample.size()); @@ -2428,15 +2018,16 @@ namespace Catch { return { point, resample[lo], resample[hi], confidence_level }; } - double outlier_variance(Estimate mean, Estimate stddev, int n); - struct bootstrap_analysis { Estimate mean; Estimate standard_deviation; double outlier_variance; }; - bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector::iterator first, std::vector::iterator last); + bootstrap_analysis analyse_samples(double confidence_level, + unsigned int n_resamples, + std::vector::iterator first, + std::vector::iterator last); } // namespace Detail } // namespace Benchmark } // namespace Catch @@ -2444,7 +2035,6 @@ namespace Catch { #endif // CATCH_STATS_HPP_INCLUDED #include -#include #include #include @@ -2455,26 +2045,29 @@ namespace Catch { std::vector resolution(int k) { std::vector> times; times.reserve(static_cast(k + 1)); - std::generate_n(std::back_inserter(times), k + 1, now{}); + for ( int i = 0; i < k + 1; ++i ) { + times.push_back( Clock::now() ); + } std::vector deltas; deltas.reserve(static_cast(k)); - std::transform(std::next(times.begin()), times.end(), times.begin(), - std::back_inserter(deltas), - [](TimePoint a, TimePoint b) { return static_cast((a - b).count()); }); + for ( size_t idx = 1; idx < times.size(); ++idx ) { + deltas.push_back( static_cast( + ( times[idx] - times[idx - 1] ).count() ) ); + } return deltas; } - const auto warmup_iterations = 10000; - const auto warmup_time = std::chrono::milliseconds(100); - const auto minimum_ticks = 1000; - const auto warmup_seed = 10000; - const auto clock_resolution_estimation_time = std::chrono::milliseconds(500); - const auto clock_cost_estimation_time_limit = std::chrono::seconds(1); - const auto clock_cost_estimation_tick_limit = 100000; - const auto clock_cost_estimation_time = std::chrono::milliseconds(10); - const auto clock_cost_estimation_iterations = 10000; + constexpr auto warmup_iterations = 10000; + constexpr auto warmup_time = std::chrono::milliseconds(100); + constexpr auto minimum_ticks = 1000; + constexpr auto warmup_seed = 10000; + constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500); + constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1); + constexpr auto clock_cost_estimation_tick_limit = 100000; + constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10); + constexpr auto clock_cost_estimation_iterations = 10000; template int warmup() { @@ -2509,9 +2102,11 @@ namespace Catch { std::vector times; int nsamples = static_cast(std::ceil(time_limit / r.elapsed)); times.reserve(static_cast(nsamples)); - std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] { - return static_cast((time_clock(r.iterations) / r.iterations).count()); - }); + for ( int s = 0; s < nsamples; ++s ) { + times.push_back( static_cast( + ( time_clock( r.iterations ) / r.iterations ) + .count() ) ); + } return { FloatDuration(mean(times.begin(), times.end())), classify_outliers(times.begin(), times.end()), @@ -2559,9 +2154,7 @@ namespace Catch { #define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED -#include #include -#include namespace Catch { namespace Benchmark { @@ -2577,7 +2170,9 @@ namespace Catch { operator SampleAnalysis() const { std::vector samples2; samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); }); + for (auto const& d : samples) { + samples2.push_back(Duration2(d)); + } return { CATCH_MOVE(samples2), mean, @@ -2592,8 +2187,6 @@ namespace Catch { #endif // CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED -#include -#include #include namespace Catch { @@ -2604,7 +2197,9 @@ namespace Catch { if (!cfg.benchmarkNoAnalysis()) { std::vector samples; samples.reserve(static_cast(last - first)); - std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); }); + for (auto current = first; current != last; ++current) { + samples.push_back( current->count() ); + } auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end()); auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end()); @@ -2619,7 +2214,10 @@ namespace Catch { }; std::vector samples2; samples2.reserve(samples.size()); - std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); }); + for (auto s : samples) { + samples2.push_back( Duration( s ) ); + } + return { CATCH_MOVE(samples2), wrap_estimate(analysis.mean), @@ -2655,6 +2253,8 @@ namespace Catch { #endif // CATCH_ANALYSE_HPP_INCLUDED #include +#include +#include #include #include #include @@ -2710,7 +2310,7 @@ namespace Catch { auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); BenchmarkStats> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; getResultCapture().benchmarkEnded(stats); - } CATCH_CATCH_ANON (TestFailureException) { + } CATCH_CATCH_ANON (TestFailureException const&) { getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); } CATCH_CATCH_ALL{ getResultCapture().benchmarkFailed(translateActiveException()); @@ -2878,6 +2478,7 @@ namespace Catch { #ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED #define CATCH_CONFIG_WCHAR_HPP_INCLUDED + // We assume that WCHAR should be enabled by default, and only disabled // for a shortlist (so far only DJGPP) of compilers. @@ -3101,7 +2702,6 @@ namespace Catch { } // namespace Detail - // If we decide for C++14, change these to enable_if_ts template struct StringMaker { template @@ -3701,73 +3301,210 @@ namespace Catch { return !operator==( lhs, rhs ); } - template ::value>> - friend bool operator != ( Approx const& lhs, T const& rhs ) { - return !operator==( rhs, lhs ); + template ::value>> + friend bool operator != ( Approx const& lhs, T const& rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>> + friend bool operator <= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>> + friend bool operator <= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value < static_cast(rhs) || lhs == rhs; + } + + template ::value>> + friend bool operator >= ( T const& lhs, Approx const& rhs ) { + return static_cast(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>> + friend bool operator >= ( Approx const& lhs, T const& rhs ) { + return lhs.m_value > static_cast(rhs) || lhs == rhs; + } + + template ::value>> + Approx& epsilon( T const& newEpsilon ) { + const auto epsilonAsDouble = static_cast(newEpsilon); + setEpsilon(epsilonAsDouble); + return *this; + } + + template ::value>> + Approx& margin( T const& newMargin ) { + const auto marginAsDouble = static_cast(newMargin); + setMargin(marginAsDouble); + return *this; + } + + template ::value>> + Approx& scale( T const& newScale ) { + m_scale = static_cast(newScale); + return *this; + } + + std::string toString() const; + + private: + double m_epsilon; + double m_margin; + double m_scale; + double m_value; + }; + +namespace literals { + Approx operator ""_a(long double val); + Approx operator ""_a(unsigned long long val); +} // end namespace literals + +template<> +struct StringMaker { + static std::string convert(Catch::Approx const& value); +}; + +} // end namespace Catch + +#endif // CATCH_APPROX_HPP_INCLUDED + + +#ifndef CATCH_ASSERTION_INFO_HPP_INCLUDED +#define CATCH_ASSERTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED +#define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + +#include +#include + +namespace Catch { + + struct SourceLineInfo { + + SourceLineInfo() = delete; + constexpr SourceLineInfo( char const* _file, std::size_t _line ) noexcept: + file( _file ), + line( _line ) + {} + + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + + friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info); + }; +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +#endif // CATCH_SOURCE_LINE_INFO_HPP_INCLUDED + +namespace Catch { + + struct AssertionInfo { + // AssertionInfo() = delete; + + StringRef macroName; + SourceLineInfo lineInfo; + StringRef capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + +} // end namespace Catch + +#endif // CATCH_ASSERTION_INFO_HPP_INCLUDED + + +#ifndef CATCH_ASSERTION_RESULT_HPP_INCLUDED +#define CATCH_ASSERTION_RESULT_HPP_INCLUDED + + + +#ifndef CATCH_LAZY_EXPR_HPP_INCLUDED +#define CATCH_LAZY_EXPR_HPP_INCLUDED + +#include + +namespace Catch { + + class ITransientExpression; + + class LazyExpression { + friend class AssertionHandler; + friend struct AssertionStats; + friend class RunContext; + + ITransientExpression const* m_transientExpression = nullptr; + bool m_isNegated; + public: + LazyExpression( bool isNegated ): + m_isNegated(isNegated) + {} + LazyExpression(LazyExpression const& other) = default; + LazyExpression& operator = ( LazyExpression const& ) = delete; + + explicit operator bool() const { + return m_transientExpression != nullptr; } - template ::value>> - friend bool operator <= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) < rhs.m_value || lhs == rhs; - } + friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&; + }; - template ::value>> - friend bool operator <= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value < static_cast(rhs) || lhs == rhs; - } +} // namespace Catch - template ::value>> - friend bool operator >= ( T const& lhs, Approx const& rhs ) { - return static_cast(lhs) > rhs.m_value || lhs == rhs; - } +#endif // CATCH_LAZY_EXPR_HPP_INCLUDED - template ::value>> - friend bool operator >= ( Approx const& lhs, T const& rhs ) { - return lhs.m_value > static_cast(rhs) || lhs == rhs; - } +#include - template ::value>> - Approx& epsilon( T const& newEpsilon ) { - const auto epsilonAsDouble = static_cast(newEpsilon); - setEpsilon(epsilonAsDouble); - return *this; - } +namespace Catch { - template ::value>> - Approx& margin( T const& newMargin ) { - const auto marginAsDouble = static_cast(newMargin); - setMargin(marginAsDouble); - return *this; - } + struct AssertionResultData + { + AssertionResultData() = delete; - template ::value>> - Approx& scale( T const& newScale ) { - m_scale = static_cast(newScale); - return *this; - } + AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression ); - std::string toString() const; + std::string message; + mutable std::string reconstructedExpression; + LazyExpression lazyExpression; + ResultWas::OfType resultType; - private: - double m_epsilon; - double m_margin; - double m_scale; - double m_value; + std::string reconstructExpression() const; }; -namespace literals { - Approx operator ""_a(long double val); - Approx operator ""_a(unsigned long long val); -} // end namespace literals + class AssertionResult { + public: + AssertionResult() = delete; + AssertionResult( AssertionInfo const& info, AssertionResultData&& data ); -template<> -struct StringMaker { - static std::string convert(Catch::Approx const& value); -}; + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + StringRef getMessage() const; + SourceLineInfo getSourceInfo() const; + StringRef getTestMacroName() const; + + //protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; } // end namespace Catch -#endif // CATCH_APPROX_HPP_INCLUDED +#endif // CATCH_ASSERTION_RESULT_HPP_INCLUDED #ifndef CATCH_CONFIG_HPP_INCLUDED @@ -3934,6 +3671,7 @@ namespace Catch { #ifndef CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED + #include namespace Catch { @@ -3942,35 +3680,50 @@ namespace Catch { template class Optional { public: - Optional() : nullableValue( nullptr ) {} - Optional( T const& _value ) - : nullableValue( new( storage ) T( _value ) ) - {} - Optional( Optional const& _other ) - : nullableValue( _other ? new( storage ) T( *_other ) : nullptr ) - {} + Optional(): nullableValue( nullptr ) {} + ~Optional() { reset(); } + + Optional( T const& _value ): + nullableValue( new ( storage ) T( _value ) ) {} + Optional( T&& _value ): + nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {} - ~Optional() { + Optional& operator=( T const& _value ) { + reset(); + nullableValue = new ( storage ) T( _value ); + return *this; + } + Optional& operator=( T&& _value ) { reset(); + nullableValue = new ( storage ) T( CATCH_MOVE( _value ) ); + return *this; } - Optional& operator= ( Optional const& _other ) { - if( &_other != this ) { + Optional( Optional const& _other ): + nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {} + Optional( Optional&& _other ): + nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) ) + : nullptr ) {} + + Optional& operator=( Optional const& _other ) { + if ( &_other != this ) { reset(); - if( _other ) - nullableValue = new( storage ) T( *_other ); + if ( _other ) { nullableValue = new ( storage ) T( *_other ); } } return *this; } - Optional& operator = ( T const& _value ) { - reset(); - nullableValue = new( storage ) T( _value ); + Optional& operator=( Optional&& _other ) { + if ( &_other != this ) { + reset(); + if ( _other ) { + nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) ); + } + } return *this; } void reset() { - if( nullableValue ) - nullableValue->~T(); + if ( nullableValue ) { nullableValue->~T(); } nullableValue = nullptr; } @@ -4017,7 +3770,7 @@ namespace Catch { } private: - T *nullableValue; + T* nullableValue; alignas(alignof(T)) char storage[sizeof(T)]; }; @@ -4424,10 +4177,10 @@ namespace Catch { // as well as // << stuff +StreamEndStop struct StreamEndStop { - StringRef operator+() const { return StringRef(); } + constexpr StringRef operator+() const { return StringRef(); } template - friend T const& operator+( T const& value, StreamEndStop ) { + constexpr friend T const& operator+( T const& value, StreamEndStop ) { return value; } }; @@ -4436,12 +4189,47 @@ namespace Catch { #endif // CATCH_STREAM_END_STOP_HPP_INCLUDED + +#ifndef CATCH_MESSAGE_INFO_HPP_INCLUDED +#define CATCH_MESSAGE_INFO_HPP_INCLUDED + + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( StringRef _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + StringRef macroName; + std::string message; + SourceLineInfo lineInfo; + ResultWas::OfType type; + unsigned int sequence; + + bool operator == (MessageInfo const& other) const { + return sequence == other.sequence; + } + bool operator < (MessageInfo const& other) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + +} // end namespace Catch + +#endif // CATCH_MESSAGE_INFO_HPP_INCLUDED + #include #include namespace Catch { struct SourceLineInfo; + class IResultCapture; struct MessageStream { @@ -4482,7 +4270,7 @@ namespace Catch { class Capturer { std::vector m_messages; - IResultCapture& m_resultCapture = getResultCapture(); + IResultCapture& m_resultCapture; size_t m_captured = 0; public: Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ); @@ -4518,7 +4306,10 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \ - Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \ + Catch::Capturer varName( macroName##_catch_sr, \ + CATCH_INTERNAL_LINEINFO, \ + Catch::ResultWas::Info, \ + #__VA_ARGS__##_catch_sr ); \ varName.captureValues( 0, __VA_ARGS__ ) /////////////////////////////////////////////////////////////////////////////// @@ -4566,6 +4357,75 @@ namespace Catch { #endif // CATCH_MESSAGE_HPP_INCLUDED +#ifndef CATCH_SECTION_INFO_HPP_INCLUDED +#define CATCH_SECTION_INFO_HPP_INCLUDED + + + +#ifndef CATCH_TOTALS_HPP_INCLUDED +#define CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts operator - ( Counts const& other ) const; + Counts& operator += ( Counts const& other ); + + std::uint64_t total() const; + bool allPassed() const; + bool allOk() const; + + std::uint64_t passed = 0; + std::uint64_t failed = 0; + std::uint64_t failedButOk = 0; + std::uint64_t skipped = 0; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const; + Totals& operator += ( Totals const& other ); + + Totals delta( Totals const& prevTotals ) const; + + Counts assertions; + Counts testCases; + }; +} + +#endif // CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct SectionInfo { + // The last argument is ignored, so that people can write + // SECTION("ShortName", "Proper description that is long") and + // still use the `-c` flag comfortably. + SectionInfo( SourceLineInfo const& _lineInfo, std::string _name, + const char* const = nullptr ): + name(CATCH_MOVE(_name)), + lineInfo(_lineInfo) + {} + + std::string name; + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +#endif // CATCH_SECTION_INFO_HPP_INCLUDED + + #ifndef CATCH_SESSION_HPP_INCLUDED #define CATCH_SESSION_HPP_INCLUDED @@ -5841,8 +5701,6 @@ namespace Catch { namespace Catch { - class IResultCapture; - struct AssertionReaction { bool shouldDebugBreak = false; bool shouldThrow = false; @@ -5883,7 +5741,6 @@ namespace Catch { void handleUnexpectedInflightException(); void complete(); - void setCompleted(); // query auto allowThrows() const -> bool; @@ -5895,6 +5752,19 @@ namespace Catch { #endif // CATCH_ASSERTION_HANDLER_HPP_INCLUDED + +#ifndef CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED +#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + + +#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) + #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr +#else + #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr +#endif + +#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED + // We need this suppression to leak, because it took until GCC 10 // for the front end to handle local suppression via _Pragma properly #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && __GNUC__ <= 9 @@ -5903,12 +5773,6 @@ namespace Catch { #if !defined(CATCH_CONFIG_DISABLE) -#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION) - #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__ -#else - #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION" -#endif - #if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) /////////////////////////////////////////////////////////////////////////////// @@ -5976,6 +5840,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -5996,6 +5861,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(expr); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6022,6 +5888,7 @@ namespace Catch { if( catchAssertionHandler.allowThrows() ) \ try { \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \ CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \ static_cast(__VA_ARGS__); \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ @@ -6045,6 +5912,34 @@ namespace Catch { + +/** \file + * Wrapper for the STATIC_ANALYSIS_SUPPORT configuration option + * + * Some of Catch2's macros can be defined differently to work better with + * static analysis tools, like clang-tidy or coverity. + * Currently the main use case is to show that `SECTION`s are executed + * exclusively, and not all in one run of a `TEST_CASE`. + */ + +#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED +#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + + +#if defined(__clang_analyzer__) || defined(__COVERITY__) + #define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT +#endif + +#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \ + !defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) +# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT +#endif + + +#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED + + #ifndef CATCH_TIMER_HPP_INCLUDED #define CATCH_TIMER_HPP_INCLUDED @@ -6089,17 +5984,63 @@ namespace Catch { } // end namespace Catch -#define INTERNAL_CATCH_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \ + catch_internal_Section ) = \ + Catch::SectionInfo( \ + CATCH_INTERNAL_LINEINFO, \ + ( Catch::ReusableStringStream() << __VA_ARGS__ ) \ + .str() ) ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#else + +// These section definitions imply that at most one section at one level +// will be intered (because only one section's __LINE__ can be equal to +// the dummy `catchInternalSectionHint` variable from `TEST_CASE`). + +namespace Catch { + namespace Detail { + // Intentionally without linkage, as it should only be used as a dummy + // symbol for static analysis. + int GetNewSectionHint(); + } // namespace Detail +} // namespace Catch + + +# define INTERNAL_CATCH_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ + catchInternalSectionHint, \ + catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ + catchInternalPreviousSectionHint == __LINE__ ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +#endif -#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION #endif // CATCH_SECTION_HPP_INCLUDED @@ -6109,42 +6050,20 @@ namespace Catch { -#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED - -#include +#ifndef CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED +#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED namespace Catch { - class TestSpec; - struct TestCaseInfo; - class ITestInvoker { public: - virtual void invoke () const = 0; + virtual void invoke() const = 0; virtual ~ITestInvoker(); // = default }; - class TestCaseHandle; - class IConfig; - - class ITestCaseRegistry { - public: - virtual ~ITestCaseRegistry(); // = default - // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later - virtual std::vector const& getAllInfos() const = 0; - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} +} // namespace Catch -#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED #ifndef CATCH_PREPROCESSOR_REMOVE_PARENS_HPP_INCLUDED @@ -6216,6 +6135,9 @@ struct AutoReg : Detail::NonCopyable { void TestName::test() #endif + +#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT) + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ static void TestName(); \ @@ -6228,13 +6150,40 @@ struct AutoReg : Detail::NonCopyable { #define INTERNAL_CATCH_TESTCASE( ... ) \ INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ ) - /////////////////////////////////////////////////////////////////////////////// - #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ - namespace{ const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#else // ^^ !CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT | vv CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT + + +// Dummy registrator for the dumy test case macros +namespace Catch { + namespace Detail { + struct DummyUse { + DummyUse( void ( * )( int ) ); + }; + } // namespace Detail +} // namespace Catch + +// Note that both the presence of the argument and its exact name are +// necessary for the section support. + +// We provide a shadowed variable so that a `SECTION` inside non-`TEST_CASE` +// tests can compile. The redefined `TEST_CASE` shadows this with param. +static int catchInternalSectionHint = 0; + +# define INTERNAL_CATCH_TESTCASE2( fname ) \ + static void fname( int ); \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ + dummyUser )( &fname ); \ + CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ + static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +# define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( dummyFunction ) ) + + +#endif // CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ @@ -6245,13 +6194,33 @@ struct AutoReg : Detail::NonCopyable { struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \ void test(); \ }; \ - const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &TestName::test ), \ + CATCH_INTERNAL_LINEINFO, \ + #ClassName##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \ } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \ void TestName::test() #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ + namespace { \ + const Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( \ + Catch::makeTestInvoker( &QualifiedMethod ), \ + CATCH_INTERNAL_LINEINFO, \ + "&" #QualifiedMethod##_catch_sr, \ + Catch::NameAndTags{ __VA_ARGS__ } ); \ + } /* NOLINT */ \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + + /////////////////////////////////////////////////////////////////////////////// #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ do { \ @@ -6878,7 +6847,7 @@ struct AutoReg : Detail::NonCopyable { void reg_tests() { \ size_t index = 0; \ using expander = size_t[]; \ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFunc ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */\ } \ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \ @@ -7013,7 +6982,7 @@ struct AutoReg : Detail::NonCopyable { void reg_tests(){\ size_t index = 0;\ using expander = size_t[];\ - (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(INTERNAL_CATCH_STRINGIZE(TmplList)) + " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ + (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName##_catch_sr, Catch::NameAndTags{ Name " - " INTERNAL_CATCH_STRINGIZE(TmplList) " - " + std::to_string(index), Tags } ), index++)... };/* NOLINT */ \ }\ };\ static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\ @@ -7294,6 +7263,10 @@ namespace Catch { #include namespace Catch { + namespace Detail { + void registerTranslatorImpl( + Detail::unique_ptr&& translator ); + } class ExceptionTranslatorRegistrar { template @@ -7327,9 +7300,9 @@ namespace Catch { public: template ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) { - getMutableRegistryHub().registerTranslator( - Detail::make_unique>(translateFunction) - ); + Detail::registerTranslatorImpl( + Detail::make_unique>( + translateFunction ) ); } }; @@ -7401,8 +7374,8 @@ namespace Catch { #define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MAJOR 3 -#define CATCH_VERSION_MINOR 3 -#define CATCH_VERSION_PATCH 1 +#define CATCH_VERSION_MINOR 4 +#define CATCH_VERSION_PATCH 0 #endif // CATCH_VERSION_MACROS_HPP_INCLUDED @@ -7754,16 +7727,19 @@ namespace Detail { } // namespace Generators } // namespace Catch +#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr +#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__) + #define GENERATE( ... ) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ CATCH_INTERNAL_LINEINFO, \ [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_COPY( ... ) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ CATCH_INTERNAL_LINEINFO, \ [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) #define GENERATE_REF( ... ) \ - Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ + Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \ CATCH_INTERNAL_LINEINFO, \ [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace) @@ -8248,28 +8224,254 @@ GeneratorWrapper from_range(Container const& cnt) { } // namespace Catch -#endif // CATCH_GENERATORS_RANGE_HPP_INCLUDED +#endif // CATCH_GENERATORS_RANGE_HPP_INCLUDED + +#endif // CATCH_GENERATORS_ALL_HPP_INCLUDED + + +/** \file + * This is a convenience header for Catch2's interfaces. It includes + * **all** of Catch2 headers related to interfaces. + * + * Generally the Catch2 users should use specific includes they need, + * but this header can be used instead for ease-of-experimentation, or + * just plain convenience, at the cost of somewhat increased compilation + * times. + * + * When a new header is added to either the `interfaces` folder, or to + * the corresponding internal subfolder, it should be added here. + */ + + +#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED +#define CATCH_INTERFACES_ALL_HPP_INCLUDED + + + +#ifndef CATCH_INTERFACES_REPORTER_HPP_INCLUDED +#define CATCH_INTERFACES_REPORTER_HPP_INCLUDED + + + +#ifndef CATCH_TEST_RUN_INFO_HPP_INCLUDED +#define CATCH_TEST_RUN_INFO_HPP_INCLUDED + + +namespace Catch { + + struct TestRunInfo { + constexpr TestRunInfo(StringRef _name) : name(_name) {} + StringRef name; + }; + +} // end namespace Catch + +#endif // CATCH_TEST_RUN_INFO_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + struct ReporterDescription; + struct ListenerDescription; + struct TagInfo; + struct TestCaseInfo; + class TestCaseHandle; + class IConfig; + class IStream; + enum class ColourMode : std::uint8_t; + + struct ReporterConfig { + ReporterConfig( IConfig const* _fullConfig, + Detail::unique_ptr _stream, + ColourMode colourMode, + std::map customOptions ); + + ReporterConfig( ReporterConfig&& ) = default; + ReporterConfig& operator=( ReporterConfig&& ) = default; + ~ReporterConfig(); // = default + + Detail::unique_ptr takeStream() &&; + IConfig const* fullConfig() const; + ColourMode colourMode() const; + std::map const& customOptions() const; + + private: + Detail::unique_ptr m_stream; + IConfig const* m_fullConfig; + ColourMode m_colourMode; + std::map m_customOptions; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ); + + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = delete; + AssertionStats& operator = ( AssertionStats && ) = delete; + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo&& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ); + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string&& _stdOut, + std::string&& _stdErr, + bool _aborting ); + + TestCaseInfo const * testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ); + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + //! By setting up its preferences, a reporter can modify Catch2's behaviour + //! in some regards, e.g. it can request Catch2 to capture writes to + //! stdout/stderr during test execution, and pass them to the reporter. + struct ReporterPreferences { + //! Catch2 should redirect writes to stdout and pass them to the + //! reporter + bool shouldRedirectStdOut = false; + //! Catch2 should call `Reporter::assertionEnded` even for passing + //! assertions + bool shouldReportAllAssertions = false; + }; + + /** + * The common base for all reporters and event listeners + * + * Implementing classes must also implement: + * + * //! User-friendly description of the reporter/listener type + * static std::string getDescription() + * + * Generally shouldn't be derived from by users of Catch2 directly, + * instead they should derive from one of the utility bases that + * derive from this class. + */ + class IEventListener { + protected: + //! Derived classes can set up their preferences here + ReporterPreferences m_preferences; + //! The test run's config as filled in from CLI and defaults + IConfig const* m_config; + + public: + IEventListener( IConfig const* config ): m_config( config ) {} + + virtual ~IEventListener(); // = default; + + // Implementing class must also provide the following static methods: + // static std::string getDescription(); + + ReporterPreferences const& getPreferences() const { + return m_preferences; + } + + //! Called when no test cases match provided test spec + virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0; + //! Called for all invalid test specs from the cli + virtual void reportInvalidTestSpec( StringRef invalidArgument ) = 0; + + /** + * Called once in a testing run before tests are started + * + * Not called if tests won't be run (e.g. only listing will happen) + */ + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0; + //! Called when a `SECTION` is being entered. Not called for skipped sections + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + //! Called when user-code is being probed before the actual benchmark runs + virtual void benchmarkPreparing( StringRef benchmarkName ) = 0; + //! Called after probe but before the user-code is being benchmarked + virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0; + //! Called with the benchmark results if benchmark successfully finishes + virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0; + //! Called if running the benchmarks fails for any reason + virtual void benchmarkFailed( StringRef benchmarkName ) = 0; + + //! Called before assertion success/failure is evaluated + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + //! Called after assertion was fully evaluated + virtual void assertionEnded( AssertionStats const& assertionStats ) = 0; -#endif // CATCH_GENERATORS_ALL_HPP_INCLUDED + //! Called after a `SECTION` has finished running + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections) + virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0; + //! Called _once_ for each TEST_CASE, no matter how many times it is entered + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + /** + * Called once after all tests in a testing run are finished + * + * Not called if tests weren't run (e.g. only listings happened) + */ + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + /** + * Called with test cases that are skipped due to the test run aborting. + * NOT called for test cases that are explicitly skipped using the `SKIP` macro. + * + * Deprecated - will be removed in the next major release. + */ + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; -/** \file - * This is a convenience header for Catch2's interfaces. It includes - * **all** of Catch2 headers related to interfaces. - * - * Generally the Catch2 users should use specific includes they need, - * but this header can be used instead for ease-of-experimentation, or - * just plain convenience, at the cost of somewhat increased compilation - * times. - * - * When a new header is added to either the `interfaces` folder, or to - * the corresponding internal subfolder, it should be added here. - */ + //! Called if a fatal error (signal/structured exception) occurred + virtual void fatalErrorEncountered( StringRef error ) = 0; + //! Writes out information about provided reporters using reporter-specific format + virtual void listReporters(std::vector const& descriptions) = 0; + //! Writes out the provided listeners descriptions using reporter-specific format + virtual void listListeners(std::vector const& descriptions) = 0; + //! Writes out information about provided tests using reporter-specific format + virtual void listTests(std::vector const& tests) = 0; + //! Writes out information about the provided tags using reporter-specific format + virtual void listTags(std::vector const& tags) = 0; + }; + using IEventListenerPtr = Detail::unique_ptr; -#ifndef CATCH_INTERFACES_ALL_HPP_INCLUDED -#define CATCH_INTERFACES_ALL_HPP_INCLUDED +} // end namespace Catch +#endif // CATCH_INTERFACES_REPORTER_HPP_INCLUDED #ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED @@ -8310,89 +8512,79 @@ namespace Catch { #endif // CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED -#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED +#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED + +#include +namespace Catch { + struct TagAlias; -#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED -#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED + class ITagAliasRegistry { + public: + virtual ~ITagAliasRegistry(); // = default + // Nullptr if not present + virtual TagAlias const* find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + static ITagAliasRegistry const& get(); + }; -namespace Catch { - namespace Detail { - //! Provides case-insensitive `op<` semantics when called - struct CaseInsensitiveLess { - bool operator()( StringRef lhs, - StringRef rhs ) const; - }; +} // end namespace Catch - //! Provides case-insensitive `op==` semantics when called - struct CaseInsensitiveEqualTo { - bool operator()( StringRef lhs, - StringRef rhs ) const; - }; +#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED - } // namespace Detail -} // namespace Catch -#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED +#ifndef CATCH_INTERFACES_TESTCASE_HPP_INCLUDED +#define CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#include #include -#include namespace Catch { + struct TestCaseInfo; + class TestCaseHandle; class IConfig; - class IEventListener; - using IEventListenerPtr = Detail::unique_ptr; - class IReporterFactory; - using IReporterFactoryPtr = Detail::unique_ptr; - struct ReporterConfig; - class EventListenerFactory; - - class IReporterRegistry { + class ITestCaseRegistry { public: - using FactoryMap = std::map; - using Listeners = std::vector>; - - virtual ~IReporterRegistry(); // = default - virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0; - virtual FactoryMap const& getFactories() const = 0; - virtual Listeners const& getListeners() const = 0; + virtual ~ITestCaseRegistry(); // = default + // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later + virtual std::vector const& getAllInfos() const = 0; + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; }; -} // end namespace Catch - -#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED - +} -#ifndef CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED -#define CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED +#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED -#include +#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED -namespace Catch { - struct TagAlias; +#ifndef CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED +#define CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED - class ITagAliasRegistry { - public: - virtual ~ITagAliasRegistry(); // = default - // Nullptr if not present - virtual TagAlias const* find( std::string const& alias ) const = 0; - virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; - static ITagAliasRegistry const& get(); - }; +namespace Catch { + namespace Detail { + //! Provides case-insensitive `op<` semantics when called + struct CaseInsensitiveLess { + bool operator()( StringRef lhs, + StringRef rhs ) const; + }; -} // end namespace Catch + //! Provides case-insensitive `op==` semantics when called + struct CaseInsensitiveEqualTo { + bool operator()( StringRef lhs, + StringRef rhs ) const; + }; -#endif // CATCH_INTERFACES_TAG_ALIAS_REGISTRY_HPP_INCLUDED + } // namespace Detail +} // namespace Catch -#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED +#endif // CATCH_CASE_INSENSITIVE_COMPARISONS_HPP_INCLUDED @@ -8434,6 +8626,7 @@ namespace Catch { #ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED #define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED + #if defined(_MSC_VER) # if _MSC_VER >= 1900 // Visual Studio 2015 or newer # define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS @@ -8724,7 +8917,6 @@ namespace Catch { ~ExceptionTranslatorRegistry() override; void registerTranslator( Detail::unique_ptr&& translator ); std::string translateActiveException() const override; - std::string tryTranslators() const; private: ExceptionTranslators m_translators; @@ -8919,6 +9111,139 @@ namespace Detail { #endif // CATCH_GETENV_HPP_INCLUDED +#ifndef CATCH_IS_PERMUTATION_HPP_INCLUDED +#define CATCH_IS_PERMUTATION_HPP_INCLUDED + +#include +#include + +namespace Catch { + namespace Detail { + + template + ForwardIter find_sentinel( ForwardIter start, + Sentinel sentinel, + T const& value, + Comparator cmp ) { + while ( start != sentinel ) { + if ( cmp( *start, value ) ) { break; } + ++start; + } + return start; + } + + template + std::ptrdiff_t count_sentinel( ForwardIter start, + Sentinel sentinel, + T const& value, + Comparator cmp ) { + std::ptrdiff_t count = 0; + while ( start != sentinel ) { + if ( cmp( *start, value ) ) { ++count; } + ++start; + } + return count; + } + + template + std::enable_if_t::value, + std::ptrdiff_t> + sentinel_distance( ForwardIter iter, const Sentinel sentinel ) { + std::ptrdiff_t dist = 0; + while ( iter != sentinel ) { + ++iter; + ++dist; + } + return dist; + } + + template + std::ptrdiff_t sentinel_distance( ForwardIter first, + ForwardIter last ) { + return std::distance( first, last ); + } + + template + bool check_element_counts( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { + auto cursor = first_1; + while ( cursor != end_1 ) { + if ( find_sentinel( first_1, cursor, *cursor, cmp ) == + cursor ) { + // we haven't checked this element yet + const auto count_in_range_2 = + count_sentinel( first_2, end_2, *cursor, cmp ); + // Not a single instance in 2nd range, so it cannot be a + // permutation of 1st range + if ( count_in_range_2 == 0 ) { return false; } + + const auto count_in_range_1 = + count_sentinel( cursor, end_1, *cursor, cmp ); + if ( count_in_range_1 != count_in_range_2 ) { + return false; + } + } + + ++cursor; + } + + return true; + } + + template + bool is_permutation( ForwardIter1 first_1, + const Sentinel1 end_1, + ForwardIter2 first_2, + const Sentinel2 end_2, + Comparator cmp ) { + // TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types + // TODO: Comparator has to be "both sides", e.g. a == b => b == a + // This skips shared prefix of the two ranges + while (first_1 != end_1 && first_2 != end_2 && cmp(*first_1, *first_2)) { + ++first_1; + ++first_2; + } + + // We need to handle case where at least one of the ranges has no more elements + if (first_1 == end_1 || first_2 == end_2) { + return first_1 == end_1 && first_2 == end_2; + } + + // pair counting is n**2, so we pay linear walk to compare the sizes first + auto dist_1 = sentinel_distance( first_1, end_1 ); + auto dist_2 = sentinel_distance( first_2, end_2 ); + + if (dist_1 != dist_2) { return false; } + + // Since we do not try to handle stronger iterators pair (e.g. + // bidir) optimally, the only thing left to do is to check counts in + // the remaining ranges. + return check_element_counts( first_1, end_1, first_2, end_2, cmp ); + } + + } // namespace Detail +} // namespace Catch + +#endif // CATCH_IS_PERMUTATION_HPP_INCLUDED + + #ifndef CATCH_ISTREAM_HPP_INCLUDED #define CATCH_ISTREAM_HPP_INCLUDED @@ -9152,28 +9477,45 @@ namespace Catch { #include +#include +#include namespace Catch { - class ReporterRegistry : public IReporterRegistry { - public: + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; + class IReporterFactory; + using IReporterFactoryPtr = Detail::unique_ptr; + struct ReporterConfig; + class EventListenerFactory; + + class ReporterRegistry { + struct ReporterRegistryImpl; + Detail::unique_ptr m_impl; + public: ReporterRegistry(); - ~ReporterRegistry() override; // = default, out of line to allow fwd decl + ~ReporterRegistry(); // = default; - IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override; + IEventListenerPtr create( std::string const& name, + ReporterConfig&& config ) const; - void registerReporter( std::string const& name, IReporterFactoryPtr factory ); - void registerListener( Detail::unique_ptr factory ); + void registerReporter( std::string const& name, + IReporterFactoryPtr factory ); - FactoryMap const& getFactories() const override; - Listeners const& getListeners() const override; + void + registerListener( Detail::unique_ptr factory ); - private: - FactoryMap m_factories; - Listeners m_listeners; + std::map const& + getFactories() const; + + std::vector> const& + getListeners() const; }; -} + +} // end namespace Catch #endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED @@ -9199,8 +9541,12 @@ namespace TestCaseTracking { NameAndLocation( std::string&& _name, SourceLineInfo const& _location ); friend bool operator==(NameAndLocation const& lhs, NameAndLocation const& rhs) { - return lhs.name == rhs.name - && lhs.location == rhs.location; + // This is a very cheap check that should have a very high hit rate. + // If we get to SourceLineInfo::operator==, we will redo it, but the + // cost of repeating is trivial at that point (we will be paying + // multiple strcmp/memcmps at that point). + if ( lhs.location.line != rhs.location.line ) { return false; } + return lhs.name == rhs.name && lhs.location == rhs.location; } friend bool operator!=(NameAndLocation const& lhs, NameAndLocation const& rhs) { @@ -9224,11 +9570,16 @@ namespace TestCaseTracking { name( name_ ), location( location_ ) {} friend bool operator==( NameAndLocation const& lhs, - NameAndLocationRef rhs ) { + NameAndLocationRef const& rhs ) { + // This is a very cheap check that should have a very high hit rate. + // If we get to SourceLineInfo::operator==, we will redo it, but the + // cost of repeating is trivial at that point (we will be paying + // multiple strcmp/memcmps at that point). + if ( lhs.location.line != rhs.location.line ) { return false; } return StringRef( lhs.name ) == rhs.name && lhs.location == rhs.location; } - friend bool operator==( NameAndLocationRef lhs, + friend bool operator==( NameAndLocationRef const& lhs, NameAndLocation const& rhs ) { return rhs == lhs; } @@ -9279,8 +9630,10 @@ namespace TestCaseTracking { //! Returns true if tracker run to completion (successfully or not) virtual bool isComplete() const = 0; - //! Returns true if tracker run to completion succesfully - bool isSuccessfullyCompleted() const; + //! Returns true if tracker run to completion successfully + bool isSuccessfullyCompleted() const { + return m_runState == CompletedSuccessfully; + } //! Returns true if tracker has started but hasn't been completed bool isOpen() const; //! Returns true iff tracker has started @@ -9298,7 +9651,7 @@ namespace TestCaseTracking { * * Returns nullptr if not found. */ - ITracker* findChild( NameAndLocationRef nameAndLocation ); + ITracker* findChild( NameAndLocationRef const& nameAndLocation ); //! Have any children been added? bool hasChildren() const { return !m_children.empty(); @@ -9339,13 +9692,15 @@ namespace TestCaseTracking { public: ITracker& startRun(); - void endRun(); - void startCycle(); + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } void completeCycle(); bool completedCycle() const; - ITracker& currentTracker(); + ITracker& currentTracker() { return *m_currentTracker; } void setCurrentTracker( ITracker* tracker ); }; @@ -9371,7 +9726,11 @@ namespace TestCaseTracking { class SectionTracker : public TrackerBase { std::vector m_filters; - std::string m_trimmed_name; + // Note that lifetime-wise we piggy back off the name stored in the `ITracker` parent`. + // Currently it allocates owns the name, so this is safe. If it is later refactored + // to not own the name, the name still has to outlive the `ITracker` parent, so + // this should still be safe. + StringRef m_trimmed_name; public: SectionTracker( NameAndLocation&& nameAndLocation, TrackerContext& ctx, ITracker* parent ); @@ -9379,14 +9738,14 @@ namespace TestCaseTracking { bool isComplete() const override; - static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef nameAndLocation ); + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocationRef const& nameAndLocation ); void tryOpen(); void addInitialFilters( std::vector const& filters ); void addNextFilters( std::vector const& filters ); //! Returns filters active in this tracker - std::vector const& getFilters() const; + std::vector const& getFilters() const { return m_filters; } //! Returns whitespace-trimmed name of the tracked section StringRef trimmedName() const; }; @@ -9405,13 +9764,14 @@ using TestCaseTracking::SectionTracker; namespace Catch { - class IMutableContext; class IGeneratorTracker; class IConfig; + class IEventListener; + using IEventListenerPtr = Detail::unique_ptr; /////////////////////////////////////////////////////////////////////////// - class RunContext : public IResultCapture { + class RunContext final : public IResultCapture { public: RunContext( RunContext const& ) = delete; @@ -9440,7 +9800,7 @@ namespace Catch { AssertionReaction& reaction ) override; void handleUnexpectedInflightException ( AssertionInfo const& info, - std::string const& message, + std::string&& message, AssertionReaction& reaction ) override; void handleIncomplete ( AssertionInfo const& info ) override; @@ -9449,6 +9809,7 @@ namespace Catch { ResultWas::OfType resultType, AssertionReaction &reaction ) override; + void notifyAssertionStarted( AssertionInfo const& info ) override; bool sectionStarted( StringRef sectionName, SourceLineInfo const& sectionLineInfo, Counts& assertions ) override; @@ -9499,7 +9860,7 @@ namespace Catch { void resetAssertionInfo(); bool testForMissingAssertions( Counts& assertions ); - void assertionEnded( AssertionResult const& result ); + void assertionEnded( AssertionResult&& result ); void reportExpr ( AssertionInfo const &info, ResultWas::OfType resultType, @@ -9513,7 +9874,6 @@ namespace Catch { void handleUnfinishedSections(); TestRunInfo m_runInfo; - IMutableContext& m_context; TestCaseHandle const* m_activeTestCase = nullptr; ITracker* m_testCaseTracker = nullptr; Optional m_lastResult; @@ -9770,16 +10130,14 @@ namespace Catch { namespace Catch { - class TestCaseHandle; class IConfig; + class ITestInvoker; + class TestCaseHandle; class TestSpec; std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ); bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config ); - bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config ); - - void enforceNoDuplicateTestCases( std::vector const& functions ); std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); std::vector const& getAllTestCasesSorted( IConfig const& config ); @@ -9808,18 +10166,6 @@ namespace Catch { /////////////////////////////////////////////////////////////////////////// - class TestInvokerAsFunction final : public ITestInvoker { - using TestType = void(*)(); - TestType m_testAsFunction; - public: - TestInvokerAsFunction(TestType testAsFunction) noexcept: - m_testAsFunction(testAsFunction) {} - - void invoke() const override; - }; - - /////////////////////////////////////////////////////////////////////////// - } // end namespace Catch @@ -9955,7 +10301,7 @@ namespace Catch { // Calculates the length of the current line void calcLength(); - // Returns current indention width + // Returns current indentation width size_t indentSize() const; // Creates an indented and (optionally) suffixed string from @@ -10268,6 +10614,8 @@ namespace Catch { #define CATCH_MATCHERS_IMPL_HPP_INCLUDED +#include + namespace Catch { template @@ -10960,13 +11308,11 @@ namespace Catch { } template - bool match(RangeLike&& rng) const { - using std::begin; using std::end; - - return end(rng) != std::find_if(begin(rng), end(rng), - [&](auto const& elem) { - return m_eq(elem, m_desired); - }); + bool match( RangeLike&& rng ) const { + for ( auto&& elem : rng ) { + if ( m_eq( elem, m_desired ) ) { return true; } + } + return false; } }; @@ -11018,7 +11364,7 @@ namespace Catch { /** * Creates a matcher that checks whether a range contains a specific element. * - * Uses `eq` to do the comparisons + * Uses `eq` to do the comparisons, the element is provided on the rhs */ template ContainsElementMatcher Contains(T&& elem, Equality&& eq) { @@ -11107,6 +11453,11 @@ namespace Matchers { double m_margin; }; + //! Creates a matcher that accepts numbers within certain range of target + WithinAbsMatcher WithinAbs( double target, double margin ); + + + class WithinUlpsMatcher final : public MatcherBase { public: WithinUlpsMatcher( double target, @@ -11120,6 +11471,13 @@ namespace Matchers { Detail::FloatingPointKind m_type; }; + //! Creates a matcher that accepts doubles within certain ULP range of target + WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); + //! Creates a matcher that accepts floats within certain ULP range of target + WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); + + + // Given IEEE-754 format for floats and doubles, we can assume // that float -> double promotion is lossless. Given this, we can // assume that if we do the standard relative comparison of @@ -11136,13 +11494,6 @@ namespace Matchers { double m_epsilon; }; - //! Creates a matcher that accepts doubles within certain ULP range of target - WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff); - //! Creates a matcher that accepts floats within certain ULP range of target - WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff); - //! Creates a matcher that accepts numbers within certain range of target - WithinAbsMatcher WithinAbs(double target, double margin); - //! Creates a matcher that accepts doubles within certain relative range of target WithinRelMatcher WithinRel(double target, double eps); //! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target @@ -11152,6 +11503,17 @@ namespace Matchers { //! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target WithinRelMatcher WithinRel(float target); + + + class IsNaNMatcher final : public MatcherBase { + public: + IsNaNMatcher() = default; + bool match( double const& matchee ) const override; + std::string describe() const override; + }; + + IsNaNMatcher IsNaN(); + } // namespace Matchers } // namespace Catch @@ -11370,6 +11732,7 @@ namespace Catch { #ifndef CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED #define CATCH_MATCHERS_RANGE_EQUALS_HPP_INCLUDED + #include #include @@ -11394,13 +11757,19 @@ namespace Catch { template bool match( RangeLike&& rng ) const { - using std::begin; - using std::end; - return std::equal( begin(m_desired), - end(m_desired), - begin(rng), - end(rng), - m_predicate ); + auto rng_start = begin( rng ); + const auto rng_end = end( rng ); + auto target_start = begin( m_desired ); + const auto target_end = end( m_desired ); + + while (rng_start != rng_end && target_start != target_end) { + if (!m_predicate(*rng_start, *target_start)) { + return false; + } + ++rng_start; + ++target_start; + } + return rng_start == rng_end && target_start == target_end; } std::string describe() const override { @@ -11428,11 +11797,11 @@ namespace Catch { bool match( RangeLike&& rng ) const { using std::begin; using std::end; - return std::is_permutation( begin( m_desired ), - end( m_desired ), - begin( rng ), - end( rng ), - m_predicate ); + return Catch::Detail::is_permutation( begin( m_desired ), + end( m_desired ), + begin( rng ), + end( rng ), + m_predicate ); } std::string describe() const override { @@ -11482,7 +11851,7 @@ namespace Catch { /** * Creates a matcher that checks if all elements in a range are equal - * to all elements in another range, in some permuation. + * to all elements in another range, in some permutation. * * Uses to provided predicate `predicate` to do the comparisons */ @@ -11652,11 +12021,10 @@ namespace Matchers { // - a more general approach would be via a compare template that defaults // to using !=. but could be specialised for, e.g. std::vector etc // - then just call that directly - if (m_comparator.size() != v.size()) - return false; - for (std::size_t i = 0; i < v.size(); ++i) - if (m_comparator[i] != v[i]) - return false; + if ( m_comparator.size() != v.size() ) { return false; } + for ( std::size_t i = 0; i < v.size(); ++i ) { + if ( !( m_comparator[i] == v[i] ) ) { return false; } + } return true; } std::string describe() const override { @@ -12160,7 +12528,7 @@ namespace Catch { void skipTest(TestCaseInfo const&) override {} protected: - //! Should the cumulative base store the assertion expansion for succesful assertions? + //! Should the cumulative base store the assertion expansion for successful assertions? bool m_shouldStoreSuccesfulAssertions = true; //! Should the cumulative base store the assertion expansion for failed assertions? bool m_shouldStoreFailedAssertions = true; @@ -12467,7 +12835,8 @@ namespace Catch { //! independent on the reporter's concrete type void registerReporterImpl( std::string const& name, IReporterFactoryPtr reporterPtr ); - + //! Actually registers the factory, independent on listener's concrete type + void registerListenerImpl( Detail::unique_ptr listenerFactory ); } // namespace Detail class IEventListener; @@ -12528,7 +12897,7 @@ namespace Catch { public: ListenerRegistrar(StringRef listenerName) { - getMutableRegistryHub().registerListener( Detail::make_unique(listenerName) ); + registerListenerImpl( Detail::make_unique(listenerName) ); } }; } @@ -12549,7 +12918,7 @@ namespace Catch { CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ namespace { \ Catch::ListenerRegistrar INTERNAL_CATCH_UNIQUE_NAME( \ - catch_internal_RegistrarFor )( #listenerType ); \ + catch_internal_RegistrarFor )( #listenerType##_catch_sr ); \ } \ CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION diff --git a/Framework/Foundation/CMakeLists.txt b/Framework/Foundation/CMakeLists.txt index 1024dbebb1eae..aa7a073fca581 100644 --- a/Framework/Foundation/CMakeLists.txt +++ b/Framework/Foundation/CMakeLists.txt @@ -38,7 +38,12 @@ target_link_libraries(o2-test-framework-foundation PRIVATE O2::Catch2) add_executable(o2-test-framework-Signpost test/test_Signpost.cxx) +add_executable(o2-test-framework-ThreadSanitizer + test/test_ThreadSanitizer.cxx) + target_link_libraries(o2-test-framework-Signpost PRIVATE O2::FrameworkFoundation) +target_link_libraries(o2-test-framework-ThreadSanitizer + PRIVATE O2::FrameworkFoundation Threads::Threads) add_executable(o2-test-framework-SignpostLogger test/test_SignpostLogger.cxx @@ -50,6 +55,7 @@ get_filename_component(outdir ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/../tests ABSOLUT set_property(TARGET o2-test-framework-foundation PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) set_property(TARGET o2-test-framework-Signpost PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) set_property(TARGET o2-test-framework-SignpostLogger PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) +set_property(TARGET o2-test-framework-ThreadSanitizer PROPERTY RUNTIME_OUTPUT_DIRECTORY ${outdir}) add_test(NAME framework:foundation COMMAND o2-test-framework-foundation) diff --git a/Framework/Foundation/test/test_ThreadSanitizer.cxx b/Framework/Foundation/test/test_ThreadSanitizer.cxx new file mode 100644 index 0000000000000..16b6f39b8d59e --- /dev/null +++ b/Framework/Foundation/test/test_ThreadSanitizer.cxx @@ -0,0 +1,34 @@ +// 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 +#include +#include +#include + +using map_t = std::map; + +void* threadfunc(void* p) +{ + map_t& m = *(map_t*)p; + m["foo"] = "bar"; + return nullptr; +} + +int main() +{ + map_t m; + // Create a thread in C++11 mode, which executes threadfunc(&m) + std::thread thread(threadfunc, &m); + // Start + printf("foo=%s\n", m["foo"].c_str()); + thread.join(); +} diff --git a/Framework/TestWorkflows/CMakeLists.txt b/Framework/TestWorkflows/CMakeLists.txt index 1fae48e50364d..05c40f034b9d2 100644 --- a/Framework/TestWorkflows/CMakeLists.txt +++ b/Framework/TestWorkflows/CMakeLists.txt @@ -105,3 +105,7 @@ o2_add_dpl_workflow(test-ccdb-fetcher SOURCES src/test_CCDBFetcher.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF O2::Framework COMPONENT_NAME TestWorkflows) + +add_executable(o2-test-deadlock src/o2DeadlockReproducer.cxx) +target_link_libraries(o2-test-deadlock PUBLIC FairMQ::FairMQ) +add_test(NAME o2-test-deadlock COMMAND o2-test-deadlock --channel-config "name=data,type=sub,method=bind,address=ipc://127.0.0.1,rateLogging=0" --id foo --transport zeromq --control static) diff --git a/Framework/TestWorkflows/src/o2DeadlockReproducer.cxx b/Framework/TestWorkflows/src/o2DeadlockReproducer.cxx new file mode 100644 index 0000000000000..196c448efe016 --- /dev/null +++ b/Framework/TestWorkflows/src/o2DeadlockReproducer.cxx @@ -0,0 +1,66 @@ +// 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 +#include + +#include + +namespace bpo = boost::program_options; + +void addCustomOptions(bpo::options_description& options) +{ +} + +struct Deadlocked : fair::mq::Device { + Deadlocked() + { + auto stateWatcher = [this](fair::mq::State newState) { + static bool first = true; + LOG(info) << "State changed to " << newState; + if (newState != fair::mq::State::Ready) { + LOG(info) << "Not ready state, ignoring"; + return; + } + if (first) { + first = false; + return; + } + fair::mq::Parts parts; + LOG(info) << "Draining messages" << std::endl; + auto& channels = this->GetChannels(); + LOG(info) << "Number of channels: " << channels.size(); + if (channels.size() == 0) { + LOG(info) << "No messages to drain"; + return; + } + auto& channel = channels.at("data")[0]; + while (this->NewStatePending() == false) { + channel.Receive(parts, 10); + if (parts.Size() != 0) { + LOG(info) << "Draining" << parts.Size() << "messages"; + } + } + LOG(info) << "Done draining"; + }; + this->SubscribeToStateChange("99-drain", stateWatcher); + } + + void Run() override + { + LOG(info) << "Simply exit"; + } +}; + +std::unique_ptr getDevice(fair::mq::ProgOptions& /*config*/) +{ + return std::make_unique(); +} diff --git a/Framework/Utils/CMakeLists.txt b/Framework/Utils/CMakeLists.txt index 5f2d495339a18..db234537a0b6f 100644 --- a/Framework/Utils/CMakeLists.txt +++ b/Framework/Utils/CMakeLists.txt @@ -19,7 +19,7 @@ o2_add_library(DPLUtils test/DPLBroadcasterMerger.cxx test/DPLOutputTest.cxx test/RawPageTestData.cxx - PUBLIC_LINK_LIBRARIES O2::Framework ROOT::Tree ROOT::TreePlayer) + PUBLIC_LINK_LIBRARIES O2::Framework ROOT::Tree ROOT::TreePlayer O2::CommonUtils) o2_add_executable(raw-proxy COMPONENT_NAME dpl diff --git a/Framework/Utils/include/DPLUtils/DPLRawPageSequencer.h b/Framework/Utils/include/DPLUtils/DPLRawPageSequencer.h index 9f05a925a84bd..785dc9e04bd45 100644 --- a/Framework/Utils/include/DPLUtils/DPLRawPageSequencer.h +++ b/Framework/Utils/include/DPLUtils/DPLRawPageSequencer.h @@ -98,10 +98,16 @@ class DPLRawPageSequencer int retVal = 0; for (auto const& ref : mInput) { auto size = DataRefUtils::getPayloadSize(ref); + const auto dh = DataRefUtils::getHeader(ref); + if (dh == nullptr) { + continue; + } if (size == 0) { + if (dh->subSpecification == 0xDEADBEEF) { + raw_parser::RawParserHelper::warnDeadBeef(dh); + } continue; } - const auto dh = DataRefUtils::getHeader(ref); auto const pageSize = rawparser_type::max_size; auto nPages = size / pageSize + (size % pageSize ? 1 : 0); if (!preCheck(ref.payload, dh->subSpecification)) { diff --git a/Framework/Utils/include/DPLUtils/RawParser.h b/Framework/Utils/include/DPLUtils/RawParser.h index ec302629f7200..2c35c008983f0 100644 --- a/Framework/Utils/include/DPLUtils/RawParser.h +++ b/Framework/Utils/include/DPLUtils/RawParser.h @@ -25,6 +25,10 @@ #include #include #include +namespace o2::header +{ +struct DataHeader; +} // namespace o2::header // FIXME: probably moved somewhere else namespace o2::framework @@ -87,6 +91,7 @@ struct RawParserHelper { static unsigned long sErrorScale; // Exponentionally downscale verbosity. static bool checkPrintError(size_t& localCounter); + static void warnDeadBeef(const o2::header::DataHeader* dh); }; /// @class ConcreteRawParser diff --git a/Framework/Utils/src/RawParser.cxx b/Framework/Utils/src/RawParser.cxx index e216c2c560793..2ec48c7f93d36 100644 --- a/Framework/Utils/src/RawParser.cxx +++ b/Framework/Utils/src/RawParser.cxx @@ -15,6 +15,8 @@ /// @brief Generic parser for consecutive raw pages #include "DPLUtils/RawParser.h" +#include "CommonUtils/VerbosityConfig.h" +#include "Headers/DataHeader.h" #include "fmt/format.h" #include @@ -40,6 +42,15 @@ bool RawParserHelper::checkPrintError(size_t& localCounter) return sErrors % sErrorScale == 0; } +void RawParserHelper::warnDeadBeef(const o2::header::DataHeader* dh) +{ + static auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; + static int contDeadBeef = 0; + if (++contDeadBeef <= maxWarn) { + LOGP(alarm, "Found input [{}/{}/0xDEADBEEF] assuming no payload for all links in this TF{}", dh->dataOrigin.as(), dh->dataDescription.as(), contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); + } +} + const char* RDHFormatter::sFormatString = "{:>5} {:>4} {:>4} {:>4} {:>4} {:>3} {:>3} {:>3} {:>1} {:>2}"; void RDHFormatter::apply(std::ostream& os, V7 const& header, FormatSpec choice, const char* delimiter) { diff --git a/Framework/Utils/src/raw-proxy.cxx b/Framework/Utils/src/raw-proxy.cxx index d518863bb66fe..195e1bd61d081 100644 --- a/Framework/Utils/src/raw-proxy.cxx +++ b/Framework/Utils/src/raw-proxy.cxx @@ -31,6 +31,14 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{ "dataspec", VariantType::String, "A:FLP/RAWDATA;B:FLP/DISTSUBTIMEFRAME/0", {"selection string for the data to be proxied"}}); + workflowOptions.push_back( + ConfigParamSpec{ + "inject-missing-data", VariantType::Bool, false, {"inject missing data according to dataspec if not found in the input"}}); + + workflowOptions.push_back( + ConfigParamSpec{ + "print-input-sizes", VariantType::Int, 0, {"print statistics about sizes per input spec every n TFs"}}); + workflowOptions.push_back( ConfigParamSpec{ "throwOnUnmatched", VariantType::Bool, false, {"throw if unmatched input data is found"}}); @@ -46,6 +54,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) { std::string processorName = config.options().get("proxy-name"); std::string outputconfig = config.options().get("dataspec"); + bool injectMissingData = config.options().get("inject-missing-data"); + unsigned int printSizes = config.options().get("print-input-sizes"); bool throwOnUnmatched = config.options().get("throwOnUnmatched"); uint64_t minSHM = std::stoul(config.options().get("timeframes-shm-limit")); std::vector matchers = select(outputconfig.c_str()); @@ -60,7 +70,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) processorName.c_str(), std::move(readoutProxyOutput), "type=pair,method=connect,address=ipc:///tmp/readout-pipe-0,rateLogging=1,transport=shmem", - dplModelAdaptor(filterSpecs, throwOnUnmatched), minSHM); + dplModelAdaptor(filterSpecs, throwOnUnmatched), minSHM, false, injectMissingData, printSizes); WorkflowSpec workflow; workflow.emplace_back(readoutProxy); diff --git a/GPU/Common/GPUCommonMath.h b/GPU/Common/GPUCommonMath.h index 1a3a18ddcbdf9..dd385853e99bc 100644 --- a/GPU/Common/GPUCommonMath.h +++ b/GPU/Common/GPUCommonMath.h @@ -81,6 +81,8 @@ class GPUCommonMath GPUhdni() static float Hypot(float x, float y, float z, float w); GPUd() static float Log(float x); + GPUd() static float Exp(float x); + template GPUdi() static T AtomicExch(GPUglobalref() GPUgeneric() GPUAtomic(T) * addr, T val) { @@ -412,6 +414,7 @@ GPUdi() float GPUCommonMath::ASin(float x) { return CHOICE(asinf(x), asinf(x), a GPUdi() float GPUCommonMath::ACos(float x) { return CHOICE(acosf(x), acosf(x), acos(x)); } GPUdi() float GPUCommonMath::Log(float x) { return CHOICE(logf(x), logf(x), log(x)); } +GPUdi() float GPUCommonMath::Exp(float x) { return CHOICE(expf(x), expf(x), exp(x)); } GPUhdi() float GPUCommonMath::Copysign(float x, float y) { diff --git a/GPU/GPUTracking/Base/GPUMemoryResource.h b/GPU/GPUTracking/Base/GPUMemoryResource.h index 0d73da0f8b5f7..b8967787d7399 100644 --- a/GPU/GPUTracking/Base/GPUMemoryResource.h +++ b/GPU/GPUTracking/Base/GPUMemoryResource.h @@ -78,12 +78,10 @@ class GPUMemoryResource ALLOCATION_INDIVIDUAL = 1, ALLOCATION_GLOBAL = 2 }; -#ifndef GPUCA_GPUCODE GPUMemoryResource(GPUProcessor* proc, void* (GPUProcessor::*setPtr)(void*), MemoryType type, const char* name = "") : mProcessor(proc), mPtr(nullptr), mPtrDevice(nullptr), mSetPointers(setPtr), mName(name), mSize(0), mOverrideSize(0), mReuse(-1), mType(type) { } GPUMemoryResource(const GPUMemoryResource&) CON_DEFAULT; -#endif void* SetPointers(void* ptr) { diff --git a/GPU/GPUTracking/Base/GPUParam.cxx b/GPU/GPUTracking/Base/GPUParam.cxx index f030625c7e5d6..5eb83c6457e9c 100644 --- a/GPU/GPUTracking/Base/GPUParam.cxx +++ b/GPU/GPUTracking/Base/GPUParam.cxx @@ -42,6 +42,12 @@ void GPUParam::SetDefaults(float solenoidBz) new (&tpcGeometry) GPUTPCGeometry; new (&rec) GPUSettingsRec; +#ifdef GPUCA_TPC_GEOMETRY_O2 + const float kErrorsY[4] = {0.06, 0.24, 0.12, 0.1}; + const float kErrorsZ[4] = {0.06, 0.24, 0.15, 0.1}; + + UpdateRun3ClusterErrors(kErrorsY, kErrorsZ); +#else // clang-format off const float kParamS0Par[2][3][6] = { @@ -80,6 +86,7 @@ void GPUParam::SetDefaults(float solenoidBz) } } } +#endif par.dAlpha = 0.349066f; bzkG = solenoidBz; @@ -121,7 +128,7 @@ void GPUParam::SetDefaults(float solenoidBz) GPUTPCGMPolynomialFieldManager::GetPolynomialField(bzkG, polynomialField); } -void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p) +void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p, const GPURecoStepConfiguration* w) { if (g) { bzkG = g->solenoidBz; @@ -142,23 +149,44 @@ void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessi if (p) { par.debugLevel = p->debugLevel; par.resetTimers = p->resetTimers; + UpdateRun3ClusterErrors(p->param.tpcErrorParamY, p->param.tpcErrorParamZ); + } + if (w) { + par.dodEdx = w->steps.isSet(GPUDataTypes::RecoStep::TPCdEdx); + if (par.dodEdx && p && p->tpcDownscaledEdx != 0) { + par.dodEdx = (rand() % 100) < p->tpcDownscaledEdx; + } } } void GPUParam::SetDefaults(const GPUSettingsGRP* g, const GPUSettingsRec* r, const GPUSettingsProcessing* p, const GPURecoStepConfiguration* w) { SetDefaults(g->solenoidBz); - if (w) { - par.dodEdx = w->steps.isSet(GPUDataTypes::RecoStep::TPCdEdx); - } if (r) { rec = *r; if (rec.fitPropagateBzOnly == -1) { rec.fitPropagateBzOnly = rec.tpc.nWays - 1; } } - UpdateSettings(g, p); + UpdateSettings(g, p, w); +} + +void GPUParam::UpdateRun3ClusterErrors(const float* yErrorParam, const float* zErrorParam) +{ +#ifdef GPUCA_TPC_GEOMETRY_O2 + for (int yz = 0; yz < 2; yz++) { + const float* param = yz ? zErrorParam : yErrorParam; + for (int rowType = 0; rowType < 4; rowType++) { + constexpr int regionMap[4] = {0, 4, 6, 8}; + ParamErrors[yz][rowType][0] = param[0] * param[0]; + ParamErrors[yz][rowType][1] = param[1] * param[1] * tpcGeometry.PadHeightByRegion(regionMap[rowType]); + ParamErrors[yz][rowType][2] = param[2] * param[2] / tpcGeometry.TPCLength() / tpcGeometry.PadHeightByRegion(regionMap[rowType]); + ParamErrors[yz][rowType][3] = param[3] * param[3]; + } + } +#endif } + #ifndef GPUCA_ALIROOT_LIB void GPUParam::LoadClusterErrors(bool Print) { diff --git a/GPU/GPUTracking/Base/GPUParam.h b/GPU/GPUTracking/Base/GPUParam.h index 48bc8b2f731ce..5d8ae5dee4f86 100644 --- a/GPU/GPUTracking/Base/GPUParam.h +++ b/GPU/GPUTracking/Base/GPUParam.h @@ -63,8 +63,12 @@ struct GPUParam_t { GPUParamSlice SliceParam[GPUCA_NSLICES]; protected: +#ifdef GPUCA_TPC_GEOMETRY_O2 + float ParamErrors[2][4][4]; +#else float ParamErrorsSeeding0[2][3][4]; // cluster shape parameterization coeficients float ParamS0Par[2][3][6]; // cluster error parameterization coeficients +#endif }; } // namespace internal @@ -75,9 +79,10 @@ struct GPUParam : public internal::GPUParam_t #ifndef GPUCA_GPUCODE void SetDefaults(float solenoidBz); void SetDefaults(const GPUSettingsGRP* g, const GPUSettingsRec* r = nullptr, const GPUSettingsProcessing* p = nullptr, const GPURecoStepConfiguration* w = nullptr); - void UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr); + void UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr, const GPURecoStepConfiguration* w = nullptr); void LoadClusterErrors(bool Print = 0); o2::base::Propagator* GetDefaultO2Propagator(bool useGPUField = false) const; + void UpdateRun3ClusterErrors(const float* yErrorParam, const float* zErrorParam); #endif GPUd() float Alpha(int iSlice) const @@ -92,6 +97,7 @@ struct GPUParam : public internal::GPUParam_t } GPUd() float GetClusterErrorSeeding(int yz, int type, float z, float angle2) const; GPUd() void GetClusterErrorsSeeding2(int row, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const; + GPUd() float GetSystematicClusterErrorIFC2(float x, float z, bool sideC) const; GPUd() float GetClusterError2(int yz, int type, float z, float angle2) const; GPUd() void GetClusterErrors2(int row, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const; diff --git a/GPU/GPUTracking/Base/GPUParam.inc b/GPU/GPUTracking/Base/GPUParam.inc index 0302af6ec30e2..2cb5f2275b13d 100644 --- a/GPU/GPUTracking/Base/GPUParam.inc +++ b/GPU/GPUTracking/Base/GPUParam.inc @@ -41,11 +41,59 @@ GPUdi() void MEM_LG(GPUParam)::Global2Slice(int iSlice, float X, float Y, float *z = Z; } +#ifdef GPUCA_TPC_GEOMETRY_O2 + MEM_CLASS_PRE() -GPUdi() float MEM_LG(GPUParam)::GetClusterErrorSeeding(int yz, int type, float z, float angle2) const +GPUdi() void MEM_LG(GPUParam)::GetClusterErrorsSeeding2(int iRow, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const +{ + GetClusterErrors2(iRow, z, sinPhi, DzDs, ErrY2, ErrZ2); +} + +MEM_CLASS_PRE() +GPUdi() float MEM_LG(GPUParam)::GetClusterError2(int yz, int type, float z, float angle2) const +{ + MakeType(const float*) c = ParamErrors[yz][type]; + float v = c[0] + c[1] * angle2 + c[2] * z + c[3] * 0; + v = CAMath::Abs(v); + v *= yz ? rec.tpc.clusterError2CorrectionZ : rec.tpc.clusterError2CorrectionY; + v += yz ? rec.tpc.clusterError2AdditionalZ : rec.tpc.clusterError2AdditionalY; + return v; +} + +MEM_CLASS_PRE() +GPUdi() float MEM_LG(GPUParam)::GetSystematicClusterErrorIFC2(float x, float z, bool sideC) const { - //* recalculate the cluster ErrorsSeeding wih respect to the track slope + const float kEpsZBoundary = 1.e-6f; // to disentangle A,C and A+C-common regions + const float kMaxExpArg = 9.f; // limit r-dumped error to this exp. argument + const float kMaxExpArgZ = CAMath::Sqrt(2.f * kMaxExpArg); + + double sysErr = 0; + if (rec.tpc.sysClusErrorInner0 > 0.f) { + double dr = CAMath::Abs(x - 85.f); + float argExp = dr / rec.tpc.sysClusErrorInner1; // is the Z-range limited ? + if (argExp < kMaxExpArg) { + if (rec.tpc.sysClusErrorZRegion < -kEpsZBoundary && !sideC) { + return 0; // don't apply to A-side clusters if the Z-boundary is for C-region + } else if (rec.tpc.sysClusErrorZRegion > kEpsZBoundary && sideC) { + return 0; // don't apply to C-side clusters if the Z-boundary is for A-region + } + const float dz = CAMath::Abs((rec.tpc.sysClusErrorZRegion-z)*rec.tpc.sysClusErrorZRegionSigInv); + if (dz < kMaxExpArgZ) { // is it small enough to call exp? + argExp += 0.5f * dz*dz; + if (argExp 2) { - rowType = 2; // TODO: Add type 3 - } - z = CAMath::Abs(GPUTPCGeometry::TPCLength() - CAMath::Abs(z)); + z = CAMath::Abs(tpcGeometry.TPCLength() - CAMath::Abs(z)); float s2 = sinPhi * sinPhi; if (s2 > 0.95f * 0.95f) { s2 = 0.95f * 0.95f; @@ -70,15 +115,13 @@ GPUdi() void MEM_LG(GPUParam)::GetClusterErrorsSeeding2(int iRow, float z, float ErrY2 = GetClusterErrorSeeding(0, rowType, z, angleY2); ErrZ2 = GetClusterErrorSeeding(1, rowType, z, angleZ2); - ErrY2 *= ErrY2 * rec.tpc.clusterError2CorrectionY + rec.tpc.clusterError2AdditionalY; - ErrZ2 *= ErrZ2 * rec.tpc.clusterError2CorrectionZ + rec.tpc.clusterError2AdditionalZ; + ErrY2 = ErrY2 * ErrY2 * rec.tpc.clusterError2CorrectionY + rec.tpc.clusterError2AdditionalY; + ErrZ2 = ErrZ2 * ErrZ2 * rec.tpc.clusterError2CorrectionZ + rec.tpc.clusterError2AdditionalZ; } MEM_CLASS_PRE() GPUdi() float MEM_LG(GPUParam)::GetClusterError2(int yz, int type, float z, float angle2) const { - //* recalculate the cluster error wih respect to the track slope - MakeType(const float*) c = ParamS0Par[yz][type]; float v = c[0] + c[1] * z + c[2] * angle2 + c[3] * z * z + c[4] * angle2 * angle2 + c[5] * z * angle2; v = CAMath::Abs(v); @@ -90,15 +133,20 @@ GPUdi() float MEM_LG(GPUParam)::GetClusterError2(int yz, int type, float z, floa return v; } +MEM_CLASS_PRE() +GPUdi() float MEM_LG(GPUParam)::GetSystematicClusterErrorIFC2(float x, float z, bool sideC) const +{ + return 0; +} + +#endif // !GPUCA_TPC_GEOMETRY_O2 + MEM_CLASS_PRE() GPUdi() void MEM_LG(GPUParam)::GetClusterErrors2(int iRow, float z, float sinPhi, float DzDs, float& ErrY2, float& ErrZ2) const { // Calibrated cluster error from OCDB for Y and Z int rowType = tpcGeometry.GetROC(iRow); - if (rowType > 2) { - rowType = 2; // TODO: Add type 3 - } - z = CAMath::Abs(GPUTPCGeometry::TPCLength() - CAMath::Abs(z)); + z = CAMath::Abs(tpcGeometry.TPCLength() - CAMath::Abs(z)); float s2 = sinPhi * sinPhi; if (s2 > 0.95f * 0.95f) { s2 = 0.95f * 0.95f; @@ -115,20 +163,20 @@ MEM_CLASS_PRE() GPUdi() void MEM_LG(GPUParam)::UpdateClusterError2ByState(short clusterState, float& ErrY2, float& ErrZ2) const { if (clusterState & GPUTPCGMMergedTrackHit::flagEdge) { - ErrY2 += 0.35f; - ErrZ2 += 0.15f; + ErrY2 += rec.tpc.extraClusterErrorEdgeY2; + ErrZ2 += rec.tpc.extraClusterErrorEdgeZ2; } if (clusterState & GPUTPCGMMergedTrackHit::flagSingle) { - ErrY2 += 0.2f; - ErrZ2 += 0.2f; + ErrY2 += rec.tpc.extraClusterErrorSingleY2; + ErrZ2 += rec.tpc.extraClusterErrorSingleZ2; } if (clusterState & (GPUTPCGMMergedTrackHit::flagSplitPad | GPUTPCGMMergedTrackHit::flagShared | GPUTPCGMMergedTrackHit::flagSingle)) { - ErrY2 += 0.03f; - ErrY2 *= 3; + ErrY2 += rec.tpc.extraClusterErrorSplitPadSharedSingleY2; + ErrY2 *= rec.tpc.extraClusterErrorFactorSplitPadSharedSingleY2; } if (clusterState & (GPUTPCGMMergedTrackHit::flagSplitTime | GPUTPCGMMergedTrackHit::flagShared | GPUTPCGMMergedTrackHit::flagSingle)) { - ErrZ2 += 0.03f; - ErrZ2 *= 3; + ErrZ2 += rec.tpc.extraClusterErrorSplitTimeSharedSingleZ2; + ErrZ2 *= rec.tpc.extraClusterErrorFactorSplitTimeSharedSingleZ2; } } diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index 94a9a446ca6db..676ab0f6cf004 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -279,7 +279,7 @@ int GPUReconstruction::InitPhaseBeforeDevice() if (!(mRecoStepsGPU & RecoStep::TPCMerging) || !param().rec.tpc.mergerReadFromTrackerDirectly) { mProcessingSettings.fullMergerOnGPU = false; } - if (mProcessingSettings.debugLevel || !mProcessingSettings.fullMergerOnGPU) { + if (mProcessingSettings.debugLevel > 3 || !mProcessingSettings.fullMergerOnGPU) { mProcessingSettings.delayedOutput = false; } if (!mProcessingSettings.fullMergerOnGPU && GetRecoStepsGPU() & RecoStep::TPCMerging) { @@ -289,8 +289,8 @@ int GPUReconstruction::InitPhaseBeforeDevice() } } - UpdateSettings(); - GPUCA_GPUReconstructionUpdateDefailts(); + UpdateAutomaticProcessingSettings(); + GPUCA_GPUReconstructionUpdateDefaults(); if (!mProcessingSettings.trackletConstructorInPipeline) { mProcessingSettings.trackletSelectorInPipeline = false; } @@ -531,7 +531,7 @@ size_t GPUReconstruction::AllocateRegisteredMemoryHelper(GPUMemoryResource* res, return retVal; } if (memorypool == nullptr) { - GPUInfo("Memory pool uninitialized"); + GPUError("Cannot allocate memory from uninitialized pool"); throw std::bad_alloc(); } size_t retVal; @@ -972,8 +972,10 @@ void GPUReconstruction::RunPipelineWorker() } else { q->retVal = q->chain->RunChain(); } - std::lock_guard lk(q->m); - q->done = true; + { + std::lock_guard lk(q->m); + q->done = true; + } q->c.notify_one(); } if (mProcessingSettings.debugLevel >= 3) { @@ -1059,10 +1061,17 @@ void GPUReconstruction::DumpSettings(const char* dir) } } -void GPUReconstruction::UpdateGRPSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p) +void GPUReconstruction::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p) { - mGRPSettings = *g; - param().UpdateSettings(g, p); + if (g) { + mGRPSettings = *g; + } + if (p) { + mProcessingSettings.debugLevel = p->debugLevel; + mProcessingSettings.resetTimers = p->resetTimers; + } + GPURecoStepConfiguration w = {mRecoSteps, mRecoStepsGPU, mRecoStepsInputs, mRecoStepsOutputs}; + param().UpdateSettings(g, p, &w); if (mInitialized) { WriteConstantParams(); } @@ -1187,7 +1196,7 @@ GPUReconstruction* GPUReconstruction::CreateInstance(const GPUSettingsDeviceBack retVal = CreateInstance(cfg2); } } else { - GPUInfo("Created GPUReconstruction instance for device type %s (%u) %s", GPUDataTypes::DEVICE_TYPE_NAMES[type], type, cfg.master ? " (slave)" : ""); + GPUInfo("Created GPUReconstruction instance for device type %s (%u)%s", GPUDataTypes::DEVICE_TYPE_NAMES[type], type, cfg.master ? " (slave)" : ""); } return retVal; diff --git a/GPU/GPUTracking/Base/GPUReconstruction.h b/GPU/GPUTracking/Base/GPUReconstruction.h index 190a2e3d2a7c2..8b56e46888e9b 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -240,7 +240,7 @@ class GPUReconstruction void SetResetTimers(bool reset) { mProcessingSettings.resetTimers = reset; } // May update also after Init() void SetDebugLevelTmp(int level) { mProcessingSettings.debugLevel = level; } // Temporarily, before calling SetSettings() GPUParam& GetNonConstParam() { return mHostConstantMem->param; } // Kind of hack, but needed for manual updates after SetSettings(), if the default configKeyValues parsing for SetSettings() is used. - void UpdateGRPSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr); + void UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr); void SetOutputControl(const GPUOutputControl& v) { mOutputControl = v; } void SetOutputControl(void* ptr, size_t size); void SetInputControl(void* ptr, size_t size); @@ -278,7 +278,7 @@ class GPUReconstruction void FreeRegisteredMemory(GPUMemoryResource* res); GPUReconstruction(const GPUSettingsDeviceBackend& cfg); // Constructor int InitPhaseBeforeDevice(); - virtual void UpdateSettings() {} + virtual void UpdateAutomaticProcessingSettings() {} virtual int InitDevice() = 0; int InitPhasePermanentMemory(); int InitPhaseAfterDevice(); diff --git a/GPU/GPUTracking/Base/GPUReconstructionIncludes.h b/GPU/GPUTracking/Base/GPUReconstructionIncludes.h index 6cbb80cc71dc9..77732623eee31 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionIncludes.h +++ b/GPU/GPUTracking/Base/GPUReconstructionIncludes.h @@ -39,7 +39,7 @@ #define RANDOM_ERROR //#define RANDOM_ERROR || rand() % 500 == 1 -#define GPUCA_GPUReconstructionUpdateDefailts() \ +#define GPUCA_GPUReconstructionUpdateDefaults() \ if (mProcessingSettings.trackletConstructorInPipeline < 0) { \ mProcessingSettings.trackletConstructorInPipeline = GPUCA_CONSTRUCTOR_IN_PIPELINE; \ } \ diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 0330a60dac2af..f933f7e8c1822 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -50,7 +50,7 @@ if(NOT ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") set(RTC_CUDA_ARCH "750") endif() separate_arguments(CUDARTC_FLAGS) - + # convenience variables if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") get_filename_component(GPUDIR ${CMAKE_SOURCE_DIR}/../ ABSOLUTE) @@ -78,7 +78,7 @@ if(NOT ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") VERBATIM ) create_binary_resource(${CURTC_BIN}.command ${CURTC_BIN}.command.o) - + set(SRCS ${SRCS} ${CURTC_BIN}.src.o ${CURTC_BIN}.command.o) endif() # -------------------------------- End RTC ------------------------------------------------------- @@ -124,8 +124,6 @@ if(ALIGPU_BUILD_TYPE STREQUAL "ALIROOT") install(TARGETS ${targetName} ARCHIVE DESTINATION lib LIBRARY DESTINATION lib) install(FILES ${HDRS} DESTINATION include) - - endif() if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") @@ -136,14 +134,7 @@ if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") endif() target_link_libraries(${targetName} PRIVATE cuda cudart nvrtc) -target_compile_definitions(${targetName} PUBLIC GPUCA_GPULIBRARY=CUDA) -if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "86") -target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_AMPERE) -elseif(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "75") -target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_TURING) -else() -target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_AMPERE) -endif() +set_target_cuda_arch(${targetName}) if(OpenMP_CXX_FOUND) # Must be private, depending libraries might be compiled by compiler not understanding -fopenmp diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index b115070c7aec3..269b3452b7d94 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -124,9 +124,9 @@ void GPUReconstructionCUDA::GetITSTraits(std::unique_ptr } } -void GPUReconstructionCUDA::UpdateSettings() +void GPUReconstructionCUDA::UpdateAutomaticProcessingSettings() { - GPUCA_GPUReconstructionUpdateDefailts(); + GPUCA_GPUReconstructionUpdateDefaults(); } int GPUReconstructionCUDA::InitDevice_Runtime() @@ -335,8 +335,6 @@ int GPUReconstructionCUDA::InitDevice_Runtime() } dummyInitKernel<<>>(mDeviceMemoryBase); - GPUInfo("CUDA Initialisation successfull (Device %d: %s (Frequency %d, Cores %d), %lld / %lld bytes host / global memory, Stack frame %d, Constant memory %lld)", mDeviceId, cudaDeviceProp.name, cudaDeviceProp.clockRate, cudaDeviceProp.multiProcessorCount, (long long int)mHostMemorySize, - (long long int)mDeviceMemorySize, (int)GPUCA_GPU_STACK_SIZE, (long long int)gGPUConstantMemBufferSize); #ifndef GPUCA_ALIROOT_LIB if (mProcessingSettings.rtc.enable) { @@ -363,6 +361,8 @@ int GPUReconstructionCUDA::InitDevice_Runtime() } #endif mDeviceConstantMem = (GPUConstantMem*)devPtrConstantMem; + + GPUInfo("CUDA Initialisation successfull (Device %d: %s (Frequency %d, Cores %d), %lld / %lld bytes host / global memory, Stack frame %d, Constant memory %lld)", mDeviceId, cudaDeviceProp.name, cudaDeviceProp.clockRate, cudaDeviceProp.multiProcessorCount, (long long int)mHostMemorySize, (long long int)mDeviceMemorySize, (int)GPUCA_GPU_STACK_SIZE, (long long int)gGPUConstantMemBufferSize); } else { GPUReconstructionCUDA* master = dynamic_cast(mMaster); mDeviceId = master->mDeviceId; @@ -375,6 +375,8 @@ int GPUReconstructionCUDA::InitDevice_Runtime() std::copy(master->mDeviceConstantMemRTC.begin(), master->mDeviceConstantMemRTC.end(), mDeviceConstantMemRTC.begin()); mInternals = master->mInternals; GPUFailedMsg(cudaSetDevice(mDeviceId)); + + GPUInfo("CUDA Initialized from master"); } for (unsigned int i = 0; i < mEvents.size(); i++) { diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h index 33da396652f83..6d1d43bdea768 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h @@ -63,7 +63,7 @@ class GPUReconstructionCUDA : public GPUReconstructionKernels GetThreadContext() override; bool CanQueryMaxMemory() override { return true; } diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index 7d0d651b1a3b5..ad3d2465ba76a 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -88,15 +88,4 @@ if(ALIGPU_BUILD_TYPE STREQUAL "Standalone") install(TARGETS GPUTrackingHIP) endif() -if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx906") - message(STATUS "Using optimized HIP settings for MI50 GPU") - target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_VEGA) -elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx908") - message(STATUS "Using optimized HIP settings for MI100 GPU") - target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_MI2xx) -elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx90a") - message(STATUS "Using optimized HIP settings for MI210 GPU") - target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_MI2xx) -else() - target_compile_definitions(${targetName} PUBLIC GPUCA_GPUTYPE_VEGA) -endif() +set_target_hip_arch(${targetName}) \ No newline at end of file diff --git a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h index 472e04100bbd8..44729cc4da58f 100644 --- a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h +++ b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.h @@ -41,7 +41,7 @@ class GPUReconstructionHIPBackend : public GPUReconstructionDeviceBase int InitDevice_Runtime() override; int ExitDevice_Runtime() override; - void UpdateSettings() override; + void UpdateAutomaticProcessingSettings() override; std::unique_ptr GetThreadContext() override; void SynchronizeGPU() override; diff --git a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx index 73177bad20660..999e96c9823a9 100644 --- a/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx +++ b/GPU/GPUTracking/Base/hip/GPUReconstructionHIP.hip.cxx @@ -241,9 +241,9 @@ void GPUReconstructionHIPBackend::GetITSTraits(std::unique_ptr(mMaster); mDeviceId = master->mDeviceId; @@ -449,6 +448,7 @@ int GPUReconstructionHIPBackend::InitDevice_Runtime() mDeviceConstantMem = master->mDeviceConstantMem; mInternals = master->mInternals; GPUFailedMsgI(hipSetDevice(mDeviceId)); + GPUInfo("HIP Initialized from master"); } for (unsigned int i = 0; i < mEvents.size(); i++) { hipEvent_t* events = (hipEvent_t*)mEvents[i].data(); diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx index 3356a280a5a22..537aa64b21a25 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx @@ -75,9 +75,9 @@ void GPUReconstructionOCL::GPUFailedMsgA(const long int error, const char* file, } } -void GPUReconstructionOCL::UpdateSettings() +void GPUReconstructionOCL::UpdateAutomaticProcessingSettings() { - GPUCA_GPUReconstructionUpdateDefailts(); + GPUCA_GPUReconstructionUpdateDefaults(); } int GPUReconstructionOCL::InitDevice_Runtime() diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h index 90a9707a80684..2a97296afcbbe 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h @@ -36,7 +36,7 @@ class GPUReconstructionOCL : public GPUReconstructionDeviceBase protected: int InitDevice_Runtime() override; int ExitDevice_Runtime() override; - void UpdateSettings() override; + void UpdateAutomaticProcessingSettings() override; int GPUFailedMsgAI(const long int error, const char* file, int line); void GPUFailedMsgA(const long int error, const char* file, int line); diff --git a/GPU/GPUTracking/Benchmark/standalone.cxx b/GPU/GPUTracking/Benchmark/standalone.cxx index a0fcf59400f3e..9dc4c5a4908bf 100644 --- a/GPU/GPUTracking/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Benchmark/standalone.cxx @@ -120,8 +120,8 @@ int ReadConfiguration(int argc, char** argv) configStandalone.proc.debugLevel = 0; } #ifndef _WIN32 - setlocale(LC_ALL, ""); - setlocale(LC_NUMERIC, ""); + setlocale(LC_ALL, "en_US.utf-8"); + setlocale(LC_NUMERIC, "en_US.utf-8"); if (configStandalone.cpuAffinity != -1) { cpu_set_t mask; CPU_ZERO(&mask); @@ -183,18 +183,12 @@ int ReadConfiguration(int argc, char** argv) return 1; } #endif - if (configStandalone.proc.runQA) { - if (getenv("LC_NUMERIC")) { - printf("Please unset the LC_NUMERIC env variable, otherwise ROOT will not be able to fit correctly\n"); // BUG: ROOT Problem - return 1; - } - } if (configStandalone.proc.doublePipeline && configStandalone.testSyncAsync) { printf("Cannot run asynchronous processing with double pipeline\n"); return 1; } if (configStandalone.proc.doublePipeline && (configStandalone.runs < 4 || !configStandalone.outputcontrolmem)) { - printf("Double pipeline mode needs at least 3 runs per event and external output\n"); + printf("Double pipeline mode needs at least 3 runs per event and external output. To cycle though multiple events, use --preloadEvents and --runs n for n iterations round-robin\n"); return 1; } if (configStandalone.TF.bunchSim && configStandalone.TF.nMerge) { @@ -421,7 +415,9 @@ int SetupReconstruction() if (configStandalone.testSyncAsync || configStandalone.testSync) { // Set settings for synchronous - steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, 0); + if (configStandalone.rundEdx == -1) { + steps.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, 0); + } recSet.useMatLUT = false; if (configStandalone.testSyncAsync) { procSet.eventDisplay = nullptr; @@ -505,6 +501,7 @@ int ReadEvent(int n) if ((configStandalone.proc.runQA || configStandalone.eventDisplay) && !configStandalone.QA.noMC) { chainTracking->ForceInitQA(); snprintf(filename, 256, "events/%s/mc.%d.dump", configStandalone.eventsDir, n); + chainTracking->GetQA()->UpdateChain(chainTracking); if (chainTracking->GetQA()->ReadO2MCData(filename)) { snprintf(filename, 256, "events/%s/mc.%d.dump", configStandalone.eventsDir, 0); if (chainTracking->GetQA()->ReadO2MCData(filename) && configStandalone.proc.runQA) { @@ -738,12 +735,14 @@ int main(int argc, char** argv) recAsync->SetDebugLevelTmp(configStandalone.proc.debugLevel); } chainTrackingAsync = recAsync->AddChain(); + chainTrackingAsync->SetQAFromForeignChain(chainTracking); } if (configStandalone.proc.doublePipeline) { if (configStandalone.proc.debugLevel >= 3) { recPipeline->SetDebugLevelTmp(configStandalone.proc.debugLevel); } chainTrackingPipeline = recPipeline->AddChain(); + chainTrackingPipeline->SetQAFromForeignChain(chainTracking); } #ifdef GPUCA_HAVE_O2HEADERS if (!configStandalone.proc.doublePipeline) { @@ -867,12 +866,12 @@ int main(int argc, char** argv) } else { grp.continuousMaxTimeBin = chainTracking->mIOPtrs.tpcZS ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcZS) : chainTracking->mIOPtrs.tpcPackedDigits ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcPackedDigits) : GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.clustersNative); printf("Max time bin set to %d\n", (int)grp.continuousMaxTimeBin); - rec->UpdateGRPSettings(&grp); + rec->UpdateSettings(&grp); if (recAsync) { - recAsync->UpdateGRPSettings(&grp); + recAsync->UpdateSettings(&grp); } if (recPipeline) { - recPipeline->UpdateGRPSettings(&grp); + recPipeline->UpdateSettings(&grp); } } } diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 5591ccf8ff06d..ac150fc19b9d1 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -82,13 +82,14 @@ set(SRCS_O2_DATATYPE_HEADERS set(SRCS_O2_DATATYPES ${SRCS_DATATYPES} Interface/GPUO2InterfaceConfigurableParam.cxx) -set(HDRS_CINT_O2 Merger/GPUTPCGMMergedTrack.h) +set(HDRS_CINT_O2 Merger/GPUTPCGMMergedTrack.h Merger/GPUTPCGMSliceTrack.h Merger/GPUTPCGMBorderTrack.h) set(HDRS_CINT_O2_DATATYPES DataTypes/GPUDataTypes.h DataTypes/GPUTPCGMMergedTrackHit.h) set(HDRS_CIND_O2_CONFIGURABLEPARAM Interface/GPUO2InterfaceConfigurableParam.h) set(HDRS_CINT_O2_ADDITIONAL DataTypes/GPUSettings.h Definitions/GPUSettingsList.h DataTypes/GPUDataTypes.h DataTypes/GPUTRDTrack.h DataTypes/CalibdEdxTrackTopologyPol.h DataTypes/CalibdEdxTrackTopologySpline.h) # Manual dependencies for ROOT dictionary generation set(SRCS_NO_CINT DataTypes/GPUMemorySizeScalers.cxx + DataTypes/GPUNewCalibValues.cxx Base/GPUReconstruction.cxx Base/GPUReconstructionCPU.cxx Base/GPUProcessor.cxx @@ -153,10 +154,10 @@ set(HDRS_INSTALL DataTypes/GPUO2FakeClasses.h Definitions/GPUSettingsList.h DataTypes/GPUOutputControl.h + DataTypes/GPUTriggerOutputs.h DataTypes/GPUO2DataTypes.h DataTypes/GPUHostDataTypes.h DataTypes/GPUdEdxInfo.h - DataTypes/GPUNewCalibValues.h DataTypes/GPUTRDInterfaceO2Track.h Base/GPUParam.inc DataCompression/TPCClusterDecompressor.inc diff --git a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx new file mode 100644 index 0000000000000..f443809d15ef5 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx @@ -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. + +/// \file GPUNewCalibValues.cxx +/// \author David Rohr + +#include "GPUNewCalibValues.h" + +using namespace GPUCA_NAMESPACE::gpu; + +void GPUNewCalibValues::updateFrom(const GPUNewCalibValues* from) +{ + if (from->newSolenoidField) { + solenoidField = from->newSolenoidField; + } + if (from->newContinuousMaxTimeBin) { + continuousMaxTimeBin = from->continuousMaxTimeBin; + } +} diff --git a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h index 3e1280dec81bf..5c5a770f81be3 100644 --- a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h +++ b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h @@ -27,6 +27,8 @@ struct GPUNewCalibValues { bool newContinuousMaxTimeBin = false; float solenoidField = 0.f; unsigned int continuousMaxTimeBin = 0; + + void updateFrom(const GPUNewCalibValues* from); }; } // namespace gpu diff --git a/GPU/GPUTracking/DataTypes/GPUOutputControl.h b/GPU/GPUTracking/DataTypes/GPUOutputControl.h index 4db0fc9eec37d..639b92e9aa381 100644 --- a/GPU/GPUTracking/DataTypes/GPUOutputControl.h +++ b/GPU/GPUTracking/DataTypes/GPUOutputControl.h @@ -74,6 +74,7 @@ struct GPUTrackingOutputs { GPUOutputControl tpcTracksO2; GPUOutputControl tpcTracksO2ClusRefs; GPUOutputControl tpcTracksO2Labels; + GPUOutputControl tpcTriggerWords; static constexpr size_t count() { return sizeof(GPUTrackingOutputs) / sizeof(GPUOutputControl); } GPUOutputControl* asArray() { return (GPUOutputControl*)this; } diff --git a/GPU/GPUTracking/DataTypes/GPUTRDInterfaceO2Track.h b/GPU/GPUTracking/DataTypes/GPUTRDInterfaceO2Track.h index fb1190b7b6216..19cad3649fa42 100644 --- a/GPU/GPUTracking/DataTypes/GPUTRDInterfaceO2Track.h +++ b/GPU/GPUTracking/DataTypes/GPUTRDInterfaceO2Track.h @@ -77,10 +77,11 @@ class trackInterface : public o2::track::TrackParCov GPUdi() void setPileUpDistance(unsigned char bwd, unsigned char fwd) { setUserField((((unsigned short)bwd) << 8) | fwd); } GPUdi() bool hasPileUpInfo() const { return getUserField() != 0; } - GPUdi() unsigned char getPileUpDistanceBwd() const { return ((unsigned char)getUserField()) >> 8; } - GPUdi() unsigned char getPileUpDistanceFwd() const { return ((unsigned char)getUserField()) & 255; } + GPUdi() bool hasPileUpInfoBothSides() const { return getPileUpDistanceBwd() > 0 && getPileUpDistanceFwd() > 0; } + GPUdi() unsigned char getPileUpDistanceBwd() const { return getUserField() >> 8; } + GPUdi() unsigned char getPileUpDistanceFwd() const { return getUserField() & 255; } GPUdi() unsigned short getPileUpSpan() const { return ((unsigned short)getPileUpDistanceBwd()) + getPileUpDistanceFwd(); } - GPUdi() float getPileUpMean() const { return 0.5 * ((float)getPileUpDistanceFwd()) - ((float)getPileUpDistanceBwd()); } + GPUdi() float getPileUpMean() const { return hasPileUpInfoBothSides() ? 0.5 * (getPileUpDistanceFwd() + getPileUpDistanceBwd()) : getPileUpDistanceFwd() + getPileUpDistanceBwd(); } GPUdi() float getPileUpTimeShiftMUS() const { return getPileUpMean() * o2::constants::lhc::LHCBunchSpacingMUS; } GPUdi() float getPileUpTimeErrorMUS() const { return getPileUpSpan() * o2::constants::lhc::LHCBunchSpacingMUS / 3.4641016; } diff --git a/GPU/GPUTracking/DataTypes/GPUTriggerOutputs.h b/GPU/GPUTracking/DataTypes/GPUTriggerOutputs.h new file mode 100644 index 0000000000000..0a3208f8e67a7 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/GPUTriggerOutputs.h @@ -0,0 +1,61 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file GPUTriggerOutputs.h +/// \author David Rohr + +#ifndef GPUTRIGGEROUTPUTS_H +#define GPUTRIGGEROUTPUTS_H + +#include "GPUCommonDef.h" +#include +#include +#ifdef GPUCA_HAVE_O2HEADERS +#include "DataFormatsTPC/ZeroSuppression.h" +#endif + +namespace GPUCA_NAMESPACE +{ +namespace gpu +{ + +struct GPUTriggerOutputs { +#ifdef GPUCA_HAVE_O2HEADERS + struct hasher { + size_t operator()(const o2::tpc::TriggerInfoDLBZS& key) const + { + std::array tmp; + memcpy((void*)tmp.data(), (const void*)&key, sizeof(key)); + std::hash std_hasher; + size_t result = 0; + for (size_t i = 0; i < tmp.size(); ++i) { + result ^= std_hasher(tmp[i]); + } + return result; + } + }; + + struct equal { + bool operator()(const o2::tpc::TriggerInfoDLBZS& lhs, const o2::tpc::TriggerInfoDLBZS& rhs) const + { + return memcmp((const void*)&lhs, (const void*)&rhs, sizeof(lhs)) == 0; + } + }; + + std::unordered_set triggers; + static_assert(sizeof(o2::tpc::TriggerInfoDLBZS) % sizeof(unsigned int) == 0); +#endif +}; + +} // namespace gpu +} // namespace GPUCA_NAMESPACE + +#endif diff --git a/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h b/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h index c402b2ed2b845..2688b416e9ecd 100644 --- a/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h +++ b/GPU/GPUTracking/Definitions/GPUDefConstantsAndSettings.h @@ -32,25 +32,11 @@ #define GPUCA_TRACKLET_SELECTOR_MIN_HITS_B5(QPTB5) (CAMath::Abs(QPTB5) > 10 ? 10 : (CAMath::Abs(QPTB5) > 5 ? 15 : 29)) // Minimum hits should depend on Pt, low Pt tracks can have few hits. 29 Hits default, 15 for < 200 mev, 10 for < 100 mev -#define GPUCA_GLOBAL_TRACKING_RANGE 45 // Number of rows from the upped/lower limit to search for global track candidates in for -#define GPUCA_GLOBAL_TRACKING_Y_RANGE_UPPER 0.85f // Inner portion of y-range in slice that is not used in searching for global track candidates -#define GPUCA_GLOBAL_TRACKING_Y_RANGE_LOWER 0.85f -#define GPUCA_GLOBAL_TRACKING_MIN_ROWS 10 // Min num of rows an additional global track must span over -#define GPUCA_GLOBAL_TRACKING_MIN_HITS 8 // Min num of hits for an additional global track - -#define GPUCA_MERGER_CE_ROWLIMIT 5 //Distance from first / last row in order to attempt merging accross CE -#define GPUCA_MERGER_LOOPER_QPTB5_LIMIT 4 // Min Q/Pt (@B=0.5T) to run special looper merging procedure -#define GPUCA_MERGER_HORIZONTAL_DOUBLE_QPTB5_LIMIT 2 // Min Q/Pt (@B=0.5T) to attempt second horizontal merge between slices after a vertical merge was found #define GPUCA_MERGER_MAX_TRACK_CLUSTERS 1000 // Maximum number of clusters a track may have after merging -#define GPUCA_Y_FACTOR 4 // Weight of y residual vs z residual in tracklet constructor #define GPUCA_MAXN 40 // Maximum number of neighbor hits to consider in one row in neightbors finder -#define GPUCA_TRACKLET_CONSTRUCTOR_MAX_ROW_GAP 4 // Maximum number of consecutive rows without hit in track following -#define GPUCA_TRACKLET_CONSTRUCTOR_MAX_ROW_GAP_SEED 2 // Same, but during fit of seed -#define GPUCA_MERGER_MAXN_MISSED_HARD 10 // Hard limit for number of missed rows in fit / propagation -#define GPUCA_MERGER_COV_LIMIT 1000 // Abort fit when y/z cov exceed the limit #define GPUCA_MIN_TRACK_PTB5_DEFAULT 0.010 // Default setting for minimum track Pt at some places (at B=0.5T) -#define GPUCA_MIN_TRACK_PTB5_REJECT 0.050f // Default setting for Pt (at B=0.5T) where tracks are rejected +#define GPUCA_MIN_TRACK_PTB5_REJECT_DEFAULT 0.050f // Default setting for Pt (at B=0.5T) where tracks are rejected #define GPUCA_MAX_SIN_PHI_LOW 0.99f // Limits for maximum sin phi during fit #define GPUCA_MAX_SIN_PHI 0.999f // Must be preprocessor define because c++ pre 11 cannot use static constexpr for initializes diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 046cfe9c46f4b..7322d6edeaef0 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -33,10 +33,10 @@ using namespace GPUCA_NAMESPACE::gpu; BeginNamespace(GPUCA_NAMESPACE) BeginNamespace(gpu) -// Settings concerning the reconstruction +// Settings concerning the reconstruction, stored as parameters in GPU constant memory // There must be no bool in here, use char, as sizeof(bool) is compiler dependent and fails on GPUs!!!!!! BeginSubConfig(GPUSettingsRecTPC, tpc, configStandalone.rec, "RECTPC", 0, "Reconstruction settings", rec_tpc) -AddOptionRTC(rejectQPtB5, float, 1.f / GPUCA_MIN_TRACK_PTB5_REJECT, "", 0, "QPt threshold to reject clusters of TPC tracks (Inverse Pt, scaled to B=0.5T!!!)") +AddOptionRTC(rejectQPtB5, float, 1.f / GPUCA_MIN_TRACK_PTB5_REJECT_DEFAULT, "", 0, "QPt threshold to reject clusters of TPC tracks (Inverse Pt, scaled to B=0.5T!!!)") AddOptionRTC(hitPickUpFactor, float, 1.f, "", 0, "multiplier for the combined cluster+track error during track following") AddOptionRTC(hitSearchArea2, float, 2.f, "", 0, "square of maximum search road of hits during seeding") AddOptionRTC(neighboursSearchArea, float, 3.f, "", 0, "area in cm for the search of neighbours, only used if searchWindowDZDR = 0") @@ -46,9 +46,13 @@ AddOptionRTC(clusterError2AdditionalY, float, 0.f, "", 0, "correction (additive) AddOptionRTC(clusterError2AdditionalZ, float, 0.f, "", 0, "correction (additive) for the squared cluster error during tracking") AddOptionRTC(clusterRejectChi2TolleranceY, float, 1.f, "", 0, "Multiplicative factor multiplied onto chi2 in Y direction for cluster rejection check during track fit") AddOptionRTC(clusterRejectChi2TolleranceZ, float, 1.f, "", 0, "Multiplicative factor multiplied onto chi2 in Z direction for cluster rejection check during track fit") +AddOptionRTC(sysClusErrorInner0, float, 1.f, "", 0, "Systematic cluster error parameterization clInner[0]") +AddOptionRTC(sysClusErrorInner1, float, 5.f, "", 0, "Systematic cluster error parameterization clInner[1]") +AddOptionRTC(sysClusErrorZRegion, float, -5.f, "", 0, "Systematic cluster error parameterization z Region") +AddOptionRTC(sysClusErrorZRegionSigInv, float, 1.f/2.0f, "", 0, "Systematic cluster error parameterization z Region Sigma Inverse") AddOptionRTC(minNClustersTrackSeed, int, -1, "", 0, "required min number of clusters on the track after track following (before merging)") AddOptionRTC(minNClustersFinalTrack, int, -1, "", 0, "required min number of clusters on the final track") -AddOptionRTC(searchWindowDZDR, float, 2.5, "", 0, "Use DZDR window for seeding instead of neighboursSearchArea") +AddOptionRTC(searchWindowDZDR, float, 2.5f, "", 0, "Use DZDR window for seeding instead of neighboursSearchArea") AddOptionRTC(trackReferenceX, float, 1000.f, "", 0, "Transport all tracks to this X after tracking (disabled if > 500, auto = 1000)") AddOptionRTC(zsThreshold, float, 2.0f, "", 0, "Zero-Suppression threshold") AddOptionRTC(tubeChi2, float, 5.f * 5.f, "", 0, "Max chi2 to mark cluster adjacent to track") @@ -56,13 +60,42 @@ AddOptionRTC(tubeMaxSize2, float, 2.5f * 2.5f, "", 0, "Square of max tube size ( AddOptionRTC(clustersShiftTimebins, float, 0, "", 0, "Shift of TPC clusters (applied during CTF cluster decoding)") AddOptionRTC(clustersShiftTimebinsClusterizer, float, 0, "", 0, "Shift of TPC clusters (applied during CTF clusterization)") AddOptionRTC(defaultZOffsetOverR, float, 0.5210953f, "", 0, "Shift of TPC clusters (applied during CTF cluster decoding)") -AddOptionRTC(PID_EKrangeMin, float, 0.38, "", 0, "min P of electron/K BB bands crossing") -AddOptionRTC(PID_EKrangeMax, float, 0.48, "", 0, "max P of electron/K BB bands crossing") -AddOptionRTC(PID_EPrangeMin, float, 0.75, "", 0, "min P of electron/p BB bands crossing") -AddOptionRTC(PID_EPrangeMax, float, 0.85, "", 0, "max P of electron/p BB bands crossing") +AddOptionRTC(PID_EKrangeMin, float, 0.38f, "", 0, "min P of electron/K BB bands crossing") +AddOptionRTC(PID_EKrangeMax, float, 0.48f, "", 0, "max P of electron/K BB bands crossing") +AddOptionRTC(PID_EPrangeMin, float, 0.75f, "", 0, "min P of electron/p BB bands crossing") +AddOptionRTC(PID_EPrangeMax, float, 0.85f, "", 0, "max P of electron/p BB bands crossing") +AddOptionRTC(extraClusterErrorEdgeY2, float, 0.35f, "", 0, "Additive extra cluster error for Y2 if edge flag set") +AddOptionRTC(extraClusterErrorEdgeZ2, float, 0.15f, "", 0, "Additive extra cluster error for Z2 if edge flag set") +AddOptionRTC(extraClusterErrorSingleY2, float, 0.2f, "", 0, "Additive extra cluster error for Y2 if edge single set") +AddOptionRTC(extraClusterErrorSingleZ2, float, 0.2f, "", 0, "Additive extra cluster error for Z2 if edge single set") +AddOptionRTC(extraClusterErrorSplitPadSharedSingleY2, float, 0.03f, "", 0, "Additive extra cluster error for Y2 if splitpad, shared, or single set") +AddOptionRTC(extraClusterErrorFactorSplitPadSharedSingleY2, float, 3.0f, "", 0, "Multiplicative extra cluster error for Y2 if splitpad, shared, or single set") +AddOptionRTC(extraClusterErrorSplitTimeSharedSingleZ2, float, 0.03f, "", 0, "Additive extra cluster error for Z2 if splittime, shared, or single set") +AddOptionRTC(extraClusterErrorFactorSplitTimeSharedSingleZ2, float, 3.0f, "", 0, "Multiplicative extra cluster error for Z2 if splittime, shared, or single set") +AddOptionArrayRTC(errorsCECrossing, float, 5, (0.f, 0.f, 0.f, 0.f, 0.f), "", 0, "Extra errors to add to track when crossing CE, depending on addErrorsCECrossing") +AddOptionRTC(globalTrackingYRangeUpper, float, 0.85f, "", 0, "Inner portion of y-range in slice that is not used in searching for global track candidates") +AddOptionRTC(globalTrackingYRangeLower, float, 0.85f, "", 0, "Inner portion of y-range in slice that is not used in searching for global track candidates") +AddOptionRTC(trackFollowingYFactor, float, 4.f, "", 0, "Weight of y residual vs z residual in tracklet constructor") +AddOptionRTC(trackMergerFactor2YS, float, 1.5f * 1.5f, "", 0, "factor2YS for track merging") +AddOptionRTC(trackMergerFactor2ZT, float, 1.5f * 1.5f, "", 0, "factor2ZT for track merging") +AddOptionRTC(trackMergerFactor2K, float, 2.0f * 2.0f, "", 0, "factor2K for track merging") +AddOptionRTC(trackMergerFactor2General, float, 3.5f * 3.5f, "", 0, "General factor for track merging") AddOptionRTC(maxTimeBinAboveThresholdIn1000Bin, unsigned short, 500, "", 0, "Except pad from cluster finding if total number of charges in a fragment is above this baseline (disable = 0)") AddOptionRTC(maxConsecTimeBinAboveThreshold, unsigned short, 200, "", 0, "Except pad from cluster finding if number of consecutive charges in a fragment is above this baseline (disable = 0)") AddOptionRTC(noisyPadSaturationThreshold, unsigned short, 700, "", 0, "Threshold where a timebin is considered saturated, disabling the noisy pad check for that pad") +AddOptionRTC(trackFitCovLimit, unsigned short, 1000, "", 0, "Abort fit when y/z cov exceed the limit") +AddOption(addErrorsCECrossing, unsigned char, 0, "", 0, "Add additional custom track errors when crossing CE, 0 = no custom errors but att 0.5 to sigma_z^2, 1 = only to cov diagonal, 2 = preserve correlations") +AddOptionRTC(trackMergerMinPartHits, unsigned char, 10, "", 0, "Minimum hits of track part during track merging") +AddOptionRTC(trackMergerMinTotalHits, unsigned char, 20, "", 0, "Minimum total of track part during track merging") +AddOptionRTC(mergerCERowLimit, unsigned char, 5, "", 0, "Distance from first / last row in order to attempt merging accross CE") +AddOptionRTC(mergerLooperQPtB5Limit, unsigned char, 4, "", 0, "Min Q/Pt (@B=0.5T) to run special looper merging procedure") +AddOptionRTC(mergerLooperSecondHorizontalQPtB5Limit, unsigned char, 2, "", 0, "Min Q/Pt (@B=0.5T) to attempt second horizontal merge between slices after a vertical merge was found") +AddOptionRTC(trackFollowingMaxRowGap, unsigned char, 4, "", 0, "Maximum number of consecutive rows without hit in track following") +AddOptionRTC(trackFollowingMaxRowGapSeed, unsigned char, 2, "", 0, "Maximum number of consecutive rows without hit in track following during fit of seed") +AddOptionRTC(trackFitMaxRowMissedHard, unsigned char, 10, "", 0, "Hard limit for number of missed rows in fit / propagation") +AddOptionRTC(globalTrackingRowRange, unsigned char, 45, "", 0, "Number of rows from the upped/lower limit to search for global track candidates in for") +AddOptionRTC(globalTrackingMinRows, unsigned char, 10, "", 0, "Min num of rows an additional global track must span over") +AddOptionRTC(globalTrackingMinHits, unsigned char, 8, "", 0, "Min num of hits for an additional global track") AddOptionRTC(noisyPadsQuickCheck, unsigned char, 1, "", 0, "Only check first fragment for noisy pads instead of all fragments (when test is enabled).") AddOptionRTC(cfQMaxCutoff, unsigned char, 3, "", 0, "Cluster Finder rejects cluster with qmax below or equal to this threshold") AddOptionRTC(cfQTotCutoff, unsigned char, 5, "", 0, "Cluster Finder rejects cluster with qtot below or equal to this threshold") @@ -88,10 +121,11 @@ AddOptionRTC(dropLoopers, unsigned char, 0, "", 0, "Drop looping tracks starting AddOptionRTC(mergerCovSource, unsigned char, 2, "", 0, "Method to obtain covariance in track merger: 0 = simple filterErrors method, 1 = use cov from track following, 2 = refit (default)") AddOptionRTC(mergerInterpolateErrors, unsigned char, 1, "", 0, "Use interpolation instead of extrapolation for chi2 based cluster rejection") AddOptionRTC(mergeCE, unsigned char, 1, "", 0, "Merge tracks accross the central electrode") -AddOptionRTC(retryRefit, char, 1, "", 0, "Retry refit when fit fails") +AddOptionRTC(retryRefit, char, 1, "", 0, "Retry refit with seeding errors and without cluster rejection when fit fails") AddOptionRTC(loopInterpolationInExtraPass, char, -1, "", 0, "Perform loop interpolation in an extra pass") AddOptionRTC(mergerReadFromTrackerDirectly, char, 1, "", 0, "Forward data directly from tracker to merger on GPU") AddOptionRTC(dropSecondaryLegsInOutput, char, 1, "", 0, "Do not store secondary legs of looping track in TrackTPC") +AddOptionRTC(enablePID, char, 1, "", 0, "Enable PID response") AddHelp("help", 'h') EndConfig() @@ -132,7 +166,7 @@ AddSubConfig(GPUSettingsRecTRD, trd) AddHelp("help", 'h') EndConfig() -// Settings steering the processing once the device was selected +// Settings steering the processing once the device was selected, only available on the host BeginSubConfig(GPUSettingsProcessingRTC, rtc, configStandalone.proc, "RTC", 0, "Processing settings", proc_rtc) AddOption(cacheOutput, bool, false, "", 0, "Cache RTC compilation results") AddOption(optConstexpr, bool, true, "", 0, "Replace constant variables by static constexpr expressions") @@ -141,6 +175,13 @@ AddOption(enable, bool, false, "", 0, "Use RTC to optimize GPU code") AddHelp("help", 'h') EndConfig() +BeginSubConfig(GPUSettingsProcessingParam, param, configStandalone.proc, "PARAM", 0, "Processing settings", proc_param) +AddOptionArray(tpcErrorParamY, float, 4, (0.06, 0.24, 0.12, 0.1), "", 0, "TPC Cluster Y Error Parameterization") +AddOptionArray(tpcErrorParamZ, float, 4, (0.06, 0.24, 0.15, 0.1), "", 0, "TPC Cluster Z Error Parameterization") +AddOption(tpcTriggerHandling, bool, true, "", 0, "Enable TPC trigger handling") +AddHelp("help", 'h') +EndConfig() + BeginSubConfig(GPUSettingsProcessing, proc, configStandalone, "PROC", 0, "Processing settings", proc) AddOption(platformNum, int, -1, "", 0, "Platform to use, in case the backend provides multiple platforms (-1 = auto-select)") AddOption(deviceNum, int, -1, "gpuDevice", 0, "Set GPU device to use (-1: automatic, -2: for round-robin usage in timeslice-pipeline)") @@ -208,8 +249,10 @@ AddOption(lateO2MatLutProvisioningSize, unsigned int, 0u, "", 0, "Memory size to AddOption(throttleAlarms, bool, false, "", 0, "Throttle rate at which alarms are sent to the InfoLogger in online runs") AddOption(outputSanityCheck, bool, false, "", 0, "Run some simple sanity checks finding errors in the output") AddOption(tpcSingleSector, int, -1, "", 0, "Restrict TPC processing to a single sector") +AddOption(tpcDownscaledEdx, unsigned char, 0, "", 0, "If != 0, downscale dEdx processing (if enabled) to x %") AddVariable(eventDisplay, GPUCA_NAMESPACE::gpu::GPUDisplayFrontendInterface*, nullptr) AddSubConfig(GPUSettingsProcessingRTC, rtc) +AddSubConfig(GPUSettingsProcessingParam, param) AddHelp("help", 'h') EndConfig() @@ -299,7 +342,6 @@ AddOption(uniformBuffersInDeviceMemory, bool, 1, "", 0, "Have uniform buffers in AddHelp("help", 'h') EndConfig() - // Settings concerning the event display (fixed settings, cannot be changed) BeginSubConfig(GPUSettingsDisplay, display, configStandalone, "GL", 'g', "OpenGL display settings", display) AddOption(showTPCTracksFromO2Format, bool, false, "", 0, "Use TPC tracks in O2 output format instead of GPU format") diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h index 47cf2a6f2b3d2..431e283aa6390 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2.h @@ -19,7 +19,12 @@ #pragma link off all functions; #pragma link C++ class o2::gpu::GPUTPCGMMergedTrack + ; +#pragma link C++ class o2::gpu::GPUTPCGMSliceTrack + ; +#pragma link C++ class o2::gpu::GPUTPCGMBorderTrack + ; #pragma link C++ class o2::gpu::GPUTPCGMTrackParam + ; +#pragma link C++ class o2::gpu::GPUTPCTrack + ; +#pragma link C++ struct o2::gpu::GPUTPCBaseTrackParam + ; +#pragma link C++ struct o2::gpu::GPUTPCGMSliceTrack::sliceTrackParam + ; #pragma link C++ class o2::gpu::gputpcgmmergertypes::GPUTPCOuterParam + ; #pragma link C++ class o2::gpu::gputpcgmmergertypes::InterpolationErrorHit + ; diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h index 531dc2dbe1d13..16f9b769123f7 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h @@ -29,6 +29,7 @@ #pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecTPC + ; #pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecTRD + ; #pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessing + ; +#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingParam + ; #pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingRTC + ; #pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplay + ; #pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayLight + ; diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index b4c078f6200eb..fb7324f3b055c 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -42,6 +42,7 @@ #include "GPUMemorySizeScalers.h" #include "GPUTrackingInputProvider.h" #include "GPUNewCalibValues.h" +#include "GPUTriggerOutputs.h" #ifdef GPUCA_HAVE_O2HEADERS #include "GPUTPCClusterStatistics.h" @@ -65,7 +66,7 @@ using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; using namespace o2::trd; -GPUChainTracking::GPUChainTracking(GPUReconstruction* rec, unsigned int maxTPCHits, unsigned int maxTRDTracklets) : GPUChain(rec), mIOPtrs(processors()->ioPtrs), mInputsHost(new GPUTrackingInputProvider), mInputsShadow(new GPUTrackingInputProvider), mClusterNativeAccess(new ClusterNativeAccess), mMaxTPCHits(maxTPCHits), mMaxTRDTracklets(maxTRDTracklets), mDebugFile(new std::ofstream) +GPUChainTracking::GPUChainTracking(GPUReconstruction* rec, unsigned int maxTPCHits, unsigned int maxTRDTracklets) : GPUChain(rec), mIOPtrs(processors()->ioPtrs), mInputsHost(new GPUTrackingInputProvider), mInputsShadow(new GPUTrackingInputProvider), mClusterNativeAccess(new ClusterNativeAccess), mTriggerBuffer(new GPUTriggerOutputs), mMaxTPCHits(maxTPCHits), mMaxTRDTracklets(maxTRDTracklets), mDebugFile(new std::ofstream) { ClearIOPointers(); mFlatObjectsShadow.mChainTracking = this; @@ -372,11 +373,14 @@ int GPUChainTracking::Init() } if (GPUQA::QAAvailable() && (GetProcessingSettings().runQA || GetProcessingSettings().eventDisplay)) { - mQA.reset(new GPUQA(this)); + auto& qa = mQAFromForeignChain ? mQAFromForeignChain->mQA : mQA; + if (!qa) { + qa.reset(new GPUQA(this)); + } } if (GetProcessingSettings().eventDisplay) { #ifndef GPUCA_ALIROOT_LIB - mEventDisplay.reset(GPUDisplayInterface::getDisplay(GetProcessingSettings().eventDisplay, this, mQA.get())); + mEventDisplay.reset(GPUDisplayInterface::getDisplay(GetProcessingSettings().eventDisplay, this, GetQA())); #endif if (mEventDisplay == nullptr) { throw std::runtime_error("Error loading event display"); @@ -401,53 +405,53 @@ int GPUChainTracking::Init() return 0; } -void GPUChainTracking::UpdateGPUCalibObjects(int stream) +void GPUChainTracking::UpdateGPUCalibObjects(int stream, const GPUCalibObjectsConst* ptrMask) { - if (processors()->calibObjects.fastTransform) { + if (processors()->calibObjects.fastTransform && (ptrMask == nullptr || ptrMask->fastTransform)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.fastTransform, (const void*)processors()->calibObjects.fastTransform, sizeof(*processors()->calibObjects.fastTransform)); memcpy((void*)mFlatObjectsShadow.mTpcTransformBuffer, (const void*)processors()->calibObjects.fastTransform->getFlatBufferPtr(), processors()->calibObjects.fastTransform->getFlatBufferSize()); mFlatObjectsShadow.mCalibObjects.fastTransform->clearInternalBufferPtr(); mFlatObjectsShadow.mCalibObjects.fastTransform->setActualBufferAddress(mFlatObjectsShadow.mTpcTransformBuffer); mFlatObjectsShadow.mCalibObjects.fastTransform->setFutureBufferAddress(mFlatObjectsDevice.mTpcTransformBuffer); } - if (processors()->calibObjects.fastTransformRef) { + if (processors()->calibObjects.fastTransformRef && (ptrMask == nullptr || ptrMask->fastTransformRef)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.fastTransformRef, (const void*)processors()->calibObjects.fastTransformRef, sizeof(*processors()->calibObjects.fastTransformRef)); memcpy((void*)mFlatObjectsShadow.mTpcTransformRefBuffer, (const void*)processors()->calibObjects.fastTransformRef->getFlatBufferPtr(), processors()->calibObjects.fastTransformRef->getFlatBufferSize()); mFlatObjectsShadow.mCalibObjects.fastTransformRef->clearInternalBufferPtr(); mFlatObjectsShadow.mCalibObjects.fastTransformRef->setActualBufferAddress(mFlatObjectsShadow.mTpcTransformRefBuffer); mFlatObjectsShadow.mCalibObjects.fastTransformRef->setFutureBufferAddress(mFlatObjectsDevice.mTpcTransformRefBuffer); } - if (processors()->calibObjects.fastTransformHelper) { + if (processors()->calibObjects.fastTransformHelper && (ptrMask == nullptr || ptrMask->fastTransformHelper)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.fastTransformHelper, (const void*)processors()->calibObjects.fastTransformHelper, sizeof(*processors()->calibObjects.fastTransformHelper)); mFlatObjectsShadow.mCalibObjects.fastTransformHelper->setCorrMap(mFlatObjectsShadow.mCalibObjects.fastTransform); mFlatObjectsShadow.mCalibObjects.fastTransformHelper->setCorrMapRef(mFlatObjectsShadow.mCalibObjects.fastTransformRef); } #ifdef GPUCA_HAVE_O2HEADERS - if (processors()->calibObjects.dEdxCalibContainer) { + if (processors()->calibObjects.dEdxCalibContainer && (ptrMask == nullptr || ptrMask->dEdxCalibContainer)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.dEdxCalibContainer, (const void*)processors()->calibObjects.dEdxCalibContainer, sizeof(*processors()->calibObjects.dEdxCalibContainer)); memcpy((void*)mFlatObjectsShadow.mdEdxSplinesBuffer, (const void*)processors()->calibObjects.dEdxCalibContainer->getFlatBufferPtr(), processors()->calibObjects.dEdxCalibContainer->getFlatBufferSize()); mFlatObjectsShadow.mCalibObjects.dEdxCalibContainer->clearInternalBufferPtr(); mFlatObjectsShadow.mCalibObjects.dEdxCalibContainer->setActualBufferAddress(mFlatObjectsShadow.mdEdxSplinesBuffer); mFlatObjectsShadow.mCalibObjects.dEdxCalibContainer->setFutureBufferAddress(mFlatObjectsDevice.mdEdxSplinesBuffer); } - if (processors()->calibObjects.matLUT) { + if (processors()->calibObjects.matLUT && (ptrMask == nullptr || ptrMask->matLUT)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.matLUT, (const void*)processors()->calibObjects.matLUT, sizeof(*processors()->calibObjects.matLUT)); memcpy((void*)mFlatObjectsShadow.mMatLUTBuffer, (const void*)processors()->calibObjects.matLUT->getFlatBufferPtr(), processors()->calibObjects.matLUT->getFlatBufferSize()); mFlatObjectsShadow.mCalibObjects.matLUT->clearInternalBufferPtr(); mFlatObjectsShadow.mCalibObjects.matLUT->setActualBufferAddress(mFlatObjectsShadow.mMatLUTBuffer); mFlatObjectsShadow.mCalibObjects.matLUT->setFutureBufferAddress(mFlatObjectsDevice.mMatLUTBuffer); } - if (processors()->calibObjects.trdGeometry) { + if (processors()->calibObjects.trdGeometry && (ptrMask == nullptr || ptrMask->trdGeometry)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.trdGeometry, (const void*)processors()->calibObjects.trdGeometry, sizeof(*processors()->calibObjects.trdGeometry)); mFlatObjectsShadow.mCalibObjects.trdGeometry->clearInternalBufferPtr(); } - if (processors()->calibObjects.tpcPadGain) { + if (processors()->calibObjects.tpcPadGain && (ptrMask == nullptr || ptrMask->tpcPadGain)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.tpcPadGain, (const void*)processors()->calibObjects.tpcPadGain, sizeof(*processors()->calibObjects.tpcPadGain)); } - if (processors()->calibObjects.tpcZSLinkMapping) { + if (processors()->calibObjects.tpcZSLinkMapping && (ptrMask == nullptr || ptrMask->tpcZSLinkMapping)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.tpcZSLinkMapping, (const void*)processors()->calibObjects.tpcZSLinkMapping, sizeof(*processors()->calibObjects.tpcZSLinkMapping)); } - if (processors()->calibObjects.o2Propagator) { + if (processors()->calibObjects.o2Propagator && (ptrMask == nullptr || ptrMask->o2Propagator)) { memcpy((void*)mFlatObjectsShadow.mCalibObjects.o2Propagator, (const void*)processors()->calibObjects.o2Propagator, sizeof(*processors()->calibObjects.o2Propagator)); mFlatObjectsShadow.mCalibObjects.o2Propagator->setGPUField(&processorsDevice()->param.polynomialField); mFlatObjectsShadow.mCalibObjects.o2Propagator->setBz(param().polynomialField.GetNominalBz()); @@ -478,19 +482,21 @@ int GPUChainTracking::PrepareEvent() int GPUChainTracking::ForceInitQA() { - if (!mQA) { - mQA.reset(new GPUQA(this)); + auto& qa = mQAFromForeignChain ? mQAFromForeignChain->mQA : mQA; + if (!qa) { + qa.reset(new GPUQA(this)); } - if (!mQA->IsInitialized()) { - return mQA->InitQA(); + if (!GetQA()->IsInitialized()) { + return GetQA()->InitQA(); } return 0; } int GPUChainTracking::Finalize() { - if (GetProcessingSettings().runQA && mQA->IsInitialized() && !(mConfigQA && mConfigQA->shipToQC)) { - mQA->DrawQAHistograms(); + if (GetProcessingSettings().runQA && GetQA()->IsInitialized() && !(mConfigQA && mConfigQA->shipToQC) && !mQAFromForeignChain) { + GetQA()->UpdateChain(this); + GetQA()->DrawQAHistograms(); } if (GetProcessingSettings().debugLevel >= 6) { mDebugFile->close(); @@ -606,35 +612,57 @@ void GPUChainTracking::SetTRDGeometry(std::unique_ptr&& g processors()->calibObjects.trdGeometry = mTRDGeometryU.get(); } -void GPUChainTracking::DoQueuedCalibUpdates(int stream) +int GPUChainTracking::DoQueuedUpdates(int stream, bool updateSlave) { + int retVal = 0; + std::unique_ptr grp; + const GPUSettingsProcessing* p = nullptr; + std::lock_guard lk(mMutexUpdateCalib); if (mUpdateNewCalibObjects) { - void** pSrc = (void**)&mNewCalibObjects; + void* const* pSrc = (void* const*)mNewCalibObjects.get(); void** pDst = (void**)&processors()->calibObjects; - for (unsigned int i = 0; i < sizeof(mNewCalibObjects) / sizeof(void*); i++) { + for (unsigned int i = 0; i < sizeof(processors()->calibObjects) / sizeof(void*); i++) { if (pSrc[i]) { pDst[i] = pSrc[i]; } } if (mRec->IsGPU()) { + std::array oldFlatPtrs, oldFlatPtrsDevice; + memcpy(oldFlatPtrs.data(), (void*)&mFlatObjectsShadow, oldFlatPtrs.size()); + memcpy(oldFlatPtrsDevice.data(), (void*)&mFlatObjectsDevice, oldFlatPtrsDevice.size()); mRec->ResetRegisteredMemoryPointers(mFlatObjectsShadow.mMemoryResFlat); - UpdateGPUCalibObjects(stream); + bool ptrsChanged = memcmp(oldFlatPtrs.data(), (void*)&mFlatObjectsShadow, oldFlatPtrs.size()) || memcmp(oldFlatPtrsDevice.data(), (void*)&mFlatObjectsDevice, oldFlatPtrsDevice.size()); + if (ptrsChanged) { + GPUInfo("Updating all calib objects since pointers changed"); + } + UpdateGPUCalibObjects(stream, ptrsChanged ? nullptr : mNewCalibObjects.get()); } if (mNewCalibValues->newSolenoidField || mNewCalibValues->newContinuousMaxTimeBin) { - GPUSettingsGRP grp = mRec->GetGRPSettings(); + grp = std::make_unique(mRec->GetGRPSettings()); if (mNewCalibValues->newSolenoidField) { - grp.solenoidBz = mNewCalibValues->solenoidField; + grp->solenoidBz = mNewCalibValues->solenoidField; } if (mNewCalibValues->newContinuousMaxTimeBin) { - grp.continuousMaxTimeBin = mNewCalibValues->continuousMaxTimeBin; + grp->continuousMaxTimeBin = mNewCalibValues->continuousMaxTimeBin; } - mRec->UpdateGRPSettings(&grp); } } - if ((mUpdateNewCalibObjects || mRec->slavesExist()) && mRec->IsGPU()) { + if (GetProcessingSettings().tpcDownscaledEdx != 0) { + p = &GetProcessingSettings(); + } + if (grp || p) { + mRec->UpdateSettings(grp.get(), p); + retVal = 1; + } + + if ((mUpdateNewCalibObjects || (mRec->slavesExist() && updateSlave)) && mRec->IsGPU()) { UpdateGPUCalibObjectsPtrs(stream); // Reinitialize + retVal = 1; } + mNewCalibObjects.reset(nullptr); + mNewCalibValues.reset(nullptr); mUpdateNewCalibObjects = false; + return retVal; } int GPUChainTracking::RunChain() @@ -647,8 +675,8 @@ int GPUChainTracking::RunChain() mCompressionStatistics.reset(new GPUTPCClusterStatistics); } const bool needQA = GPUQA::QAAvailable() && (GetProcessingSettings().runQA || (GetProcessingSettings().eventDisplay && (mIOPtrs.nMCInfosTPC || GetProcessingSettings().runMC))); - if (needQA && mQA->IsInitialized() == false) { - if (mQA->InitQA(GetProcessingSettings().runQA ? -GetProcessingSettings().runQA : -1)) { + if (needQA && GetQA()->IsInitialized() == false) { + if (GetQA()->InitQA(GetProcessingSettings().runQA ? -GetProcessingSettings().runQA : -1)) { return 1; } } @@ -658,7 +686,7 @@ int GPUChainTracking::RunChain() if (GetProcessingSettings().debugLevel >= 6) { *mDebugFile << "\n\nProcessing event " << mRec->getNEventsProcessed() << std::endl; } - DoQueuedCalibUpdates(0); + DoQueuedUpdates(0); mRec->getGeneralStepTimer(GeneralStep::Prepare).Start(); try { @@ -773,7 +801,8 @@ int GPUChainTracking::RunChainFinalize() const bool needQA = GPUQA::QAAvailable() && (GetProcessingSettings().runQA || (GetProcessingSettings().eventDisplay && mIOPtrs.nMCInfosTPC)); if (needQA && mFractionalQAEnabled) { mRec->getGeneralStepTimer(GeneralStep::QA).Start(); - mQA->RunQA(!GetProcessingSettings().runQA); + GetQA()->UpdateChain(this); + GetQA()->RunQA(!GetProcessingSettings().runQA); mRec->getGeneralStepTimer(GeneralStep::QA).Stop(); if (GetProcessingSettings().debugLevel == 0) { GPUInfo("Total QA runtime: %d us", (int)(mRec->getGeneralStepTimer(GeneralStep::QA).GetElapsedTime() * 1000000)); @@ -948,7 +977,22 @@ void GPUChainTracking::SetDefaultInternalO2Propagator(bool useGPUField) void GPUChainTracking::SetUpdateCalibObjects(const GPUCalibObjectsConst& obj, const GPUNewCalibValues& vals) { - mNewCalibObjects = obj; - mNewCalibValues.reset(new GPUNewCalibValues(vals)); + std::lock_guard lk(mMutexUpdateCalib); + if (mNewCalibObjects) { + void* const* pSrc = (void* const*)&obj; + void** pDst = (void**)mNewCalibObjects.get(); + for (unsigned int i = 0; i < sizeof(*mNewCalibObjects) / sizeof(void*); i++) { + if (pSrc[i]) { + pDst[i] = pSrc[i]; + } + } + } else { + mNewCalibObjects.reset(new GPUCalibObjectsConst(obj)); + } + if (mNewCalibValues) { + mNewCalibValues->updateFrom(&vals); + } else { + mNewCalibValues.reset(new GPUNewCalibValues(vals)); + } mUpdateNewCalibObjects = true; } diff --git a/GPU/GPUTracking/Global/GPUChainTracking.h b/GPU/GPUTracking/Global/GPUChainTracking.h index 7b5fca14e8bf0..acdf9f7b446c2 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -19,6 +19,8 @@ #include "GPUReconstructionHelpers.h" #include "GPUDataTypes.h" #include +#include +#include #include #include #include @@ -64,6 +66,7 @@ class GPUTrackingInputProvider; struct GPUChainTrackingFinalContext; struct GPUTPCCFChainContext; struct GPUNewCalibValues; +struct GPUTriggerOutputs; class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelegateBase { @@ -83,7 +86,7 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega bool SupportsDoublePipeline() override { return true; } int FinalizePipelinedProcessing() override; void ClearErrorCodes(bool cpuOnly = false); - void DoQueuedCalibUpdates(int stream); // Forces doing queue calib updates, don't call when you are not sure you are allowed to do so! + int DoQueuedUpdates(int stream, bool updateSlave = true); // Forces doing queue calib updates, don't call when you are not sure you are allowed to do so! bool QARanForTF() const { return mFractionalQAEnabled; } // Structures for input and output data @@ -153,9 +156,10 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega const GPUTPCGMMerger& GetTPCMerger() const { return processors()->tpcMerger; } GPUTPCGMMerger& GetTPCMerger() { return processors()->tpcMerger; } GPUDisplayInterface* GetEventDisplay() { return mEventDisplay.get(); } - const GPUQA* GetQA() const { return mQA.get(); } - GPUQA* GetQA() { return mQA.get(); } + const GPUQA* GetQA() const { return mQAFromForeignChain ? mQAFromForeignChain->mQA.get() : mQA.get(); } + GPUQA* GetQA() { return mQAFromForeignChain ? mQAFromForeignChain->mQA.get() : mQA.get(); } int ForceInitQA(); + void SetQAFromForeignChain(GPUChainTracking* chain) { mQAFromForeignChain = chain; } // Processing functions int RunTPCClusterizer(bool synchronizeOutput = true); @@ -189,12 +193,8 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega void SetUpdateCalibObjects(const GPUCalibObjectsConst& obj, const GPUNewCalibValues& vals); void SetDefaultInternalO2Propagator(bool useGPUField); void LoadClusterErrors(); - void SetOutputControlCompressedClusters(GPUOutputControl* v) { mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::compressedClusters)] = v; } - void SetOutputControlClustersNative(GPUOutputControl* v) { mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)] = v; } - void SetOutputControlTPCTracks(GPUOutputControl* v) { mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::tpcTracks)] = v; } - void SetOutputControlClusterLabels(GPUOutputControl* v) { mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clusterLabels)] = v; } - void SetOutputControlSharedClusterMap(GPUOutputControl* v) { mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::sharedClusterMap)] = v; } void SetSubOutputControl(int i, GPUOutputControl* v) { mSubOutputControls[i] = v; } + void SetFinalInputCallback(std::function v) { mWaitForFinalInputs = v; } const GPUSettingsDisplay* mConfigDisplay = nullptr; // Abstract pointer to Standalone Display Configuration Structure const GPUSettingsQA* mConfigQA = nullptr; // Abstract pointer to Standalone QA Configuration Structure @@ -211,7 +211,7 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega short mMemoryResFlat = -1; void* SetPointersFlatObjects(void* mem); }; - void UpdateGPUCalibObjects(int stream); + void UpdateGPUCalibObjects(int stream, const GPUCalibObjectsConst* ptrMask = nullptr); void UpdateGPUCalibObjectsPtrs(int stream); struct eventStruct // Must consist only of void* ptr that will hold the GPU event ptrs! @@ -255,6 +255,7 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega // Display / QA bool mDisplayRunning = false; std::unique_ptr mEventDisplay; + GPUChainTracking* mQAFromForeignChain = nullptr; std::unique_ptr mQA; std::unique_ptr mCompressionStatistics; @@ -271,11 +272,12 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega // Ptrs to internal buffers std::unique_ptr mClusterNativeAccess; std::array mSubOutputControls = {nullptr}; + std::unique_ptr mTriggerBuffer; // (Ptrs to) configuration objects std::unique_ptr mCFContext; bool mTPCSliceScratchOnStack = false; - GPUCalibObjectsConst mNewCalibObjects; + std::unique_ptr mNewCalibObjects; bool mUpdateNewCalibObjects = false; std::unique_ptr mNewCalibValues; @@ -308,9 +310,11 @@ class GPUChainTracking : public GPUChain, GPUReconstructionHelpers::helperDelega void RunTPCTrackingMerger_MergeBorderTracks(char withinSlice, char mergeMode, GPUReconstruction::krnlDeviceType deviceType); void RunTPCTrackingMerger_Resolve(char useOrigTrackParam, char mergeAll, GPUReconstruction::krnlDeviceType deviceType); - std::atomic_flag mLockAtomic = ATOMIC_FLAG_INIT; + std::atomic_flag mLockAtomicOutputBuffer = ATOMIC_FLAG_INIT; + std::mutex mMutexUpdateCalib; std::unique_ptr mPipelineFinalizationCtx; GPUChainTrackingFinalContext* mPipelineNotifyCtx = nullptr; + std::function mWaitForFinalInputs; int HelperReadEvent(int iSlice, int threadId, GPUReconstructionHelpers::helperParam* par); int HelperOutput(int iSlice, int threadId, GPUReconstructionHelpers::helperParam* par); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 7e829cba83afb..59e4df2246b4c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -24,6 +24,7 @@ #include "CommonDataFormat/InteractionRecord.h" #endif #ifdef GPUCA_HAVE_O2HEADERS +#include "GPUTriggerOutputs.h" #include "GPUHostDataTypes.h" #include "GPUTPCCFChainContext.h" #include "DataFormatsTPC/ZeroSuppression.h" @@ -177,7 +178,7 @@ std::pair GPUChainTracking::TPCClusterizerDecodeZSCo unsigned int pageCounter = 0; unsigned int emptyPages = 0; for (unsigned int k = 0; k < mIOPtrs.tpcZS->slice[iSlice].count[j]; k++) { - if (GetProcessingSettings().tpcSingleSector != -1 && GetProcessingSettings().tpcSingleSector != iSlice) { + if (GetProcessingSettings().tpcSingleSector != -1 && GetProcessingSettings().tpcSingleSector != (int)iSlice) { break; } nPages += mIOPtrs.tpcZS->slice[iSlice].nZSPtr[j][k]; @@ -198,6 +199,13 @@ std::pair GPUChainTracking::TPCClusterizerDecodeZSCo const TPCZSHDR* const hdr = (const TPCZSHDR*)(rdh_utils::getLink(o2::raw::RDHUtils::getFEEID(*rdh)) == rdh_utils::DLBZSLinkID ? (page + o2::raw::RDHUtils::getMemorySize(*rdh) - sizeof(TPCZSHDRV2)) : (page + sizeof(o2::header::RAWDataHeader))); if (mCFContext->zsVersion == -1) { mCFContext->zsVersion = hdr->version; + if (GetProcessingSettings().param.tpcTriggerHandling && mCFContext->zsVersion < ZSVersion::ZSVersionDenseLinkBased) { // TODO: Move tpcTriggerHandling to recoSteps bitmask + static bool errorShown = false; + if (errorShown == false) { + GPUAlarm("Trigger handling only possible with TPC Dense Link Based data, received version %d, disabling", mCFContext->zsVersion); + } + errorShown = true; + } } else if (mCFContext->zsVersion != (int)hdr->version) { GPUError("Received TPC ZS 8kb page of mixed versions, expected %d, received %d (linkid %d, feeCRU %d, feeEndpoint %d, feelinkid %d)", mCFContext->zsVersion, (int)hdr->version, (int)o2::raw::RDHUtils::getLinkID(*rdh), (int)rdh_utils::getCRU(*rdh), (int)rdh_utils::getEndPoint(*rdh), (int)rdh_utils::getLink(*rdh)); constexpr size_t bufferSize = 3 * std::max(sizeof(*rdh), sizeof(*hdr)) + 1; @@ -219,6 +227,18 @@ std::pair GPUChainTracking::TPCClusterizerDecodeZSCo GPUFatal("Cannot process with invalid TPC ZS data, exiting"); } } + if (GetProcessingSettings().param.tpcTriggerHandling) { + const TPCZSHDRV2* const hdr2 = (const TPCZSHDRV2*)hdr; + if (hdr2->flags & TPCZSHDRV2::ZSFlags::TriggerWordPresent) { + const char* triggerWord = (const char*)hdr - TPCZSHDRV2::TRIGGER_WORD_SIZE; + o2::tpc::TriggerInfoDLBZS tmp; + memcpy((void*)&tmp.triggerWord, triggerWord, TPCZSHDRV2::TRIGGER_WORD_SIZE); + tmp.orbit = o2::raw::RDHUtils::getHeartBeatOrbit(*rdh); + if (tmp.triggerWord.isValid(0)) { + mTriggerBuffer->triggers.emplace(tmp); + } + } + } nDigits += hdr->nADCsamples; endpointAdcSamples[j] += hdr->nADCsamples; unsigned int timeBin = (hdr->timeOffset + (o2::raw::RDHUtils::getHeartBeatOrbit(*rdh) - firstHBF) * o2::constants::lhc::LHCMaxBunches) / LHCBCPERTIMEBIN; @@ -456,6 +476,9 @@ int GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) mCFContext->tpcMaxTimeBin = maxAllowedTimebin; const CfFragment fragmentMax{(tpccf::TPCTime)mCFContext->tpcMaxTimeBin + 1, maxFragmentLen}; mCFContext->prepare(mIOPtrs.tpcZS, fragmentMax); + if (GetProcessingSettings().param.tpcTriggerHandling) { + mTriggerBuffer->triggers.clear(); + } if (mIOPtrs.tpcZS) { unsigned int nDigitsFragmentMax[NSLICES]; mCFContext->zsVersion = -1; @@ -507,6 +530,7 @@ int GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) processors()->tpcClusterer[iSlice].SetNMaxDigits(nDigits, mCFContext->nPagesFragmentMax, nDigits, 0); } } + if (mIOPtrs.tpcZS) { GPUInfo("Event has %u 8kb TPC ZS pages (version %d), %lld digits", mCFContext->nPagesTotal, mCFContext->zsVersion, (long long int)mRec->MemoryScalers()->nTPCdigits); } else { @@ -608,6 +632,9 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeBuffer); } if (buildNativeHost && !(buildNativeGPU && GetProcessingSettings().delayedOutput)) { + if (mWaitForFinalInputs) { + GPUFatal("Cannot use waitForFinalInput callback without delayed output"); + } AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); } @@ -621,6 +648,18 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) char transferRunning[NSLICES] = {0}; unsigned int outputQueueStart = mOutputQueue.size(); + auto notifyForeignChainFinished = [this]() { + if (mPipelineNotifyCtx) { + SynchronizeStream(mRec->NStreams() - 2); // Must finish before updating ioPtrs in (global) constant memory + { + std::lock_guard lock(mPipelineNotifyCtx->mutex); + mPipelineNotifyCtx->ready = true; + } + mPipelineNotifyCtx->cond.notify_one(); + } + }; + bool synchronizeCalibUpdate = false; + for (unsigned int iSliceBase = 0; iSliceBase < NSLICES; iSliceBase += GetProcessingSettings().nTPCClustererLanes) { std::vector laneHasData(GetProcessingSettings().nTPCClustererLanes, false); static_assert(NSLICES <= GPUCA_MAX_STREAMS, "Stream events must be able to hold all slices"); @@ -701,7 +740,7 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } } - if (GetProcessingSettings().tpcSingleSector == -1 || GetProcessingSettings().tpcSingleSector == iSlice) { + if (GetProcessingSettings().tpcSingleSector == -1 || GetProcessingSettings().tpcSingleSector == (int)iSlice) { if (not mIOPtrs.tpcZS) { runKernel(GetGrid(1, lane), {iSlice}, {}, mIOPtrs.tpcZS == nullptr); TransferMemoryResourceLinkToHost(RecoStep::TPCClusterFinding, clusterer.mMemoryId, lane); @@ -896,6 +935,14 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) GPUMemCpy(RecoStep::TPCClusterFinding, (void*)&mInputsHost->mPclusterNativeOutput[nClsFirst], (void*)&mInputsShadow->mPclusterNativeBuffer[nClsFirst], (nClsTotal - nClsFirst) * sizeof(mInputsHost->mPclusterNativeOutput[nClsFirst]), mRec->NStreams() - 1, false); } } + + if (mWaitForFinalInputs && iSliceBase >= 21 && (int)iSliceBase < 21 + GetProcessingSettings().nTPCClustererLanes) { + notifyForeignChainFinished(); + } + if (mWaitForFinalInputs && iSliceBase >= 30 && (int)iSliceBase < 30 + GetProcessingSettings().nTPCClustererLanes) { + mWaitForFinalInputs(); + synchronizeCalibUpdate = DoQueuedUpdates(0, false); + } } for (int i = 0; i < GetProcessingSettings().nTPCClustererLanes; i++) { if (transferRunning[i]) { @@ -903,6 +950,16 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } } + if (GetProcessingSettings().param.tpcTriggerHandling) { + GPUOutputControl* triggerOutput = mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::tpcTriggerWords)]; + if (triggerOutput && triggerOutput->allocator) { + // GPUInfo("Storing %lu trigger words", mTriggerBuffer->triggers.size()); + auto* outputBuffer = (decltype(mTriggerBuffer->triggers)::value_type*)triggerOutput->allocator(mTriggerBuffer->triggers.size() * sizeof(decltype(mTriggerBuffer->triggers)::value_type)); + std::copy(mTriggerBuffer->triggers.begin(), mTriggerBuffer->triggers.end(), outputBuffer); + } + mTriggerBuffer->triggers.clear(); + } + ClusterNativeAccess::ConstMCLabelContainerView* mcLabelsConstView = nullptr; if (propagateMCLabels) { // TODO: write to buffer directly @@ -945,11 +1002,8 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) mIOPtrs.clustersNative = tmpNative; } - if (mPipelineNotifyCtx) { - SynchronizeStream(mRec->NStreams() - 2); // Must finish before updating ioPtrs in (global) constant memory - std::lock_guard lock(mPipelineNotifyCtx->mutex); - mPipelineNotifyCtx->ready = true; - mPipelineNotifyCtx->cond.notify_one(); + if (!mWaitForFinalInputs) { + notifyForeignChainFinished(); } if (buildNativeGPU) { @@ -963,6 +1017,9 @@ int GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (synchronizeOutput) { SynchronizeStream(mRec->NStreams() - 1); } + if (synchronizeCalibUpdate) { + SynchronizeStream(0); + } if (buildNativeHost && GetProcessingSettings().debugLevel >= 4) { for (unsigned int i = 0; i < NSLICES; i++) { for (unsigned int j = 0; j < GPUCA_ROW_COUNT; j++) { diff --git a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx index 5c42cd2f3bdf3..6cc0f9f12ecf3 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx @@ -249,6 +249,7 @@ void GPUChainTracking::PrintOutputStat() void GPUChainTracking::SanityCheck() { +#ifdef GPUCA_HAVE_O2HEADERS size_t nErrors = 0; for (unsigned int i = 0; i < mIOPtrs.nOutputTracksTPCO2; i++) { @@ -289,4 +290,5 @@ void GPUChainTracking::SanityCheck() } else { GPUError("Sanity check found %lu errors", nErrors); } +#endif } diff --git a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx index 443186f5d3305..c1068c15ae740 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingIO.cxx @@ -33,6 +33,7 @@ #include "GPUMemorySizeScalers.h" #include "GPUTrackingInputProvider.h" #include "TPCZSLinkMapping.h" +#include "GPUTriggerOutputs.h" #ifdef GPUCA_HAVE_O2HEADERS #include "SimulationDataFormat/MCCompLabel.h" diff --git a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx index 9aa3a50cdee55..37d6bc35a0810 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx @@ -251,7 +251,7 @@ int GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) } else if (GetProcessingSettings().keepDisplayMemory || GetProcessingSettings().createO2Output <= 1 || mFractionalQAEnabled) { if (!(GetProcessingSettings().keepDisplayMemory || GetProcessingSettings().createO2Output <= 1)) { size_t size = mRec->Res(Merger.MemoryResOutput()).Size() + GPUCA_MEMALIGN; - void* buffer = mQA->AllocateScratchBuffer(size); + void* buffer = GetQA()->AllocateScratchBuffer(size); void* bufferEnd = Merger.SetPointersOutput(buffer); if ((size_t)((char*)bufferEnd - (char*)buffer) > size) { throw std::runtime_error("QA Scratch buffer exceeded"); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx index c976af9738b59..4d023cc8bca22 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx @@ -482,13 +482,12 @@ void GPUChainTracking::WriteOutput(int iSlice, int threadId) GPUInfo("Running WriteOutput for slice %d on thread %d\n", iSlice, threadId); } if (GetProcessingSettings().nDeviceHelperThreads) { - while (mLockAtomic.test_and_set(std::memory_order_acquire)) { - ; + while (mLockAtomicOutputBuffer.test_and_set(std::memory_order_acquire)) { } } processors()->tpcTrackers[iSlice].WriteOutputPrepare(); if (GetProcessingSettings().nDeviceHelperThreads) { - mLockAtomic.clear(); + mLockAtomicOutputBuffer.clear(); } processors()->tpcTrackers[iSlice].WriteOutput(); if (GetProcessingSettings().debugLevel >= 5) { diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.cxx b/GPU/GPUTracking/Interface/GPUO2Interface.cxx index 62d84bc2d9058..2dd001725ee7f 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.cxx +++ b/GPU/GPUTracking/Interface/GPUO2Interface.cxx @@ -26,88 +26,130 @@ #include "CalibdEdxContainer.h" #include #include +#include +#include +#include using namespace o2::gpu; #include "DataFormatsTPC/ClusterNative.h" -GPUO2Interface::GPUO2Interface() = default; +namespace o2::gpu +{ +struct GPUO2Interface_processingContext { + std::unique_ptr mRec; + GPUChainTracking* mChain = nullptr; + std::unique_ptr mOutputRegions; +}; + +struct GPUO2Interface_Internals { + std::unique_ptr pipelineThread; +}; +} // namespace o2::gpu + +GPUO2Interface::GPUO2Interface() : mInternals(new GPUO2Interface_Internals){}; GPUO2Interface::~GPUO2Interface() { Deinitialize(); } int GPUO2Interface::Initialize(const GPUO2InterfaceConfiguration& config) { - if (mInitialized) { + if (mNContexts) { return (1); } mConfig.reset(new GPUO2InterfaceConfiguration(config)); - mContinuous = mConfig->configGRP.continuousMaxTimeBin != 0; - mRec.reset(GPUReconstruction::CreateInstance(mConfig->configDeviceBackend)); - if (mRec == nullptr) { - GPUError("Error obtaining instance of GPUReconstruction"); - return 1; - } - mChain = mRec->AddChain(mConfig->configInterface.maxTPCHits, mConfig->configInterface.maxTRDTracklets); - mChain->mConfigDisplay = &mConfig->configDisplay; - mChain->mConfigQA = &mConfig->configQA; + mNContexts = mConfig->configProcessing.doublePipeline ? 2 : 1; + mCtx.reset(new GPUO2Interface_processingContext[mNContexts]); if (mConfig->configWorkflow.inputs.isSet(GPUDataTypes::InOutType::TPCRaw)) { mConfig->configGRP.needsClusterer = 1; } if (mConfig->configWorkflow.inputs.isSet(GPUDataTypes::InOutType::TPCCompressedClusters)) { mConfig->configGRP.doCompClusterDecode = 1; } - mRec->SetSettings(&mConfig->configGRP, &mConfig->configReconstruction, &mConfig->configProcessing, &mConfig->configWorkflow); - mChain->SetCalibObjects(mConfig->configCalib); - - if (mConfig->configWorkflow.steps.isSet(GPUDataTypes::RecoStep::ITSTracking)) { - mChainITS = mRec->AddChain(); + for (unsigned int i = 0; i < mNContexts; i++) { + if (i) { + mConfig->configDeviceBackend.master = mCtx[0].mRec.get(); + } + mCtx[i].mRec.reset(GPUReconstruction::CreateInstance(mConfig->configDeviceBackend)); + mConfig->configDeviceBackend.master = nullptr; + if (mCtx[i].mRec == nullptr) { + GPUError("Error obtaining instance of GPUReconstruction"); + mNContexts = 0; + mCtx.reset(nullptr); + return 1; + } } + for (unsigned int i = 0; i < mNContexts; i++) { + mCtx[i].mChain = mCtx[i].mRec->AddChain(mConfig->configInterface.maxTPCHits, mConfig->configInterface.maxTRDTracklets); + if (i) { + mCtx[i].mChain->SetQAFromForeignChain(mCtx[0].mChain); + } + mCtx[i].mChain->mConfigDisplay = &mConfig->configDisplay; + mCtx[i].mChain->mConfigQA = &mConfig->configQA; + mCtx[i].mRec->SetSettings(&mConfig->configGRP, &mConfig->configReconstruction, &mConfig->configProcessing, &mConfig->configWorkflow); + mCtx[i].mChain->SetCalibObjects(mConfig->configCalib); - mOutputRegions.reset(new GPUTrackingOutputs); - if (mConfig->configInterface.outputToExternalBuffers) { - for (unsigned int i = 0; i < mOutputRegions->count(); i++) { - mChain->SetSubOutputControl(i, &mOutputRegions->asArray()[i]); + if (i == 0 && mConfig->configWorkflow.steps.isSet(GPUDataTypes::RecoStep::ITSTracking)) { + mChainITS = mCtx[i].mRec->AddChain(); } - GPUOutputControl dummy; - dummy.set([](size_t size) -> void* {throw std::runtime_error("invalid output memory request, no common output buffer set"); return nullptr; }); - mRec->SetOutputControl(dummy); - } - if (mRec->Init()) { - return (1); + mCtx[i].mOutputRegions.reset(new GPUTrackingOutputs); + if (mConfig->configInterface.outputToExternalBuffers) { + for (unsigned int j = 0; j < mCtx[i].mOutputRegions->count(); j++) { + mCtx[i].mChain->SetSubOutputControl(j, &mCtx[i].mOutputRegions->asArray()[j]); + } + GPUOutputControl dummy; + dummy.set([](size_t size) -> void* {throw std::runtime_error("invalid output memory request, no common output buffer set"); return nullptr; }); + mCtx[i].mRec->SetOutputControl(dummy); + } + } + for (unsigned int i = 0; i < mNContexts; i++) { + if (i == 0 && mCtx[i].mRec->Init()) { + mNContexts = 0; + mCtx.reset(nullptr); + return (1); + } + if (!mCtx[i].mRec->IsGPU() && mCtx[i].mRec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { + mCtx[i].mRec->MemoryScalers()->factor *= 2; + } } - if (!mRec->IsGPU() && mRec->GetProcessingSettings().memoryAllocationStrategy == GPUMemoryResource::ALLOCATION_INDIVIDUAL) { - mRec->MemoryScalers()->factor *= 2; + if (mConfig->configProcessing.doublePipeline) { + mInternals->pipelineThread.reset(new std::thread([this]() { mCtx[0].mRec->RunPipelineWorker(); })); } - mInitialized = true; return (0); } void GPUO2Interface::Deinitialize() { - if (mInitialized) { - mRec->Finalize(); - mRec.reset(); + if (mNContexts) { + if (mConfig->configProcessing.doublePipeline) { + mCtx[0].mRec->TerminatePipelineWorker(); + mInternals->pipelineThread->join(); + } + for (unsigned int i = 0; i < mNContexts; i++) { + mCtx[i].mRec->Finalize(); + } + mCtx[0].mRec->Exit(); + for (int i = mNContexts - 1; i >= 0; i--) { + mCtx[i].mRec.reset(); + } } - mInitialized = false; + mNContexts = 0; } void GPUO2Interface::DumpEvent(int nEvent, GPUTrackingInOutPointers* data) { - if (mConfig->configProcessing.doublePipeline) { - throw std::runtime_error("Cannot dump events in double pipeline mode"); - } - mChain->ClearIOPointers(); - mChain->mIOPtrs = *data; + mCtx[0].mChain->ClearIOPointers(); + mCtx[0].mChain->mIOPtrs = *data; char fname[1024]; snprintf(fname, 1024, "event.%d.dump", nEvent); - mChain->DumpData(fname); + mCtx[0].mChain->DumpData(fname); if (nEvent == 0) { #ifdef GPUCA_BUILD_QA if (mConfig->configProcessing.runMC) { - mChain->ForceInitQA(); + mCtx[0].mChain->ForceInitQA(); snprintf(fname, 1024, "mc.%d.dump", nEvent); - mChain->GetQA()->DumpO2MCData(fname); + mCtx[0].mChain->GetQA()->UpdateChain(mCtx[0].mChain); + mCtx[0].mChain->GetQA()->DumpO2MCData(fname); } #endif } @@ -115,64 +157,82 @@ void GPUO2Interface::DumpEvent(int nEvent, GPUTrackingInOutPointers* data) void GPUO2Interface::DumpSettings() { - if (mConfig->configProcessing.doublePipeline) { - throw std::runtime_error("Cannot dump events in double pipeline mode"); - } - mChain->DoQueuedCalibUpdates(-1); - mRec->DumpSettings(); + mCtx[0].mChain->DoQueuedUpdates(-1); + mCtx[0].mRec->DumpSettings(); } -int GPUO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs) +int GPUO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs, unsigned int iThread, GPUInterfaceInputUpdate* inputUpdateCallback) { - if (!mInitialized) { + if (mNContexts <= iThread) { return (1); } - mChain->mIOPtrs = *data; - if (mConfig->configInterface.outputToExternalBuffers) { - for (unsigned int i = 0; i < mOutputRegions->count(); i++) { - if (outputs->asArray()[i].allocator) { - mOutputRegions->asArray()[i].set(outputs->asArray()[i].allocator); - } else if (outputs->asArray()[i].ptrBase) { - mOutputRegions->asArray()[i].set(outputs->asArray()[i].ptrBase, outputs->asArray()[i].size); - } else { - mOutputRegions->asArray()[i].reset(); + mCtx[iThread].mChain->mIOPtrs = *data; + + auto setOutputs = [this, iThread](GPUInterfaceOutputs* outputs) { + if (mConfig->configInterface.outputToExternalBuffers) { + for (unsigned int i = 0; i < mCtx[iThread].mOutputRegions->count(); i++) { + if (outputs->asArray()[i].allocator) { + mCtx[iThread].mOutputRegions->asArray()[i].set(outputs->asArray()[i].allocator); + } else if (outputs->asArray()[i].ptrBase) { + mCtx[iThread].mOutputRegions->asArray()[i].set(outputs->asArray()[i].ptrBase, outputs->asArray()[i].size); + } else { + mCtx[iThread].mOutputRegions->asArray()[i].reset(); + } } } + }; + + auto inputWaitCallback = [this, iThread, inputUpdateCallback, &data, &outputs, &setOutputs]() { + GPUTrackingInOutPointers* updatedData; + GPUInterfaceOutputs* updatedOutputs; + if (inputUpdateCallback->callback) { + inputUpdateCallback->callback(updatedData, updatedOutputs); + mCtx[iThread].mChain->mIOPtrs = *updatedData; + outputs = updatedOutputs; + data = updatedData; + setOutputs(outputs); + } + if (inputUpdateCallback->notifyCallback) { + inputUpdateCallback->notifyCallback(); + } + }; + + if (inputUpdateCallback) { + mCtx[iThread].mChain->SetFinalInputCallback(inputWaitCallback); + } else { + mCtx[iThread].mChain->SetFinalInputCallback(nullptr); + } + if (!inputUpdateCallback || !inputUpdateCallback->callback) { + setOutputs(outputs); } - int retVal = mRec->RunChains(); + int retVal = mCtx[iThread].mRec->RunChains(); if (retVal == 2) { retVal = 0; // 2 signals end of event display, ignore } - if (mConfig->configQA.shipToQC && mChain->QARanForTF()) { - outputs->qa.hist1 = &mChain->GetQA()->getHistograms1D(); - outputs->qa.hist2 = &mChain->GetQA()->getHistograms2D(); - outputs->qa.hist3 = &mChain->GetQA()->getHistograms1Dd(); - outputs->qa.hist4 = &mChain->GetQA()->getGraphs(); + if (mConfig->configQA.shipToQC && mCtx[iThread].mChain->QARanForTF()) { + outputs->qa.hist1 = &mCtx[iThread].mChain->GetQA()->getHistograms1D(); + outputs->qa.hist2 = &mCtx[iThread].mChain->GetQA()->getHistograms2D(); + outputs->qa.hist3 = &mCtx[iThread].mChain->GetQA()->getHistograms1Dd(); + outputs->qa.hist4 = &mCtx[iThread].mChain->GetQA()->getGraphs(); outputs->qa.newQAHistsCreated = true; } - *data = mChain->mIOPtrs; + *data = mCtx[iThread].mChain->mIOPtrs; return retVal; } -void GPUO2Interface::Clear(bool clearOutputs) { mRec->ClearAllocatedMemory(clearOutputs); } - -void GPUO2Interface::GetClusterErrors2(int row, float z, float sinPhi, float DzDs, short clusterState, float& ErrY2, float& ErrZ2) const -{ - mRec->GetParam().GetClusterErrors2(row, z, sinPhi, DzDs, ErrY2, ErrZ2); - mRec->GetParam().UpdateClusterError2ByState(clusterState, ErrY2, ErrZ2); -} +void GPUO2Interface::Clear(bool clearOutputs, unsigned int iThread) { mCtx[iThread].mRec->ClearAllocatedMemory(clearOutputs); } int GPUO2Interface::registerMemoryForGPU(const void* ptr, size_t size) { - return mRec->registerMemoryForGPU(ptr, size); + return mCtx[0].mRec->registerMemoryForGPU(ptr, size); } int GPUO2Interface::unregisterMemoryForGPU(const void* ptr) { - return mRec->unregisterMemoryForGPU(ptr); + return mCtx[0].mRec->unregisterMemoryForGPU(ptr); } std::unique_ptr GPUO2Interface::getPadGainCalibDefault() @@ -190,15 +250,19 @@ std::unique_ptr GPUO2Interface::getCalibdEdxContain return std::make_unique(); } -int GPUO2Interface::UpdateCalibration(const GPUCalibObjectsConst& newCalib, const GPUNewCalibValues& newVals) +int GPUO2Interface::UpdateCalibration(const GPUCalibObjectsConst& newCalib, const GPUNewCalibValues& newVals, unsigned int iThread) { - mChain->SetUpdateCalibObjects(newCalib, newVals); + for (unsigned int i = 0; i < mNContexts; i++) { + mCtx[i].mChain->SetUpdateCalibObjects(newCalib, newVals); + } return 0; } void GPUO2Interface::setErrorCodeOutput(std::vector>* v) { - mRec->setErrorCodeOutput(v); + for (unsigned int i = 0; i < mNContexts; i++) { + mCtx[i].mRec->setErrorCodeOutput(v); + } } void GPUO2Interface::GetITSTraits(o2::its::TrackerTraits*& trackerTraits, o2::its::VertexerTraits*& vertexerTraits, o2::its::TimeFrame*& timeFrame) diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.h b/GPU/GPUTracking/Interface/GPUO2Interface.h index 8a4e10033641d..d227b8c7a92e6 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.h +++ b/GPU/GPUTracking/Interface/GPUO2Interface.h @@ -31,6 +31,7 @@ #include #include "GPUCommonDef.h" #include "GPUDataTypes.h" + namespace o2::tpc { struct ClusterNativeAccess; @@ -53,10 +54,14 @@ class GPUChainTracking; class GPUChainITS; struct GPUO2InterfaceConfiguration; struct GPUInterfaceOutputs; +struct GPUInterfaceInputUpdate; struct GPUTrackingOutputs; struct GPUConstantMem; struct GPUNewCalibValues; +struct GPUO2Interface_processingContext; +struct GPUO2Interface_Internals; + class GPUO2Interface { public: @@ -66,18 +71,15 @@ class GPUO2Interface int Initialize(const GPUO2InterfaceConfiguration& config); void Deinitialize(); - int RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs = nullptr); - void Clear(bool clearOutputs); + int RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs = nullptr, unsigned int iThread = 0, GPUInterfaceInputUpdate* inputUpdateCallback = nullptr); + void Clear(bool clearOutputs, unsigned int iThread = 0); void DumpEvent(int nEvent, GPUTrackingInOutPointers* data); void DumpSettings(); void GetITSTraits(o2::its::TrackerTraits*& trackerTraits, o2::its::VertexerTraits*& vertexerTraits, o2::its::TimeFrame*& timeFrame); // Updates all calibration objects that are != nullptr in newCalib - int UpdateCalibration(const GPUCalibObjectsConst& newCalib, const GPUNewCalibValues& newVals); - - bool GetParamContinuous() { return (mContinuous); } - void GetClusterErrors2(int row, float z, float sinPhi, float DzDs, short clusterState, float& ErrY2, float& ErrZ2) const; + int UpdateCalibration(const GPUCalibObjectsConst& newCalib, const GPUNewCalibValues& newVals, unsigned int iThread = 0); static std::unique_ptr getPadGainCalibDefault(); static std::unique_ptr getPadGainCalib(const o2::tpc::CalDet& in); @@ -94,14 +96,14 @@ class GPUO2Interface GPUO2Interface(const GPUO2Interface&); GPUO2Interface& operator=(const GPUO2Interface&); - bool mInitialized = false; bool mContinuous = false; - std::unique_ptr mRec; //! - GPUChainTracking* mChain = nullptr; //! - GPUChainITS* mChainITS = nullptr; //! - std::unique_ptr mConfig; //! - std::unique_ptr mOutputRegions; //! + unsigned int mNContexts = 0; + std::unique_ptr mCtx; + + std::unique_ptr mConfig; + GPUChainITS* mChainITS = nullptr; + std::unique_ptr mInternals; }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx index e4063ff922730..da625dd704ab6 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx @@ -26,6 +26,7 @@ using namespace o2::gpu; #define AddOptionSet(name, type, value, optname, optnameshort, help, ...) #define AddOptionVec(name, type, optname, optnameshort, help, ...) #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) +#define AddOptionArrayRTC(...) AddOptionArray(__VA_ARGS__) #define AddSubConfig(name, instance) #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) O2ParamImpl(GPUCA_M_CAT(GPUConfigurableParam, name)) #define BeginHiddenConfig(...) @@ -43,6 +44,7 @@ using namespace o2::gpu; #undef AddOptionSet #undef AddOptionVec #undef AddOptionArray +#undef AddOptionArrayRTC #undef AddSubConfig #undef BeginSubConfig #undef BeginHiddenConfig @@ -65,6 +67,7 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam_internal() for (int i = 0; i < count; i++) { \ dst.name[i] = src.name[i]; \ } +#define AddOptionArrayRTC(...) AddOptionArray(__VA_ARGS__) #define AddSubConfig(name, instance) dst.instance = instance; #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) \ name instance; \ @@ -86,6 +89,7 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam_internal() #undef AddOptionSet #undef AddOptionVec #undef AddOptionArray +#undef AddOptionArrayRTC #undef AddSubConfig #undef BeginSubConfig #undef BeginHiddenConfig diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h index a296567ecb269..bb92eca425336 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.h @@ -49,7 +49,7 @@ #define AddVariableRTC(...) AddVariable(__VA_ARGS__) #define AddOptionSet(name, type, value, optname, optnameshort, help, ...) #define AddOptionVec(name, type, optname, optnameshort, help, ...) -#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {default}; +#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {GPUCA_M_STRIP(default)}; #define AddSubConfig(name, instance) #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) \ struct GPUCA_M_CAT(GPUConfigurableParam, name) : public o2::conf::ConfigurableParamHelper { \ @@ -62,6 +62,7 @@ #define AddHelp(...) #define AddShortcut(...) #define AddOptionRTC(...) AddOption(__VA_ARGS__) +#define AddOptionArrayRTC(...) AddOptionArray(__VA_ARGS__) #include "GPUSettingsList.h" #undef BeginNamespace #undef EndNamespace @@ -72,6 +73,7 @@ #undef AddOptionSet #undef AddOptionVec #undef AddOptionArray +#undef AddOptionArrayRTC #undef AddSubConfig #undef BeginSubConfig #undef BeginHiddenConfig diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h index 981ac4797355f..2d062c1422c40 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h @@ -66,6 +66,11 @@ struct GPUInterfaceOutputs : public GPUTrackingOutputs { GPUInterfaceQAOutputs qa; }; +struct GPUInterfaceInputUpdate { + std::function callback; // Callback which provides final data ptrs / outputRegions after Clusterization stage + std::function notifyCallback; // Callback called to notify that Clusterization state has finished without update +}; + // Full configuration structure with all available settings of GPU... struct GPUO2InterfaceConfiguration { GPUO2InterfaceConfiguration() = default; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMBorderTrack.h b/GPU/GPUTracking/Merger/GPUTPCGMBorderTrack.h index 285166006db2e..7c7ab6a17d421 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMBorderTrack.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMBorderTrack.h @@ -124,6 +124,8 @@ class GPUTPCGMBorderTrack float mZOffsetLinear; // Z Offset, in case of T offset scaled linearly to Z with nominal vDrift. Used only for matching / merging float mC[5]; float mD[2]; + + ClassDefNV(GPUTPCGMBorderTrack, 1); }; } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 5be266e3e19b4..41214b836fb76 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -507,7 +507,7 @@ GPUd() int GPUTPCGMMerger::RefitSliceTrack(GPUTPCGMSliceTrack& sliceTrack, const return way == 0; } trk.ConstrainSinPhi(); - if (prop.Update(y, z, row, Param(), flags & GPUTPCGMMergedTrackHit::clustererAndSharedFlags, 0, nullptr, false)) { + if (prop.Update(y, z, row, Param(), flags & GPUTPCGMMergedTrackHit::clustererAndSharedFlags, 0, nullptr, false, slice > 18)) { return way == 0; } trk.ConstrainSinPhi(); @@ -684,7 +684,7 @@ GPUd() void GPUTPCGMMerger::MakeBorderTracks(int nBlocks, int nThreads, int iBlo continue; } if (useOrigTrackParam) { // TODO: Check how far this makes sense with slice track refit - if (CAMath::Abs(track->QPt()) * Param().qptB5Scaler < GPUCA_MERGER_LOOPER_QPTB5_LIMIT) { + if (CAMath::Abs(track->QPt()) * Param().qptB5Scaler < Param().rec.tpc.mergerLooperQPtB5Limit) { continue; } const GPUTPCGMSliceTrack* trackMin = track; @@ -702,7 +702,7 @@ GPUd() void GPUTPCGMMerger::MakeBorderTracks(int nBlocks, int nThreads, int iBlo trackTmp.Set(this, trackMin->OrigTrack(), trackMin->Alpha(), trackMin->Slice()); } } else { - if (CAMath::Abs(track->QPt()) * Param().qptB5Scaler < GPUCA_MERGER_HORIZONTAL_DOUBLE_QPTB5_LIMIT) { + if (CAMath::Abs(track->QPt()) * Param().qptB5Scaler < Param().rec.tpc.mergerLooperSecondHorizontalQPtB5Limit) { if (iBorder == 0 && track->NextNeighbour() >= 0) { continue; } @@ -841,16 +841,17 @@ template <> GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int nBlocks, int nThreads, int iBlock, int iThread, int iSlice1, GPUTPCGMBorderTrack* B1, int N1, int iSlice2, GPUTPCGMBorderTrack* B2, int N2, int mergeMode) { // int statAll = 0, statMerged = 0; - float factor2ys = 1.5; // 1.5;//SG!!! - float factor2zt = 1.5; // 1.5;//SG!!! - float factor2k = 2.0; // 2.2; + float factor2ys = Param().rec.tpc.trackMergerFactor2YS; + float factor2zt = Param().rec.tpc.trackMergerFactor2ZT; + float factor2k = Param().rec.tpc.trackMergerFactor2K; + float factor2General = Param().rec.tpc.trackMergerFactor2General; - factor2k = 3.5 * 3.5 * factor2k * factor2k; - factor2ys = 3.5 * 3.5 * factor2ys * factor2ys; - factor2zt = 3.5 * 3.5 * factor2zt * factor2zt; + factor2k = factor2General * factor2k; + factor2ys = factor2General * factor2ys; + factor2zt = factor2General * factor2zt; - int minNPartHits = 10; // SG!!! - int minNTotalHits = 20; + int minNPartHits = Param().rec.tpc.trackMergerMinPartHits; + int minNTotalHits = Param().rec.tpc.trackMergerMinTotalHits; bool sameSlice = (iSlice1 == iSlice2); @@ -905,6 +906,9 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int nBlocks, int nThreads, int CADEBUG2(continue, printf("!CE SinPhi/Tgl\n")); // Crude cut to avoid totally wrong matches, TODO: check cut } } + + GPUCA_DEBUG_STREAMER_CHECK(float weight = b1.Par()[4] * b1.Par()[4]; if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamMergeBorderTracksAll, b1.TrackID(), weight)) { MergedTrackStreamer(b1, b2, "merge_all_tracks", iSlice1, iSlice2, mergeMode, weight, o2::utils::DebugStreamer::getSamplingFrequency(o2::utils::StreamFlags::streamMergeBorderTracksAll)); }); + if (!b1.CheckChi2Y(b2, factor2ys)) { CADEBUG2(continue, printf("!Y\n")); } @@ -937,6 +941,8 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int nBlocks, int nThreads, int if (iBest2 < 0) { continue; } + GPUCA_DEBUG_STREAMER_CHECK(float weight = b1.Par()[4] * b1.Par()[4]; if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamMergeBorderTracksBest, b1.TrackID(), weight)) { MergedTrackStreamer(b1, MergedTrackStreamerFindBorderTrack(B2, N2, iBest2), "merge_best_track", iSlice1, iSlice2, mergeMode, weight, o2::utils::DebugStreamer::getSamplingFrequency(o2::utils::StreamFlags::streamMergeBorderTracksBest)); }); + // statMerged++; CADEBUG(GPUInfo("Found match %d %d", b1.TrackID(), iBest2)); @@ -1314,11 +1320,9 @@ GPUd() void GPUTPCGMMerger::MergeCEFill(const GPUTPCGMSliceTrack* track, const G return; } -#ifdef GPUCA_MERGER_CE_ROWLIMIT - if (CAMath::Abs(track->QPt()) * Param().qptB5Scaler < 0.3 && (cls.row < GPUCA_MERGER_CE_ROWLIMIT || cls.row >= GPUCA_ROW_COUNT - GPUCA_MERGER_CE_ROWLIMIT)) { + if (Param().rec.tpc.mergerCERowLimit > 0 && CAMath::Abs(track->QPt()) * Param().qptB5Scaler < 0.3 && (cls.row < Param().rec.tpc.mergerCERowLimit || cls.row >= GPUCA_ROW_COUNT - Param().rec.tpc.mergerCERowLimit)) { return; } -#endif float z = 0; if (Param().par.earlyTpcTransform) { diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index ac2312b2bd71f..e4e0d796a33d3 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h @@ -193,6 +193,11 @@ class GPUTPCGMMerger : public GPUProcessor void DumpFitPrepare(std::ostream& out); void DumpRefit(std::ostream& out); void DumpFinal(std::ostream& out); + + template + void MergedTrackStreamerInternal(const GPUTPCGMBorderTrack& b1, const GPUTPCGMBorderTrack& b2, const char* name, int slice1, int slice2, int mergeMode, float weight, float frac); + void MergedTrackStreamer(const GPUTPCGMBorderTrack& b1, const GPUTPCGMBorderTrack& b2, const char* name, int slice1, int slice2, int mergeMode, float weight, float frac); + const GPUTPCGMBorderTrack& MergedTrackStreamerFindBorderTrack(const GPUTPCGMBorderTrack* tracks, int N, int trackId); #endif private: @@ -269,7 +274,7 @@ class GPUTPCGMMerger : public GPUProcessor unsigned int* mTrackSort; tmpSort* mTrackSortO2; GPUAtomic(unsigned int) * mSharedCount; // Must be unsigned int unfortunately for atomic support - GPUTPCGMBorderTrack* mBorderMemory; // memory for border tracks + GPUTPCGMBorderTrack* mBorderMemory; // memory for border tracks GPUTPCGMBorderTrack* mBorder[2 * NSLICES]; gputpcgmmergertypes::GPUTPCGMBorderRange* mBorderRangeMemory; // memory for border tracks gputpcgmmergertypes::GPUTPCGMBorderRange* mBorderRange[NSLICES]; // memory for border tracks diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx index dd3f8452e4f52..490d49f9d2a17 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx @@ -32,6 +32,7 @@ #include "GPUTPCGMSliceTrack.h" #include "GPUTPCGMBorderTrack.h" #include "GPUReconstruction.h" +#include "GPUDebugStreamer.h" using namespace GPUCA_NAMESPACE::gpu; using namespace gputpcgmmergertypes; @@ -180,3 +181,55 @@ void GPUTPCGMMerger::DumpFinal(std::ostream& out) out << " Cluster attachment " << i << ": " << getTrackOrderReverse(mClusterAttachment[i] & attachTrackMask) << " / " << (mClusterAttachment[i] & attachFlagMask) << "\n"; } } + +template +inline void GPUTPCGMMerger::MergedTrackStreamerInternal(const GPUTPCGMBorderTrack& b1, const GPUTPCGMBorderTrack& b2, const char* name, int slice1, int slice2, int mergeMode, float weight, float frac) +{ +#ifdef DEBUG_STREAMER + std::vector hits1(152), hits2(152); + for (int i = 0; i < 152; i++) { + hits1[i] = hits2[i] = -1; + } + const GPUTPCTracker& tracker1 = GetConstantMem()->tpcTrackers[slice1]; + const GPUTPCGMSliceTrack& sliceTrack1 = mSliceTrackInfos[b1.TrackID()]; + const GPUTPCTrack& inTrack1 = *sliceTrack1.OrigTrack(); + for (int i = 0; i < inTrack1.NHits(); i++) { + const GPUTPCHitId& ic1 = tracker1.TrackHits()[inTrack1.FirstHitID() + i]; + int clusterIndex = tracker1.Data().ClusterDataIndex(tracker1.Data().Row(ic1.RowIndex()), ic1.HitIndex()); + hits1[ic1.RowIndex()] = clusterIndex; + } + const GPUTPCTracker& tracker2 = GetConstantMem()->tpcTrackers[slice2]; + const GPUTPCGMSliceTrack& sliceTrack2 = mSliceTrackInfos[b2.TrackID()]; + const GPUTPCTrack& inTrack2 = *sliceTrack2.OrigTrack(); + for (int i = 0; i < inTrack2.NHits(); i++) { + const GPUTPCHitId& ic2 = tracker2.TrackHits()[inTrack2.FirstHitID() + i]; + int clusterIndex = tracker2.Data().ClusterDataIndex(tracker2.Data().Row(ic2.RowIndex()), ic2.HitIndex()); + hits2[ic2.RowIndex()] = clusterIndex; + } + + std::string debugname = std::string("debug_") + name; + std::string treename = std::string("tree_") + name; + o2::utils::DebugStreamer::instance()->getStreamer(debugname.c_str(), "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName(treename.c_str()).data() << "slice1=" << slice1 << "slice2=" << slice2 << "b1=" << b1 << "b2=" << b2 << "clusters1=" << hits1 << "clusters2=" << hits2 << "sliceTrack1=" << sliceTrack1 << "sliceTrack2=" << sliceTrack2 << "mergeMode=" << mergeMode << "weight=" << weight << "fraction=" << frac << "\n"; +#endif +} + +void GPUTPCGMMerger::MergedTrackStreamer(const GPUTPCGMBorderTrack& b1, const GPUTPCGMBorderTrack& b2, const char* name, int slice1, int slice2, int mergeMode, float weight, float frac) +{ +#ifdef DEBUG_STREAMER + if (mergeMode == 0) { + MergedTrackStreamerInternal<0>(b1, b2, name, slice1, slice2, mergeMode, weight, frac); + } else if (mergeMode >= 1 && mergeMode <= 0) { + // MergedTrackStreamerInternal<1>(b1, b2, name, slice1, slice2, mergeMode, weight, frac); Not yet working + } +#endif +} + +const GPUTPCGMBorderTrack& GPUTPCGMMerger::MergedTrackStreamerFindBorderTrack(const GPUTPCGMBorderTrack* tracks, int N, int trackId) +{ + for (int i = 0; i < N; i++) { + if (tracks[i].TrackID() == trackId) { + return tracks[i]; + } + } + throw std::runtime_error("didn't find border track"); +} diff --git a/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx b/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx index 7fbbeafed3cf6..34ef99924c11f 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx @@ -143,7 +143,7 @@ GPUdii() void GPUTPCGMO2Output::Thread(int nBlocks, in outerPar.C[6], outerPar.C[7], outerPar.C[8], outerPar.C[9], outerPar.C[10], outerPar.C[11], outerPar.C[12], outerPar.C[13], outerPar.C[14]})); - if (merger.Param().par.dodEdx) { + if (merger.Param().par.dodEdx && merger.Param().rec.tpc.enablePID) { PIDResponse pidResponse{}; const auto pid = pidResponse.getMostProbablePID(oTrack, merger.Param().rec.tpc.PID_EKrangeMin, merger.Param().rec.tpc.PID_EKrangeMax, merger.Param().rec.tpc.PID_EPrangeMin, merger.Param().rec.tpc.PID_EPrangeMax); oTrack.setPID(pid); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx index 765a5ef0fe13b..5b2b86c49842c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.cxx @@ -597,21 +597,27 @@ GPUd() int GPUTPCGMPropagator::GetPropagatedYZ(float x, float& GPUrestrict() pro return 0; } -GPUd() void GPUTPCGMPropagator::GetErr2(float& GPUrestrict() err2Y, float& GPUrestrict() err2Z, const GPUParam& GPUrestrict() param, float posZ, int iRow, short clusterState) const +GPUd() void GPUTPCGMPropagator::GetErr2(float& GPUrestrict() err2Y, float& GPUrestrict() err2Z, const GPUParam& GPUrestrict() param, float posZ, int iRow, short clusterState, bool sideC) const { - if (!mSeedingErrors) { - param.GetClusterErrors2(iRow, posZ, mT0.GetSinPhi(), mT0.DzDs(), err2Y, err2Z); - } else { +#ifndef GPUCA_TPC_GEOMETRY_O2 + if (mSeedingErrors) { param.GetClusterErrorsSeeding2(iRow, posZ, mT0.GetSinPhi(), mT0.DzDs(), err2Y, err2Z); + } else +#endif + { + param.GetClusterErrors2(iRow, posZ, mT0.GetSinPhi(), mT0.DzDs(), err2Y, err2Z); } param.UpdateClusterError2ByState(clusterState, err2Y, err2Z); + float statErr2 = param.GetSystematicClusterErrorIFC2(mT->GetX(), posZ, sideC); + err2Y += statErr2; + err2Z += statErr2; mStatErrors.GetOfflineStatisticalErrors(err2Y, err2Z, mT0.SinPhi(), mT0.DzDs(), clusterState); } -GPUd() float GPUTPCGMPropagator::PredictChi2(float posY, float posZ, int iRow, const GPUParam& GPUrestrict() param, short clusterState) const +GPUd() float GPUTPCGMPropagator::PredictChi2(float posY, float posZ, int iRow, const GPUParam& GPUrestrict() param, short clusterState, bool sideC) const { float err2Y, err2Z; - GetErr2(err2Y, err2Z, param, posZ, iRow, clusterState); + GetErr2(err2Y, err2Z, param, posZ, iRow, clusterState, sideC); return PredictChi2(posY, posZ, err2Y, err2Z); } @@ -642,10 +648,10 @@ GPUd() float GPUTPCGMPropagator::PredictChi2(float posY, float posZ, float err2Y } } -GPUd() int GPUTPCGMPropagator::Update(float posY, float posZ, int iRow, const GPUParam& GPUrestrict() param, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, bool refit GPUCA_DEBUG_STREAMER_CHECK(, int iTrk)) +GPUd() int GPUTPCGMPropagator::Update(float posY, float posZ, int iRow, const GPUParam& GPUrestrict() param, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, bool refit, bool sideC GPUCA_DEBUG_STREAMER_CHECK(, int iTrk)) { float err2Y, err2Z; - GetErr2(err2Y, err2Z, param, posZ, iRow, clusterState); + GetErr2(err2Y, err2Z, param, posZ, iRow, clusterState, sideC); if (rejectChi2 >= 2) { if (rejectChi2 == 3 && inter->errorY < (GPUCA_MERGER_INTERPOLATION_ERROR_TYPE)0) { @@ -782,7 +788,7 @@ GPUd() int GPUTPCGMPropagator::Update(float posY, float posZ, short clusterState } float dChi2 = chiY + chiZ; // GPUInfo("hits %d chi2 %f, new %f %f (dy %f dz %f)", N, mChi2, chiY, chiZ, z0, z1); - if (!mSeedingErrors && rejectChi2 == 1 && RejectCluster(chiY * param->rec.tpc.clusterRejectChi2TolleranceY, chiZ * param->rec.tpc.clusterRejectChi2TolleranceZ, clusterState)) { + if (rejectChi2 == 1 && RejectCluster(chiY * param->rec.tpc.clusterRejectChi2TolleranceY, chiZ * param->rec.tpc.clusterRejectChi2TolleranceZ, clusterState)) { return 2; } mT->Chi2() += dChi2; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h index 88c4644db9b99..77a4eb7cdc123 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMPropagator.h @@ -99,10 +99,10 @@ class GPUTPCGMPropagator GPUd() int PropagateToXAlphaBz(float posX, float posAlpha, bool inFlyDirection); - GPUd() int Update(float posY, float posZ, int iRow, const GPUParam& param, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, bool refit GPUCA_DEBUG_STREAMER_CHECK(, int iTrk = 0)); + GPUd() int Update(float posY, float posZ, int iRow, const GPUParam& param, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, bool refit, bool sideC GPUCA_DEBUG_STREAMER_CHECK(, int iTrk = 0)); GPUd() int Update(float posY, float posZ, short clusterState, bool rejectChi2, float err2Y, float err2Z, const GPUParam* param = nullptr); GPUd() int InterpolateReject(const GPUParam& param, float posY, float posZ, short clusterState, char rejectChi2, gputpcgmmergertypes::InterpolationErrorHit* inter, float err2Y, float err2Z); - GPUd() float PredictChi2(float posY, float posZ, int iRow, const GPUParam& param, short clusterState) const; + GPUd() float PredictChi2(float posY, float posZ, int iRow, const GPUParam& param, short clusterState, bool sideC) const; GPUd() float PredictChi2(float posY, float posZ, float err2Y, float err2Z) const; GPUd() int RejectCluster(float chiY, float chiZ, unsigned char clusterState) { @@ -128,7 +128,7 @@ class GPUTPCGMPropagator /// Bx,By,Bz in local coordinates rotated to Alpha GPUd() void GetBxByBz(float Alpha, float X, float Y, float Z, float B[3]) const; - GPUd() void GetErr2(float& err2Y, float& err2Z, const GPUParam& param, float posZ, int iRow, short clusterState) const; + GPUd() void GetErr2(float& err2Y, float& err2Z, const GPUParam& param, float posZ, int iRow, short clusterState, bool sideC) const; GPUd() float GetAlpha() const { return mAlpha; } GPUd() void SetAlpha(float v) { mAlpha = v; } @@ -168,22 +168,21 @@ class GPUTPCGMPropagator GPUd() float getGlobalY(float X, float Y) const; const GPUTPCGMPolynomialField* mField = nullptr; - FieldRegion mFieldRegion = TPC; - + const o2::base::MatLayerCylSet* mMatLUT = nullptr; GPUTPCGMTrackParam* mT = nullptr; float mAlpha = 0.f; // rotation angle of the track coordinate system float mCosAlpha = 1.f; // cos of the rotation angle float mSinAlpha = 0.f; // sin of the rotation angle + float mMaxSinPhi = GPUCA_MAX_SIN_PHI; GPUTPCGMPhysicalTrackModel mT0; MaterialCorrection mMaterial; + FieldRegion mFieldRegion = TPC; bool mSeedingErrors = 0; bool mFitInProjections = 1; // fit (Y,SinPhi,QPt) and (Z,DzDs) paramteres separatelly bool mPropagateBzOnly = 0; // Use Bz only in propagation bool mToyMCEvents = 0; // events are simulated with simple home-made simulation - float mMaxSinPhi = GPUCA_MAX_SIN_PHI; GPUTPCGMOfflineStatisticalErrors mStatErrors; - const o2::base::MatLayerCylSet* mMatLUT = nullptr; }; GPUdi() void GPUTPCGMPropagator::GetBxByBz(float Alpha, float X, float Y, float Z, float B[3]) const diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.h b/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.h index 30e4307a4e51f..ec67e1848aadb 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMSliceTrack.h @@ -119,12 +119,12 @@ class GPUTPCGMSliceTrack GPUd() bool TransportToX(GPUTPCGMMerger* merger, float x, float Bz, GPUTPCGMBorderTrack& b, float maxSinPhi, bool doCov = true) const; GPUd() bool TransportToXAlpha(GPUTPCGMMerger* merger, float x, float sinAlpha, float cosAlpha, float Bz, GPUTPCGMBorderTrack& b, float maxSinPhi) const; GPUd() void CopyBaseTrackCov(); - - private: struct sliceTrackParam { float mX, mY, mZ, mSinPhi, mDzDs, mQPt, mCosPhi, mSecPhi; // parameters float mC0, mC2, mC3, mC5, mC7, mC9, mC10, mC12, mC14; // covariances }; + + private: const GPUTPCTrack* mOrigTrack; // pointer to original slice track sliceTrackParam mParam; // Track parameters sliceTrackParam mParam2; // Parameters at other side @@ -138,6 +138,8 @@ class GPUTPCGMSliceTrack int mGlobalTrackIds[2]; // IDs of associated global tracks unsigned char mSlice; // slice of this track segment unsigned char mLeg; // Leg of this track segment + + ClassDefNV(GPUTPCGMSliceTrack, 1); }; } // namespace gpu } // namespace GPUCA_NAMESPACE diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx index 095316f6505a1..907e7e8cd7627 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx @@ -129,9 +129,6 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int iT if (crossCE) { lastSlice = clusters[ihit].slice; noFollowCircle2 = true; - if (mC[2] < 0.5f) { - mC[2] = 0.5f; - } } if (storeOuter == 2 && clusters[ihit].leg == clusters[maxN - 1].leg - 1) { @@ -145,7 +142,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int iT unsigned char clusterState = clusters[ihit].state; const float clAlpha = param.Alpha(clusters[ihit].slice); - if ((param.rec.tpc.trackFitRejectMode > 0 && nMissed >= param.rec.tpc.trackFitRejectMode) || nMissed2 >= GPUCA_MERGER_MAXN_MISSED_HARD || clusters[ihit].state & GPUTPCGMMergedTrackHit::flagReject) { + if ((param.rec.tpc.trackFitRejectMode > 0 && nMissed >= param.rec.tpc.trackFitRejectMode) || nMissed2 >= param.rec.tpc.trackFitMaxRowMissedHard || clusters[ihit].state & GPUTPCGMMergedTrackHit::flagReject) { CADEBUG(printf("\tSkipping hit, %d hits rejected, flag %X\n", nMissed, (int)clusters[ihit].state)); if (iWay + 2 >= nWays && !(clusters[ihit].state & GPUTPCGMMergedTrackHit::flagReject)) { clusters[ihit].state |= GPUTPCGMMergedTrackHit::flagRejectErr; @@ -197,7 +194,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int iT } } if (tryFollow) { - MirrorTo(prop, yy, zz, inFlyDirection, param, clusters[ihit].row, clusterState, false); + MirrorTo(prop, yy, zz, inFlyDirection, param, clusters[ihit].row, clusterState, false, clusters[ihit].slice > 18); lastUpdateX = mX; lastLeg = clusters[ihit].leg; lastSlice = clusters[ihit].slice; @@ -241,6 +238,18 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int iT CADEBUG(printf("\t%21sPropaga Alpha %8.3f , X %8.3f - Y %8.3f, Z %8.3f - QPt %7.2f (%7.2f), SP %5.2f (%5.2f) --- Res %8.3f %8.3f --- Cov sY %8.3f sZ %8.3f sSP %8.3f sPt %8.3f - YPt %8.3f - Err %d", "", prop.GetAlpha(), mX, mP[0], mP[1], mP[4], prop.GetQPt0(), mP[2], prop.GetSinPhi0(), mP[0] - yy, mP[1] - zz, sqrtf(mC[0]), sqrtf(mC[2]), sqrtf(mC[5]), sqrtf(mC[14]), mC[10], err)); // clang-format on + if (crossCE) { + if (param.rec.tpc.addErrorsCECrossing) { + if (param.rec.tpc.addErrorsCECrossing >= 2) { + AddCovDiagErrorsWithCorrelations(param.rec.tpc.errorsCECrossing); + } else { + AddCovDiagErrors(param.rec.tpc.errorsCECrossing); + } + } else if (mC[2] < 0.5f) { + mC[2] = 0.5f; + } + } + if (err == 0 && changeDirection) { const float mirrordY = prop.GetMirroredYTrack(); CADEBUG(printf(" -- MirroredY: %f --> %f", mP[0], mirrordY)); @@ -249,7 +258,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int iT if (allowModification) { AttachClustersMirror<0>(merger, clusters[ihit].slice, clusters[ihit].row, iTrk, yy, prop); // TODO: Never true, will always call FollowCircle above, really??? } - MirrorTo(prop, yy, zz, inFlyDirection, param, clusters[ihit].row, clusterState, true); + MirrorTo(prop, yy, zz, inFlyDirection, param, clusters[ihit].row, clusterState, true, clusters[ihit].slice > 18); noFollowCircle = false; lastUpdateX = mX; @@ -271,7 +280,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int iT const int err2 = mNDF > 0 && CAMath::Abs(prop.GetSinPhi0()) >= maxSinForUpdate; if (err || err2) { - if (mC[0] > GPUCA_MERGER_COV_LIMIT || mC[2] > GPUCA_MERGER_COV_LIMIT) { + if (mC[0] > param.rec.tpc.trackFitCovLimit || mC[2] > param.rec.tpc.trackFitCovLimit) { break; } MarkClusters(clusters, ihitMergeFirst, ihit, wayDirection, GPUTPCGMMergedTrackHit::flagNotFit); @@ -307,7 +316,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int iT tup.Fill((float)clusters[ihit].row, xx, yy, zz, clAlpha, mX, ImP0, ImP1, mP[2], mP[3], mP[4], ImC0, ImC2, mC[14]); } #endif - retVal = prop.Update(yy, zz, clusters[ihit].row, param, clusterState, rejectChi2, &interpolation.hit[ihit], refit GPUCA_DEBUG_STREAMER_CHECK(, iTrk)); + retVal = prop.Update(yy, zz, clusters[ihit].row, param, clusterState, rejectChi2, &interpolation.hit[ihit], refit, clusters[ihit].slice > 18 GPUCA_DEBUG_STREAMER_CHECK(, iTrk)); GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamUpdateTrack, iTrk)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_update_track", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_update_track").data() << "iTrk=" << iTrk << "ihit=" << ihit << "yy=" << yy << "zz=" << zz << "cluster=" << clusters[ihit] << "track=" << this << "rejectChi2=" << rejectChi2 << "interpolationhit=" << interpolation.hit[ihit] << "refit=" << refit << "retVal=" << retVal << "\n"; }) @@ -421,13 +430,13 @@ GPUdni() void GPUTPCGMTrackParam::MoveToReference(GPUTPCGMPropagator& prop, cons } } -GPUd() void GPUTPCGMTrackParam::MirrorTo(GPUTPCGMPropagator& GPUrestrict() prop, float toY, float toZ, bool inFlyDirection, const GPUParam& param, unsigned char row, unsigned char clusterState, bool mirrorParameters) +GPUd() void GPUTPCGMTrackParam::MirrorTo(GPUTPCGMPropagator& GPUrestrict() prop, float toY, float toZ, bool inFlyDirection, const GPUParam& param, unsigned char row, unsigned char clusterState, bool mirrorParameters, bool cSide) { if (mirrorParameters) { prop.Mirror(inFlyDirection); } float err2Y, err2Z; - prop.GetErr2(err2Y, err2Z, param, toZ, row, clusterState); + prop.GetErr2(err2Y, err2Z, param, toZ, row, clusterState, cSide); prop.Model().Y() = mP[0] = toY; prop.Model().Z() = mP[1] = toZ; if (mC[0] < err2Y) { @@ -452,7 +461,7 @@ GPUd() int GPUTPCGMTrackParam::MergeDoubleRowClusters(int& ihit, int wayDirectio { if (ihit + wayDirection >= 0 && ihit + wayDirection < maxN && clusters[ihit].row == clusters[ihit + wayDirection].row && clusters[ihit].slice == clusters[ihit + wayDirection].slice && clusters[ihit].leg == clusters[ihit + wayDirection].leg) { float maxDistY, maxDistZ; - prop.GetErr2(maxDistY, maxDistZ, merger->Param(), zz, clusters[ihit].row, 0); + prop.GetErr2(maxDistY, maxDistZ, merger->Param(), zz, clusters[ihit].row, 0, clusters[ihit].slice > 18); maxDistY = (maxDistY + mC[0]) * 20.f; maxDistZ = (maxDistZ + mC[2]) * 20.f; int noReject = 0; // Cannot reject if simple estimation of y/z fails (extremely unlike case) @@ -1183,3 +1192,24 @@ GPUd() void GPUTPCGMTrackParam::Rotate(float alpha) mC[11] = -mC[11]; } } + +GPUd() void GPUTPCGMTrackParam::AddCovDiagErrors(const float* GPUrestrict() errors2) +{ + mC[0] += errors2[0]; + mC[2] += errors2[1]; + mC[5] += errors2[2]; + mC[9] += errors2[3]; + mC[14] += errors2[4]; +} + +GPUd() void GPUTPCGMTrackParam::AddCovDiagErrorsWithCorrelations(const float* GPUrestrict() errors2) +{ + const int diagMap[5] = {0, 2, 5, 9, 14}; + const float oldDiag[5] = {mC[0], mC[2], mC[5], mC[9], mC[14]}; + for (int i = 0; i < 5; i++) { + mC[diagMap[i]] += errors2[i]; + for (int j = 0; j < i; j++) { + mC[diagMap[i - 1] + j + 1] *= gpu::CAMath::Sqrt(mC[diagMap[i]] * mC[diagMap[j]] / (oldDiag[i] * oldDiag[j])); + } + } +} diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h index 594936991e21e..4dd66924bf6f4 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.h @@ -145,7 +145,7 @@ class GPUTPCGMTrackParam GPUd() bool Fit(GPUTPCGMMerger* merger, int iTrk, GPUTPCGMMergedTrackHit* clusters, GPUTPCGMMergedTrackHitXYZ* clustersXYZ, int& N, int& NTolerated, float& Alpha, int attempt = 0, float maxSinPhi = GPUCA_MAX_SIN_PHI, gputpcgmmergertypes::GPUTPCOuterParam* outerParam = nullptr); GPUd() void MoveToReference(GPUTPCGMPropagator& prop, const GPUParam& param, float& alpha); - GPUd() void MirrorTo(GPUTPCGMPropagator& prop, float toY, float toZ, bool inFlyDirection, const GPUParam& param, unsigned char row, unsigned char clusterState, bool mirrorParameters); + GPUd() void MirrorTo(GPUTPCGMPropagator& prop, float toY, float toZ, bool inFlyDirection, const GPUParam& param, unsigned char row, unsigned char clusterState, bool mirrorParameters, bool cSide); GPUd() int MergeDoubleRowClusters(int& ihit, int wayDirection, GPUTPCGMMergedTrackHit* clusters, GPUTPCGMMergedTrackHitXYZ* clustersXYZ, const GPUTPCGMMerger* merger, GPUTPCGMPropagator& prop, float& xx, float& yy, float& zz, int maxN, float clAlpha, unsigned char& clusterState, bool rejectChi2); GPUd() void AttachClustersPropagate(const GPUTPCGMMerger* GPUrestrict() Merger, int slice, int lastRow, int toRow, int iTrack, bool goodLeg, GPUTPCGMPropagator& prop, bool inFlyDirection, float maxSinPhi = GPUCA_MAX_SIN_PHI); @@ -160,6 +160,9 @@ class GPUTPCGMTrackParam GPUd() void StoreOuter(gputpcgmmergertypes::GPUTPCOuterParam* outerParam, const GPUTPCGMPropagator& prop, int phase); GPUd() static void RefitLoop(const GPUTPCGMMerger* GPUrestrict() Merger, int loopIdx); + GPUd() void AddCovDiagErrors(const float* GPUrestrict() errors2); + GPUd() void AddCovDiagErrorsWithCorrelations(const float* GPUrestrict() errors2); + GPUdi() void MarkClusters(GPUTPCGMMergedTrackHit* GPUrestrict() clusters, int ihitFirst, int ihitLast, int wayDirection, unsigned char state) { clusters[ihitFirst].state |= state; diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx b/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx index 3fd29ab96a31b..f90f4193f6156 100644 --- a/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx +++ b/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx @@ -245,11 +245,12 @@ GPUd() int GPUTrackingRefit::RefitTrack(T& trkX, bool outward, bool resetCov) int start = outward ? count - 1 : begin; int stop = outward ? begin - 1 : count; const ClusterNative* cl = nullptr; - uint8_t sector, row = 0, currentSector = 0, currentRow = 0; - short clusterState = 0, nextState; + uint8_t sector = 255, row = 255; + int lastSector = -1, currentSector = -1, currentRow = -1; + short clusterState = 0, nextState = 0; int nFitted = 0; for (int i = start; i != stop; i += cl ? 0 : direction) { - float x, y, z, charge = 0.f; + float x = 0, y = 0, z = 0, charge = 0; // FIXME: initialization unneeded, but GCC incorrectly produces uninitialized warnings otherwise int clusters = 0; while (true) { if (!cl) { @@ -324,11 +325,23 @@ GPUd() int GPUTrackingRefit::RefitTrack(T& trkX, bool outward, bool resetCov) IgnoreErrors(trk.GetSinPhi()); return -2; } + if (lastSector != -1 && (lastSector < 18) != (sector < 18)) { + if (mPparam->rec.tpc.addErrorsCECrossing) { + if (mPparam->rec.tpc.addErrorsCECrossing >= 2) { + trk.AddCovDiagErrorsWithCorrelations(mPparam->rec.tpc.errorsCECrossing); + } else { + trk.AddCovDiagErrors(mPparam->rec.tpc.errorsCECrossing); + } + } else if (trk.Cov()[2] < 0.5f) { + trk.Cov()[2] = 0.5f; + } + } if (resetCov) { trk.ResetCovariance(); } CADEBUG(printf("\t%21sPropaga Alpha %8.3f , X %8.3f - Y %8.3f, Z %8.3f - QPt %7.2f (%7.2f), SP %5.2f (%5.2f) --- Res %8.3f %8.3f --- Cov sY %8.3f sZ %8.3f sSP %8.3f sPt %8.3f - YPt %8.3f\n", "", prop.GetAlpha(), x, trk.Par()[0], trk.Par()[1], trk.Par()[4], prop.GetQPt0(), trk.Par()[2], prop.GetSinPhi0(), trk.Par()[0] - y, trk.Par()[1] - z, sqrtf(trk.Cov()[0]), sqrtf(trk.Cov()[2]), sqrtf(trk.Cov()[5]), sqrtf(trk.Cov()[14]), trk.Cov()[10])); - if (prop.Update(y, z, row, *mPparam, clusterState, 0, nullptr, true)) { + lastSector = sector; + if (prop.Update(y, z, row, *mPparam, clusterState, 0, nullptr, true, sector > 18)) { IgnoreErrors(trk.GetSinPhi()); return -3; } @@ -342,6 +355,13 @@ GPUd() int GPUTrackingRefit::RefitTrack(T& trkX, bool outward, bool resetCov) IgnoreErrors(trk.getSnp()); return -2; } + if (lastSector != -1 && (lastSector < 18) != (sector < 18)) { + if (mPparam->rec.tpc.addErrorsCECrossing) { + trk.updateCov(mPparam->rec.tpc.errorsCECrossing, mPparam->rec.tpc.addErrorsCECrossing >= 2); + } else if (trk.getCov()[2] < 0.5f) { + trk.setCov(0.5f, 2); + } + } if (resetCov) { trk.resetCovariance(); float bzkG = prop->getNominalBz(), qptB5Scale = CAMath::Abs(bzkG) > 0.1 ? CAMath::Abs(bzkG) / 5.006680f : 1.f; diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCGeometry.h b/GPU/GPUTracking/SliceTracker/GPUTPCGeometry.h index 59172aeb736b6..30086b92e6206 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCGeometry.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCGeometry.h @@ -107,6 +107,7 @@ class GPUTPCGeometry // TODO: Make values constexpr GPUd() static CONSTEXPR float TPCLength() { return 250.f - 0.275f; } GPUd() float Row2X(int row) const { return (mX[row]); } GPUd() float PadHeight(int row) const { return (mPadHeight[GetRegion(row)]); } + GPUd() float PadHeightByRegion(int region) const { return (mPadHeight[region]); } GPUd() float PadWidth(int row) const { return (mPadWidth[GetRegion(row)]); } GPUd() unsigned char NPads(int row) const { return mNPads[row]; } diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx index 9cebcedb096e9..fd1d7b42be407 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.cxx @@ -71,7 +71,7 @@ GPUd() int GPUTPCGlobalTracking::PerformGlobalTrackingRun(GPUTPCTracker& tracker calink rowHits[GPUCA_ROW_COUNT]; int nHits = GPUTPCTrackletConstructor::GPUTPCTrackletConstructorGlobalTracking(tracker, smem, tParam, rowIndex, direction, 0, rowHits); - if (nHits >= GPUCA_GLOBAL_TRACKING_MIN_HITS) { + if (nHits >= tracker.Param().rec.tpc.globalTrackingMinHits) { // GPUInfo("%d hits found", nHits); unsigned int hitId = CAMath::AtomicAdd(&tracker.CommonMemory()->nTrackHits, (unsigned int)nHits); if (hitId + nHits > tracker.NMaxTrackHits()) { @@ -117,7 +117,7 @@ GPUd() int GPUTPCGlobalTracking::PerformGlobalTrackingRun(GPUTPCTracker& tracker track.SetLocalTrackId((sliceSource.ISlice() << 24) | sliceSource.Tracks()[iTrack].LocalTrackId()); } - return (nHits >= GPUCA_GLOBAL_TRACKING_MIN_HITS); + return (nHits >= tracker.Param().rec.tpc.globalTrackingMinHits); } GPUd() void GPUTPCGlobalTracking::PerformGlobalTracking(int nBlocks, int nThreads, int iBlock, int iThread, const GPUTPCTracker& tracker, GPUsharedref() MEM_LOCAL(GPUSharedMemory) & smem, GPUTPCTracker& GPUrestrict() sliceTarget, bool right) @@ -125,15 +125,15 @@ GPUd() void GPUTPCGlobalTracking::PerformGlobalTracking(int nBlocks, int nThread for (int i = iBlock * nThreads + iThread; i < tracker.CommonMemory()->nLocalTracks; i += nThreads * nBlocks) { { const int tmpHit = tracker.Tracks()[i].FirstHitID(); - if (tracker.TrackHits()[tmpHit].RowIndex() >= GPUCA_GLOBAL_TRACKING_MIN_ROWS && tracker.TrackHits()[tmpHit].RowIndex() < GPUCA_GLOBAL_TRACKING_RANGE) { + if (tracker.TrackHits()[tmpHit].RowIndex() >= tracker.Param().rec.tpc.globalTrackingMinRows && tracker.TrackHits()[tmpHit].RowIndex() < tracker.Param().rec.tpc.globalTrackingRowRange) { int rowIndex = tracker.TrackHits()[tmpHit].RowIndex(); const GPUTPCRow& GPUrestrict() row = tracker.Row(rowIndex); float Y = (float)tracker.Data().HitDataY(row, tracker.TrackHits()[tmpHit].HitIndex()) * row.HstepY() + row.Grid().YMin(); - if (!right && Y < -row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_LOWER) { + if (!right && Y < -row.MaxY() * tracker.Param().rec.tpc.globalTrackingYRangeLower) { // GPUInfo("Track %d, lower row %d, left border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, -row.MaxY()); PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, -tracker.Param().par.dAlpha, -1); } - if (right && Y > row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_LOWER) { + if (right && Y > row.MaxY() * tracker.Param().rec.tpc.globalTrackingYRangeLower) { // GPUInfo("Track %d, lower row %d, right border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, row.MaxY()); PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, tracker.Param().par.dAlpha, -1); } @@ -142,15 +142,15 @@ GPUd() void GPUTPCGlobalTracking::PerformGlobalTracking(int nBlocks, int nThread { const int tmpHit = tracker.Tracks()[i].FirstHitID() + tracker.Tracks()[i].NHits() - 1; - if (tracker.TrackHits()[tmpHit].RowIndex() < GPUCA_ROW_COUNT - GPUCA_GLOBAL_TRACKING_MIN_ROWS && tracker.TrackHits()[tmpHit].RowIndex() >= GPUCA_ROW_COUNT - GPUCA_GLOBAL_TRACKING_RANGE) { + if (tracker.TrackHits()[tmpHit].RowIndex() < GPUCA_ROW_COUNT - tracker.Param().rec.tpc.globalTrackingMinRows && tracker.TrackHits()[tmpHit].RowIndex() >= GPUCA_ROW_COUNT - tracker.Param().rec.tpc.globalTrackingRowRange) { int rowIndex = tracker.TrackHits()[tmpHit].RowIndex(); const GPUTPCRow& GPUrestrict() row = tracker.Row(rowIndex); float Y = (float)tracker.Data().HitDataY(row, tracker.TrackHits()[tmpHit].HitIndex()) * row.HstepY() + row.Grid().YMin(); - if (!right && Y < -row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_UPPER) { + if (!right && Y < -row.MaxY() * tracker.Param().rec.tpc.globalTrackingYRangeUpper) { // GPUInfo("Track %d, upper row %d, left border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, -row.MaxY()); PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, -tracker.Param().par.dAlpha, 1); } - if (right && Y > row.MaxY() * GPUCA_GLOBAL_TRACKING_Y_RANGE_UPPER) { + if (right && Y > row.MaxY() * tracker.Param().rec.tpc.globalTrackingYRangeUpper) { // GPUInfo("Track %d, upper row %d, right border (%f of %f)", i, mTrackHits[tmpHit].RowIndex(), Y, row.MaxY()); PerformGlobalTrackingRun(sliceTarget, smem, tracker, i, rowIndex, tracker.Param().par.dAlpha, 1); } diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx index b46edf1f91d35..48d88272d42a1 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx @@ -392,9 +392,9 @@ GPUdii() int GPUTPCSliceData::InitFromClusterData(int nBlocks, int nThreads, int mHitData[globalBinsortedIndex].y = (cahit)yy; } - if (iThread == 0) { + if (iThread == 0 && !mem->param.par.continuousTracking) { const float maxAbsZ = CAMath::Max(CAMath::Abs(tmpMinMax[2]), CAMath::Abs(tmpMinMax[3])); - if (maxAbsZ > 300 && !mem->param.par.continuousTracking) { + if (maxAbsZ > 300) { mem->errorCodes.raiseError(GPUErrors::ERROR_SLICEDATA_Z_OVERFLOW, iSlice, (unsigned int)maxAbsZ); return 1; } diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx index 141c35ad800f0..3d059b1f4970e 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx @@ -203,6 +203,9 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int tracker.GetErrors2Seeding(iRow, tParam.GetZ(), sinPhi, tParam.GetDzDs(), err2Y, err2Z); if (r.mNHits >= 10) { + const float sErr2 = tracker.Param().GetSystematicClusterErrorIFC2(x, tParam.GetZ(), tracker.ISlice() > 18); + err2Y += sErr2; + err2Z += sErr2; const float kFactor = tracker.Param().rec.tpc.hitPickUpFactor * tracker.Param().rec.tpc.hitPickUpFactor * 3.5f * 3.5f; float sy2 = kFactor * (tParam.Err2Y() + err2Y); float sz2 = kFactor * (tParam.Err2Z() + err2Z); @@ -215,7 +218,7 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int dy = y - tParam.Y(); dz = z - tParam.Z(); if (dy * dy > sy2 || dz * dz > sz2) { - if (++r.mNMissed >= GPUCA_TRACKLET_CONSTRUCTOR_MAX_ROW_GAP_SEED) { + if (++r.mNMissed >= tracker.Param().rec.tpc.trackFollowingMaxRowGapSeed) { r.mCurrIH = CALINK_INVAL; } rowHit = CALINK_INVAL; @@ -253,7 +256,7 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int if (r.mStage == 2 && iRow > r.mEndRow) { break; } - if (r.mNMissed > GPUCA_TRACKLET_CONSTRUCTOR_MAX_ROW_GAP) { + if (r.mNMissed > tracker.Param().rec.tpc.trackFollowingMaxRowGap) { r.mGo = 0; break; } @@ -298,6 +301,11 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int float err2Y, err2Z; tracker.GetErrors2Seeding(iRow, *((MEM_LG2(GPUTPCTrackParam)*)&tParam), err2Y, err2Z); + if (r.mNHits >= 10) { + const float sErr2 = tracker.Param().GetSystematicClusterErrorIFC2(x, tParam.GetZ(), tracker.ISlice() > 18); + err2Y += sErr2; + err2Z += sErr2; + } if (CAMath::Abs(yUncorrected) < x * MEM_GLOBAL(GPUTPCRow)::getTPCMaxY1X()) { // search for the closest hit const float kFactor = tracker.Param().rec.tpc.hitPickUpFactor * tracker.Param().rec.tpc.hitPickUpFactor * 7.0f * 7.0f; const float maxWindow2 = tracker.Param().rec.tpc.hitSearchArea2; @@ -328,7 +336,7 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int /*nBlocks*/, int float dy = y - yUncorrected; float dz = z - zUncorrected; if (dy * dy < sy2 && dz * dz < sz2) { - float dds = GPUCA_Y_FACTOR * CAMath::Abs(dy) + CAMath::Abs(dz); + float dds = tracker.Param().rec.tpc.trackFollowingYFactor * CAMath::Abs(dy) + CAMath::Abs(dz); if (dds < ds) { ds = dds; best = ih; diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 9abec1139b80c..95de3f3430e52 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -570,6 +570,9 @@ int GPUQA::loadHistograms(std::vector& i1, std::vector& i2, std::vec mHist1Dd_pos.clear(); mHistGraph_pos.clear(); mHaveExternalHists = true; + if (mConfig.noMC) { + tasks &= tasksNoQC; + } mQATasks = tasks; if (InitQACreateHistograms()) { return 1; @@ -751,6 +754,9 @@ int GPUQA::InitQA(int tasks) mHist2D = new std::vector; mHist1Dd = new std::vector; mHistGraph = new std::vector; + if (mConfig.noMC) { + tasks &= tasksNoQC; + } mQATasks = tasks; if (mTracking->GetProcessingSettings().qcRunFraction != 100.f && mQATasks != taskClusterCounts) { @@ -822,7 +828,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx throw std::runtime_error("QA not initialized"); } if (mTracking && mTracking->GetProcessingSettings().debugLevel >= 2) { - printf("Running QA\n"); + GPUInfo("Running QA - Mask %d, Efficiency %d, Resolution %d, Pulls %d, Cluster Attachment %d, Track Statistics %d, Cluster Counts %d", mQATasks, (int)(mQATasks & taskTrackingEff), (int)(mQATasks & taskTrackingRes), (int)(mQATasks & taskTrackingResPull), (int)(mQATasks & taskClusterAttach), (int)(mQATasks & taskTrackStatistics), (int)(mQATasks & taskClusterCounts)); } if (!clNative && mTracking) { clNative = mTracking->mIOPtrs.clustersNative; @@ -2030,7 +2036,7 @@ int GPUQA::DrawQAHistograms(TObjArray* qcout) } } - if (mConfig.enableLocalOutput && !mConfig.inputHistogramsOnly && (mQATasks & taskTrackingEff)) { + if (mConfig.enableLocalOutput && !mConfig.inputHistogramsOnly && (mQATasks & taskTrackingEff) && mcPresent()) { GPUInfo("QA Stats: Eff: Tracks Prim %d (Eta %d, Pt %d) %f%% (%f%%) Sec %d (Eta %d, Pt %d) %f%% (%f%%) - Res: Tracks %d (Eta %d, Pt %d)", (int)mEff[3][1][0][0]->GetEntries(), (int)mEff[3][1][0][3]->GetEntries(), (int)mEff[3][1][0][4]->GetEntries(), mEff[0][0][0][0]->GetSumOfWeights() / std::max(1., mEff[3][0][0][0]->GetSumOfWeights()), mEff[0][1][0][0]->GetSumOfWeights() / std::max(1., mEff[3][1][0][0]->GetSumOfWeights()), (int)mEff[3][1][1][0]->GetEntries(), (int)mEff[3][1][1][3]->GetEntries(), (int)mEff[3][1][1][4]->GetEntries(), mEff[0][0][1][0]->GetSumOfWeights() / std::max(1., mEff[3][0][1][0]->GetSumOfWeights()), mEff[0][1][1][0]->GetSumOfWeights() / std::max(1., mEff[3][1][1][0]->GetSumOfWeights()), (int)mRes2[0][0]->GetEntries(), @@ -2055,14 +2061,20 @@ int GPUQA::DrawQAHistograms(TObjArray* qcout) if (k == 0 && mConfig.inputHistogramsOnly == 0 && ii != 5) { if (l == 0) { // Divide eff, compute all for fake/clone + auto oldLevel = gErrorIgnoreLevel; + gErrorIgnoreLevel = kError; mEffResult[0][j / 2][j % 2][i]->Divide(mEff[l][j / 2][j % 2][i], mEff[3][j / 2][j % 2][i], "cl=0.683 b(1,1) mode"); + gErrorIgnoreLevel = oldLevel; mEff[3][j / 2][j % 2][i]->Reset(); // Sum up rec + clone + fake for clone/fake rate mEff[3][j / 2][j % 2][i]->Add(mEff[0][j / 2][j % 2][i]); mEff[3][j / 2][j % 2][i]->Add(mEff[1][j / 2][j % 2][i]); mEff[3][j / 2][j % 2][i]->Add(mEff[2][j / 2][j % 2][i]); } else { // Divide fake/clone + auto oldLevel = gErrorIgnoreLevel; + gErrorIgnoreLevel = kError; mEffResult[l][j / 2][j % 2][i]->Divide(mEff[l][j / 2][j % 2][i], mEff[3][j / 2][j % 2][i], "cl=0.683 b(1,1) mode"); + gErrorIgnoreLevel = oldLevel; } } @@ -2471,7 +2483,10 @@ int GPUQA::DrawQAHistograms(TObjArray* qcout) } for (int i = 0; i < N_CLS_HIST - 1; i++) { + auto oldLevel = gErrorIgnoreLevel; + gErrorIgnoreLevel = kError; mClusters[N_CLS_HIST + i]->Divide(mClusters[i], mClusters[N_CLS_HIST - 1], 1, 1, "B"); + gErrorIgnoreLevel = oldLevel; mClusters[N_CLS_HIST + i]->SetMinimum(-0.02); mClusters[N_CLS_HIST + i]->SetMaximum(1.02); } diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index a7e45f8fa4d3d..d5fef788c099c 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -113,6 +113,7 @@ class GPUQA int ReadO2MCData(const char* filename); static bool QAAvailable() { return true; } bool IsInitialized() { return mQAInitialized; } + void UpdateChain(GPUChainTracking* chain) { mTracking = chain; } const std::vector& getHistograms1D() const { return *mHist1D; } const std::vector& getHistograms2D() const { return *mHist2D; } @@ -135,7 +136,8 @@ class GPUQA taskTrackStatistics = 16, taskClusterCounts = 32, taskDefault = 63, - taskDefaultPostprocess = 31 + taskDefaultPostprocess = 31, + tasksNoQC = 56 }; private: diff --git a/GPU/GPUTracking/utils/qconfig.h b/GPU/GPUTracking/utils/qconfig.h index 3834b0ff7eba8..b58b40a1d3b36 100644 --- a/GPU/GPUTracking/utils/qconfig.h +++ b/GPU/GPUTracking/utils/qconfig.h @@ -254,6 +254,17 @@ enum qConfigRetVal { qcrOK = 0, AddOption(name, type, default, optname, optnameshort, help); \ } #define AddOptionRTC(name, type, default, optname, optnameshort, help, ...) AddVariableRTC(name, type, default) +#define AddOptionArrayRTC(name, type, count, default, optname, optnameshort, help, ...) \ + if (useConstexpr) { \ + out << "static constexpr " << qon_mxstr(type) << " " << qon_mxstr(name) << "[" << count << "] = {" << qConfig::print_type(std::get(tSrc)->name[0]); \ + for (int i = 1; i < count; i++) { \ + out << ", " << qConfig::print_type(std::get(tSrc)->name[i]); \ + } \ + out << "};\n"; \ + out << qon_mxstr(type) << " " << qon_mxstr(qon_mxcat(_dummy_, name)) << ";\n"; \ + } else { \ + AddOptionArray(name, type, count, default, optname, optnameshort, help); \ + } #define BeginConfig(name, instance) \ { \ using qConfigCurrentType = name; \ @@ -283,7 +294,8 @@ enum qConfigRetVal { qcrOK = 0, #if !defined(QCONFIG_GPU) #define AddOption(name, type, default, optname, optnameshort, help, ...) type name = default; #define AddVariable(name, type, default) type name = default; -#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {default}; +#define _AddOptionArray_INTERNAL_EXPAND(...) __VA_ARGS__ +#define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {_AddOptionArray_INTERNAL_EXPAND default}; #define AddOptionVec(name, type, optname, optnameshort, help, ...) std::vector name; #else #define AddOption(name, type, default, optname, optnameshort, help, ...) type name; @@ -296,6 +308,10 @@ enum qConfigRetVal { qcrOK = 0, static constexpr type name = default; \ type _dummy_##name = default; #define AddOptionRTC(name, type, default, optname, optnameshort, help, ...) AddVariableRTC(name, type, default) +#define _AddOptionArray_INTERNAL_EXPAND(...) __VA_ARGS__ +#define AddOptionArrayRTC(name, type, count, default, optname, optnameshort, help, ...) \ + static constexpr type name[count] = {_AddOptionArray_INTERNAL_EXPAND default}; \ + type _dummy_##name[count] = {_AddOptionArray_INTERNAL_EXPAND default}; #else #define AddCustomCPP(...) __VA_ARGS__ #endif @@ -344,6 +360,9 @@ enum qConfigRetVal { qcrOK = 0, #ifndef AddVariableRTC #define AddVariableRTC(...) AddVariable(__VA_ARGS__) #endif +#ifndef AddOptionArrayRTC +#define AddOptionArrayRTC(...) AddOptionArray(__VA_ARGS__) +#endif #ifndef BeginHiddenConfig #define BeginHiddenConfig(name, instance) BeginSubConfig(name, instance, , , , ) #endif @@ -357,6 +376,7 @@ enum qConfigRetVal { qcrOK = 0, #undef AddOptionSet #undef AddOptionVec #undef AddOptionArray +#undef AddOptionArrayRTC #undef AddArrayDefaults #undef AddSubConfig #undef BeginConfig diff --git a/GPU/GPUTracking/utils/qconfig_helpers.h b/GPU/GPUTracking/utils/qconfig_helpers.h index 5ca2c8a693f61..4c80963d949a5 100644 --- a/GPU/GPUTracking/utils/qconfig_helpers.h +++ b/GPU/GPUTracking/utils/qconfig_helpers.h @@ -24,6 +24,8 @@ #define qon_mxcat3(a, b, c) qon_mcat3(a, b, c) #define qon_mstr(a) #a #define qon_mxstr(a) qon_mstr(a) +#define qon_mexp(...) __VA_ARGS__ +#define qon_mxexp(X) qon_mexp X namespace qConfig { diff --git a/GPU/GPUbenchmark/Shared/Kernels.h b/GPU/GPUbenchmark/Shared/Kernels.h index b16cfcde162ee..900b683f24219 100644 --- a/GPU/GPUbenchmark/Shared/Kernels.h +++ b/GPU/GPUbenchmark/Shared/Kernels.h @@ -32,7 +32,7 @@ class GPUbenchmark final { public: GPUbenchmark() = delete; // need for a configuration - GPUbenchmark(benchmarkOpts& opts, std::shared_ptr rWriter) : mResultWriter{rWriter}, mOptions{opts} + GPUbenchmark(benchmarkOpts& opts) : mOptions{opts} { } virtual ~GPUbenchmark() = default; @@ -82,7 +82,6 @@ class GPUbenchmark final private: gpuState mState; - std::shared_ptr mResultWriter; benchmarkOpts mOptions; }; diff --git a/GPU/GPUbenchmark/Shared/Utils.h b/GPU/GPUbenchmark/Shared/Utils.h index 6afa988bd9901..97d6788eb01de 100644 --- a/GPU/GPUbenchmark/Shared/Utils.h +++ b/GPU/GPUbenchmark/Shared/Utils.h @@ -27,8 +27,6 @@ #include #include #include -#include -#include #define KNRM "\x1B[0m" #define KRED "\x1B[31m" @@ -49,6 +47,11 @@ exit(EXIT_FAILURE); #endif +template +void discardResult(const T&) +{ +} + enum class Test { Read, Write, @@ -258,66 +261,5 @@ struct gpuState { size_t nMaxThreadsPerBlock; }; -// Interface class to stream results to root file -class ResultWriter -{ - public: - explicit ResultWriter(const std::string resultsTreeFilename = "benchmark_results.root"); - ~ResultWriter() = default; - void storeBenchmarkEntry(Test test, int chunk, float entry, float chunkSizeGB, int nLaunches); - void addBenchmarkEntry(const std::string bName, const std::string type, const int nChunks); - void snapshotBenchmark(); - void saveToFile(); - - private: - std::vector mTimeResults; - std::vector mTimeTrees; - std::vector mThroughputResults; - std::vector mThroughputTrees; - TFile* mOutfile; -}; - -inline ResultWriter::ResultWriter(const std::string resultsTreeFilename) -{ - mOutfile = TFile::Open(resultsTreeFilename.data(), "recreate"); -} - -inline void ResultWriter::addBenchmarkEntry(const std::string bName, const std::string type, const int nChunks) -{ - mTimeTrees.emplace_back(new TTree((bName + "_" + type).data(), (bName + "_" + type).data())); - mTimeResults.clear(); - mTimeResults.resize(nChunks); - mTimeTrees.back()->Branch("elapsed", &mTimeResults); - - mThroughputTrees.emplace_back(new TTree((bName + "_" + type + "_TP").data(), (bName + "_" + type + "_TP").data())); - mThroughputResults.clear(); - mThroughputResults.resize(nChunks); - mThroughputTrees.back()->Branch("throughput", &mThroughputResults); -} - -inline void ResultWriter::storeBenchmarkEntry(Test test, int chunk, float entry, float chunkSizeGB, int nLaunches) -{ - mTimeResults[chunk] = entry; - mThroughputResults[chunk] = computeThroughput(test, entry, chunkSizeGB, nLaunches); -} - -inline void ResultWriter::snapshotBenchmark() -{ - mTimeTrees.back()->Fill(); - mThroughputTrees.back()->Fill(); -} - -inline void ResultWriter::saveToFile() -{ - mOutfile->cd(); - for (auto t : mTimeTrees) { - t->Write(); - } - for (auto t : mThroughputTrees) { - t->Write(); - } - mOutfile->Close(); -} - } // namespace benchmark } // namespace o2 \ No newline at end of file diff --git a/GPU/GPUbenchmark/cuda/Kernels.cu b/GPU/GPUbenchmark/cuda/Kernels.cu index 8c2bddc7634ae..c455e8f6126f4 100644 --- a/GPU/GPUbenchmark/cuda/Kernels.cu +++ b/GPU/GPUbenchmark/cuda/Kernels.cu @@ -428,13 +428,14 @@ float GPUbenchmark::runSequential(void (*kernel)(chunk_t*, size_t, T... // Warm up (*kernel)<<>>(chunkPtr, getBufferCapacity(chunk.second, mOptions.prime), args...); - + GPUCHECK(cudaGetLastError()); GPUCHECK(cudaEventCreate(&start)); GPUCHECK(cudaEventCreate(&stop)); GPUCHECK(cudaEventRecord(start)); for (auto iLaunch{0}; iLaunch < nLaunches; ++iLaunch) { // Schedule all the requested kernel launches (*kernel)<<>>(chunkPtr, getBufferCapacity(chunk.second, mOptions.prime), args...); // NOLINT: clang-tidy false-positive + GPUCHECK(cudaGetLastError()); } GPUCHECK(cudaEventRecord(stop)); // record checkpoint GPUCHECK(cudaEventSynchronize(stop)); // synchronize executions @@ -653,7 +654,6 @@ void GPUbenchmark::initTest(Test test) template void GPUbenchmark::runTest(Test test, Mode mode, KernelConfig config) { - mResultWriter.get()->addBenchmarkEntry(getTestName(mode, test, config), getType(), mState.getMaxChunks()); auto dimGrid{mState.nMultiprocessors}; auto nBlocks{(config == KernelConfig::Single) ? 1 : (config == KernelConfig::Multi) ? dimGrid / mState.testChunks.size() : (config == KernelConfig::All) ? dimGrid @@ -770,7 +770,6 @@ void GPUbenchmark::runTest(Test test, Mode mode, KernelConfig config) } else { std::cout << "" << measurement << "\t" << iChunk << "\t" << throughput << "\t" << chunkSize << "\t" << result << std::endl; } - mResultWriter.get()->storeBenchmarkEntry(test, iChunk, result, chunk.second, mState.getNKernelLaunches()); } } else if (mode == Mode::Concurrent) { if (!mOptions.raw) { @@ -805,7 +804,6 @@ void GPUbenchmark::runTest(Test test, Mode mode, KernelConfig config) } else { std::cout << "" << measurement << "\t" << iChunk << "\t" << throughput << "\t" << chunkSize << "\t" << results[iChunk] << std::endl; } - mResultWriter.get()->storeBenchmarkEntry(test, iChunk, results[iChunk], chunk.second, mState.getNKernelLaunches()); } if (mState.testChunks.size() > 1) { if (!mOptions.raw) { @@ -850,9 +848,7 @@ void GPUbenchmark::runTest(Test test, Mode mode, KernelConfig config) } else { std::cout << "" << measurement << "\t" << 0 << "\t" << throughput << "\t" << tot << "\t" << result << std::endl; } - mResultWriter.get()->storeBenchmarkEntry(test, 0, result, tot, mState.getNKernelLaunches()); } - mResultWriter.get()->snapshotBenchmark(); } } diff --git a/GPU/GPUbenchmark/cuda/benchmark.cu b/GPU/GPUbenchmark/cuda/benchmark.cu index 603058ea5cf33..5cd87b5307ea4 100644 --- a/GPU/GPUbenchmark/cuda/benchmark.cu +++ b/GPU/GPUbenchmark/cuda/benchmark.cu @@ -11,6 +11,7 @@ /// /// \author mconcas@cern.ch /// +#include #include "../Shared/Kernels.h" #define VERSION "version 0.3" @@ -56,7 +57,7 @@ bool parseArgs(o2::benchmark::benchmarkOpts& conf, int argc, const char* argv[]) if (vm.count("extra")) { o2::benchmark::benchmarkOpts opts; - o2::benchmark::GPUbenchmark bm_dummy{opts, nullptr}; + o2::benchmark::GPUbenchmark bm_dummy{opts}; bm_dummy.printDevices(); return false; } @@ -173,39 +174,32 @@ bool parseArgs(o2::benchmark::benchmarkOpts& conf, int argc, const char* argv[]) return true; } -using o2::benchmark::ResultWriter; - int main(int argc, const char* argv[]) { + std::cout << "Started benchmark with pid: " << getpid() << std::endl; o2::benchmark::benchmarkOpts opts; if (!parseArgs(opts, argc, argv)) { return -1; } - std::shared_ptr writer = std::make_shared(std::to_string(opts.deviceId) + "_" + opts.outFileName + ".root"); - for (auto& dtype : opts.dtypes) { if (dtype == "char") { - o2::benchmark::GPUbenchmark bm_char{opts, writer}; + o2::benchmark::GPUbenchmark bm_char{opts}; bm_char.run(); } else if (dtype == "int") { - o2::benchmark::GPUbenchmark bm_int{opts, writer}; + o2::benchmark::GPUbenchmark bm_int{opts}; bm_int.run(); } else if (dtype == "ulong") { - o2::benchmark::GPUbenchmark bm_size_t{opts, writer}; + o2::benchmark::GPUbenchmark bm_size_t{opts}; bm_size_t.run(); } else if (dtype == "int4") { - o2::benchmark::GPUbenchmark bm_size_t{opts, writer}; + o2::benchmark::GPUbenchmark bm_size_t{opts}; bm_size_t.run(); } else { std::cerr << "Unkonwn data type: " << dtype << std::endl; exit(1); } } - - // save results - writer.get()->saveToFile(); - return 0; } diff --git a/GPU/GPUbenchmark/hip/.gitignore b/GPU/GPUbenchmark/hip/.gitignore index 14f27f00c53c2..d8bb96de21414 100644 --- a/GPU/GPUbenchmark/hip/.gitignore +++ b/GPU/GPUbenchmark/hip/.gitignore @@ -1 +1,2 @@ -*.hip.cxx \ No newline at end of file +*.hip.cxx +*.hip \ No newline at end of file diff --git a/GPU/GPUbenchmark/hip/CMakeLists.txt b/GPU/GPUbenchmark/hip/CMakeLists.txt index 3b85ac6d3554e..94cba1e0ce1ed 100644 --- a/GPU/GPUbenchmark/hip/CMakeLists.txt +++ b/GPU/GPUbenchmark/hip/CMakeLists.txt @@ -10,32 +10,11 @@ # or submit itself to any jurisdiction. message(STATUS "Building GPU HIP benchmark") -# Hipify-perl to generate HIP sources -set(HIPIFY_EXECUTABLE "/opt/rocm/bin/hipify-perl") -file(GLOB CUDA_SOURCES_FULL_PATH "../cuda/*.cu") -foreach(file ${CUDA_SOURCES_FULL_PATH}) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${file}) - get_filename_component(CUDA_SOURCE ${file} NAME) - string(REPLACE ".cu" "" CUDA_SOURCE_NAME ${CUDA_SOURCE}) - add_custom_command( - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - COMMAND ${HIPIFY_EXECUTABLE} --quiet-warnings ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} | sed '1{/\#include \"hip\\/hip_runtime.h\"/d}' > ${CMAKE_CURRENT_SOURCE_DIR}/${CUDA_SOURCE_NAME}.hip.cxx - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/../cuda/${CUDA_SOURCE} - ) -endforeach() +set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-rdc -isystem /opt/rocm/include -fPIC") -set(CMAKE_CXX_COMPILER ${HIP_HIPCC_EXECUTABLE}) -set(CMAKE_CXX_LINKER ${HIP_HIPCC_EXECUTABLE}) -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-rdc") -o2_add_executable(gpu-memory-benchmark-hip - SOURCES benchmark.hip.cxx - Kernels.hip.cxx - PUBLIC_LINK_LIBRARIES hip::host - Boost::program_options - ROOT::Tree - TARGETVARNAME targetName) -if(O2_HIP_CMAKE_LINK_FLAGS) - # Need to add gpu target also to link flags due to gpu-rdc option - target_link_options(${targetName} PUBLIC ${O2_HIP_CMAKE_LINK_FLAGS}) -endif() +o2_add_hipified_executable(gpu-memory-benchmark-hip + SOURCES ../cuda/benchmark.cu + ../cuda/Kernels.cu + PUBLIC_LINK_LIBRARIES hip::host + Boost::program_options + TARGETVARNAME targetName) \ No newline at end of file diff --git a/GPU/Workflow/CMakeLists.txt b/GPU/Workflow/CMakeLists.txt index a0cff072ce5a4..51494b1a1703e 100644 --- a/GPU/Workflow/CMakeLists.txt +++ b/GPU/Workflow/CMakeLists.txt @@ -11,6 +11,9 @@ o2_add_library(GPUWorkflow SOURCES src/GPUWorkflowSpec.cxx + src/GPUWorkflowTPC.cxx + src/GPUWorkflowITS.cxx + src/GPUWorkflowPipeline.cxx TARGETVARNAME targetName PUBLIC_LINK_LIBRARIES O2::Framework O2::DataFormatsTPC diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index 38324b974f078..18aa671d7e380 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -21,14 +21,21 @@ #include "Framework/Task.h" #include "Framework/ConcreteDataMatcher.h" #include "Framework/InitContext.h" -#include "Framework/ProcessingContext.h" #include "Framework/CompletionPolicy.h" #include "Algorithm/Parser.h" #include #include #include +#include +#include +#include class TStopwatch; +namespace fair::mq +{ +struct RegionInfo; +enum class State : int; +} // namespace fair::mq namespace o2 { namespace base @@ -77,6 +84,16 @@ struct TPCPadGainCalib; struct TPCZSLinkMapping; struct GPUSettingsO2; class GPUO2InterfaceQA; +struct GPUTrackingInOutPointers; +struct GPUTrackingInOutZS; +struct GPUInterfaceOutputs; +struct GPUInterfaceInputUpdate; +namespace gpurecoworkflow_internals +{ +struct GPURecoWorkflowSpec_TPCZSBuffers; +struct GPURecoWorkflowSpec_PipelineInternals; +struct GPURecoWorkflow_QueueObject; +} // namespace gpurecoworkflow_internals class GPURecoWorkflowSpec : public o2::framework::Task { @@ -84,6 +101,9 @@ class GPURecoWorkflowSpec : public o2::framework::Task using CompletionPolicyData = std::vector; struct Config { + int itsTriggerType = 0; + int lumiScaleMode = 0; + int enableDoublePipeline = 0; bool decompressTPC = false; bool decompressTPCFromROOT = false; bool caClusterer = false; @@ -104,12 +124,11 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool requireCTPLumi = false; bool outputErrorQA = false; bool runITSTracking = false; - int itsTriggerType = 0; bool itsOverrBeamEst = false; - int lumiScaleMode = 0; + bool tpcTriggerHandling = false; }; - GPURecoWorkflowSpec(CompletionPolicyData* policyData, Config const& specconfig, std::vector const& tpcsectors, unsigned long tpcSectorMask, std::shared_ptr& ggr); + GPURecoWorkflowSpec(CompletionPolicyData* policyData, Config const& specconfig, std::vector const& tpcsectors, unsigned long tpcSectorMask, std::shared_ptr& ggr, std::function** gPolicyOrder = nullptr); ~GPURecoWorkflowSpec() override; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; @@ -123,6 +142,14 @@ class GPURecoWorkflowSpec : public o2::framework::Task void deinitialize(); private: + struct calibObjectStruct { + std::unique_ptr mFastTransform; + std::unique_ptr mFastTransformRef; + std::unique_ptr mFastTransformHelper; + std::unique_ptr mTPCPadGainCalib; + std::unique_ptr mdEdxCalibContainer; + }; + /// initialize TPC options from command line void initFunctionTPCCalib(o2::framework::InitContext& ic); void initFunctionITS(o2::framework::InitContext& ic); @@ -131,31 +158,41 @@ class GPURecoWorkflowSpec : public o2::framework::Task void finaliseCCDBITS(o2::framework::ConcreteDataMatcher& matcher, void* obj); /// asking for newer calib objects template - bool fetchCalibsCCDBTPC(o2::framework::ProcessingContext& pc, T& newCalibObjects); + bool fetchCalibsCCDBTPC(o2::framework::ProcessingContext& pc, T& newCalibObjects, calibObjectStruct& oldCalibObjects); bool fetchCalibsCCDBITS(o2::framework::ProcessingContext& pc); - /// storing the new calib objects by overwritting the old calibs - void storeUpdatedCalibsTPCPtrs(); + /// delete old calib objects no longer needed + void cleanOldCalibsTPCPtrs(calibObjectStruct& oldCalibObjects); + + void doCalibUpdates(o2::framework::ProcessingContext& pc, calibObjectStruct& oldCalibObjects); - void doCalibUpdates(o2::framework::ProcessingContext& pc); + void doTrackTuneTPC(GPUTrackingInOutPointers& ptrs, char* buffout); + template + void processInputs(o2::framework::ProcessingContext&, D&, E&, F&, G&, bool&, H&, I&, J&, K&); + + int runMain(o2::framework::ProcessingContext* pc, GPUTrackingInOutPointers* ptrs, GPUInterfaceOutputs* outputRegions, int threadIndex = 0, GPUInterfaceInputUpdate* inputUpdateCallback = nullptr); int runITSTracking(o2::framework::ProcessingContext& pc); + int handlePipeline(o2::framework::ProcessingContext& pc, GPUTrackingInOutPointers& ptrs, gpurecoworkflow_internals::GPURecoWorkflowSpec_TPCZSBuffers& tpcZSmeta, o2::gpu::GPUTrackingInOutZS& tpcZS, std::unique_ptr& context); + void RunReceiveThread(); + void RunWorkerThread(int id); + void ExitPipeline(); + void handlePipelineEndOfStream(o2::framework::EndOfStreamContext& ec); + void initPipeline(o2::framework::InitContext& ic); + void enqueuePipelinedJob(GPUTrackingInOutPointers* ptrs, GPUInterfaceOutputs* outputRegions, gpurecoworkflow_internals::GPURecoWorkflow_QueueObject* context, bool inputFinal); + void finalizeInputPipelinedJob(GPUTrackingInOutPointers* ptrs, GPUInterfaceOutputs* outputRegions, gpurecoworkflow_internals::GPURecoWorkflow_QueueObject* context); + void receiveFMQStateCallback(fair::mq::State); + CompletionPolicyData* mPolicyData; - std::unique_ptr> mParser; - std::unique_ptr mTracker; + std::function mPolicyOrder; + std::unique_ptr mGPUReco; std::unique_ptr mDisplayFrontend; - std::unique_ptr mFastTransform; - std::unique_ptr mFastTransformRef; - std::unique_ptr mFastTransformNew; - std::unique_ptr mFastTransformRefNew; - std::unique_ptr mFastTransformHelper; - std::unique_ptr mFastTransformHelperNew; - - std::unique_ptr mTPCPadGainCalib; + + calibObjectStruct mCalibObjects; + std::unique_ptr mdEdxCalibContainerBufferNew; std::unique_ptr mTPCPadGainCalibBufferNew; + std::queue mOldCalibObjects; std::unique_ptr mTPCZSLinkMapping; - std::unique_ptr mdEdxCalibContainer; - std::unique_ptr mdEdxCalibContainerBufferNew; std::unique_ptr mTPCVDriftHelper; std::unique_ptr mTRDGeometry; std::unique_ptr mConfig; @@ -168,13 +205,16 @@ class GPURecoWorkflowSpec : public o2::framework::Task std::vector mTPCSectors; std::unique_ptr mITSTracker; std::unique_ptr mITSVertexer; + std::unique_ptr mPipeline; o2::its::TimeFrame* mITSTimeFrame = nullptr; + std::vector mRegionInfos; const o2::itsmft::TopologyDictionary* mITSDict = nullptr; const o2::dataformats::MeanVertexObject* mMeanVertex; unsigned long mTPCSectorMask = 0; int mVerbosity = 0; unsigned int mNTFs = 0; unsigned int mNDebugDumps = 0; + unsigned int mNextThreadIndex = 0; bool mUpdateGainMapCCDB = true; std::unique_ptr mTFSettings; Config mSpecConfig; @@ -186,7 +226,6 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool mITSGeometryCreated = false; bool mTRDGeometryCreated = false; bool mPropagatorInstanceCreated = false; - bool mMustUpdateFastTransform = false; bool mITSRunVertexer = false; bool mITSCosmicsProcessing = false; std::string mITSMode = "sync"; diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx new file mode 100644 index 0000000000000..e3b66ce74a118 --- /dev/null +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -0,0 +1,399 @@ +// 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 GPUWorkflowITS.cxx +/// @author David Rohr + +#include "GPUWorkflow/GPUWorkflowSpec.h" +#include "Headers/DataHeader.h" +#include "Framework/WorkflowSpec.h" // o2::framework::mergeInputs +#include "Framework/DataRefUtils.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/SerializationMethods.h" +#include "Framework/Logger.h" +#include "Framework/CallbackService.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsTPC/TPCSectorHeader.h" +#include "DataFormatsTPC/ClusterNative.h" +#include "DataFormatsTPC/CompressedClusters.h" +#include "DataFormatsTPC/Helpers.h" +#include "DataFormatsTPC/ZeroSuppression.h" +#include "DataFormatsTPC/RawDataTypes.h" +#include "DataFormatsTPC/WorkflowHelper.h" +#include "DataFormatsGlobalTracking/TrackTuneParams.h" +#include "TPCReconstruction/TPCTrackingDigitsPreCheck.h" +#include "TPCReconstruction/TPCFastTransformHelperO2.h" +#include "DataFormatsTPC/Digit.h" +#include "TPCFastTransform.h" +#include "DetectorsBase/MatLayerCylSet.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsRaw/HBFUtils.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "CommonUtils/NameConf.h" +#include "TPCBase/RDHUtils.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUO2InterfaceQA.h" +#include "GPUO2Interface.h" +#include "CalibdEdxContainer.h" +#include "GPUNewCalibValues.h" +#include "TPCPadGainCalib.h" +#include "TPCZSLinkMapping.h" +#include "display/GPUDisplayInterface.h" +#include "TPCBase/Sector.h" +#include "TPCBase/Utils.h" +#include "TPCBase/CDBInterface.h" +#include "TPCCalibration/VDriftHelper.h" +#include "CorrectionMapsHelper.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "Algorithm/Parser.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsTRD/RecoInputContainer.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/GeometryFlat.h" +#include "ITSBase/GeometryTGeo.h" +#include "CommonUtils/VerbosityConfig.h" +#include "CommonUtils/DebugStreamer.h" +#include +#include // for make_shared +#include +#include +#include +#include +#include +#include +#include +#include +#include "GPUReconstructionConvert.h" +#include "DetectorsRaw/RDHUtils.h" +#include +#include +#include +#include +#include +#include + +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Tracker.h" +#include "ITStracking/TrackerTraits.h" +#include "ITStracking/Vertexer.h" +#include "ITStracking/VertexerTraits.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsCalibration/MeanVertexObject.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/PhysTrigger.h" +#include "CommonDataFormat/IRFrame.h" +#include "ITSReconstruction/FastMultEst.h" +#include "ITSReconstruction/FastMultEstConfig.h" + +using namespace o2::framework; +using namespace o2::header; +using namespace o2::gpu; +using namespace o2::base; +using namespace o2::dataformats; + +namespace o2::gpu +{ + +int GPURecoWorkflowSpec::runITSTracking(o2::framework::ProcessingContext& pc) +{ + using Vertex = o2::dataformats::Vertex>; + + auto compClusters = pc.inputs().get>("compClusters"); + gsl::span patterns = pc.inputs().get>("patterns"); + gsl::span physTriggers; + std::vector fromTRD; + if (mSpecConfig.itsTriggerType == 2) { // use TRD triggers + o2::InteractionRecord ir{0, pc.services().get().firstTForbit}; + auto trdTriggers = pc.inputs().get>("phystrig"); + for (const auto& trig : trdTriggers) { + if (trig.getBCData() >= ir && trig.getNumberOfTracklets()) { + ir = trig.getBCData(); + fromTRD.emplace_back(o2::itsmft::PhysTrigger{ir, 0}); + } + } + physTriggers = gsl::span(fromTRD.data(), fromTRD.size()); + } else if (mSpecConfig.itsTriggerType == 1) { // use Phys triggers from ITS stream + physTriggers = pc.inputs().get>("phystrig"); + } + + auto rofsinput = pc.inputs().get>("ROframes"); + + auto& rofs = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0, Lifetime::Timeframe}, rofsinput.begin(), rofsinput.end()); + auto& irFrames = pc.outputs().make>(Output{"ITS", "IRFRAMES", 0, Lifetime::Timeframe}); + irFrames.reserve(rofs.size()); + + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); // RS: this should come from CCDB + int nBCPerTF = alpParams.roFrameLengthInBC; + + LOG(info) << "ITSTracker pulled " << compClusters.size() << " clusters, " << rofs.size() << " RO frames"; + + const dataformats::MCTruthContainer* labels = nullptr; + gsl::span mc2rofs; + if (mSpecConfig.processMC) { + labels = pc.inputs().get*>("itsmclabels").release(); + // get the array as read-only span, a snapshot is sent forward + pc.outputs().snapshot(Output{"ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe}, pc.inputs().get>("ITSMC2ROframes")); + LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; + } + + auto& allClusIdx = pc.outputs().make>(Output{"ITS", "TRACKCLSID", 0, Lifetime::Timeframe}); + auto& allTracks = pc.outputs().make>(Output{"ITS", "TRACKS", 0, Lifetime::Timeframe}); + auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0, Lifetime::Timeframe}); + auto& vertices = pc.outputs().make>(Output{"ITS", "VERTICES", 0, Lifetime::Timeframe}); + + // MC + static pmr::vector dummyMCLabTracks, dummyMCLabVerts; + auto& allTrackLabels = mSpecConfig.processMC ? pc.outputs().make>(Output{"ITS", "TRACKSMCTR", 0, Lifetime::Timeframe}) : dummyMCLabTracks; + auto& allVerticesLabels = mSpecConfig.processMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCTR", 0, Lifetime::Timeframe}) : dummyMCLabVerts; + + std::uint32_t roFrame = 0; + + bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS); + LOG(info) << "ITSTracker RO: continuous=" << continuous; + + if (mSpecConfig.itsOverrBeamEst) { + mITSTimeFrame->setBeamPosition(mMeanVertex->getX(), + mMeanVertex->getY(), + mMeanVertex->getSigmaY2(), + mITSTracker->getParameters()[0].LayerResolution[0], + mITSTracker->getParameters()[0].SystErrorY2[0]); + } + + mITSTracker->setBz(o2::base::Propagator::Instance()->getNominalBz()); + + gsl::span::iterator pattIt = patterns.begin(); + + gsl::span rofspan(rofs); + mITSTimeFrame->loadROFrameData(rofspan, compClusters, pattIt, mITSDict, labels); + pattIt = patterns.begin(); + std::vector savedROF; + auto logger = [&](std::string s) { LOG(info) << s; }; + auto errorLogger = [&](std::string s) { LOG(error) << s; }; + + o2::its::FastMultEst multEst; // mult estimator + std::vector processingMask; + int cutVertexMult{0}, cutRandomMult = int(rofs.size()) - multEst.selectROFs(rofs, compClusters, physTriggers, processingMask); + mITSTimeFrame->setMultiplicityCutMask(processingMask); + float vertexerElapsedTime{0.f}; + if (mITSRunVertexer) { + // Run seeding vertexer + vertROFvec.reserve(rofs.size()); + vertexerElapsedTime = mITSVertexer->clustersToVertices(logger); + } else { // cosmics + mITSTimeFrame->resetRofPV(); + } + const auto& multEstConf = o2::its::FastMultEstConfig::Instance(); // parameters for mult estimation and cuts + for (auto iRof{0}; iRof < rofspan.size(); ++iRof) { + std::vector vtxVecLoc; + auto& vtxROF = vertROFvec.emplace_back(rofspan[iRof]); + vtxROF.setFirstEntry(vertices.size()); + if (mITSRunVertexer) { + auto vtxSpan = mITSTimeFrame->getPrimaryVertices(iRof); + vtxROF.setNEntries(vtxSpan.size()); + bool selROF = vtxSpan.size() == 0; + for (auto iV{0}; iV < vtxSpan.size(); ++iV) { + auto& v = vtxSpan[iV]; + if (multEstConf.isVtxMultCutRequested() && !multEstConf.isPassingVtxMultCut(v.getNContributors())) { + continue; // skip vertex of unwanted multiplicity + } + selROF = true; + vertices.push_back(v); + if (mSpecConfig.processMC) { + auto vLabels = mITSTimeFrame->getPrimaryVerticesLabels(iRof)[iV]; + allVerticesLabels.reserve(allVerticesLabels.size() + vLabels.size()); + std::copy(vLabels.begin(), vLabels.end(), std::back_inserter(allVerticesLabels)); + } + } + if (processingMask[iRof] && !selROF) { // passed selection in clusters and not in vertex multiplicity + LOG(debug) << fmt::format("ROF {} rejected by the vertex multiplicity selection [{},{}]", + iRof, + multEstConf.cutMultVtxLow, + multEstConf.cutMultVtxHigh); + processingMask[iRof] = selROF; + cutVertexMult++; + } + } else { // cosmics + vtxVecLoc.emplace_back(Vertex()); + vtxVecLoc.back().setNContributors(1); + vtxROF.setNEntries(vtxVecLoc.size()); + for (auto& v : vtxVecLoc) { + vertices.push_back(v); + } + mITSTimeFrame->addPrimaryVertices(vtxVecLoc); + } + } + LOG(info) << fmt::format(" - rejected {}/{} ROFs: random/mult.sel:{} (seed {}), vtx.sel:{}", cutRandomMult + cutVertexMult, rofspan.size(), cutRandomMult, multEst.lastRandomSeed, cutVertexMult); + LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} vertices found in {} ROFs", vertexerElapsedTime, mITSTimeFrame->getPrimaryVerticesNum(), rofspan.size()); + + if (mSpecConfig.itsOverrBeamEst) { + LOG(info) << fmt::format(" - Beam position set to: {}, {} from meanvertex object", mITSTimeFrame->getBeamX(), mITSTimeFrame->getBeamY()); + } else { + LOG(info) << fmt::format(" - Beam position computed for the TF: {}, {}", mITSTimeFrame->getBeamX(), mITSTimeFrame->getBeamY()); + } + if (mITSCosmicsProcessing && compClusters.size() > 1500 * rofspan.size()) { + LOG(error) << "Cosmics processing was requested with an average detector occupancy exceeding 1.e-7, skipping TF processing."; + } else { + + mITSTimeFrame->setMultiplicityCutMask(processingMask); + // Run CA tracker + mITSTracker->clustersToTracks(logger, errorLogger); + size_t totTracks{mITSTimeFrame->getNumberOfTracks()}, totClusIDs{mITSTimeFrame->getNumberOfUsedClusters()}; + allTracks.reserve(totTracks); + allClusIdx.reserve(totClusIDs); + + if (mITSTimeFrame->hasBogusClusters()) { + LOG(warning) << fmt::format(" - The processed timeframe had {} clusters with wild z coordinates, check the dictionaries", mITSTimeFrame->hasBogusClusters()); + } + + for (unsigned int iROF{0}; iROF < rofs.size(); ++iROF) { + auto& rof{rofs[iROF]}; + auto& tracks = mITSTimeFrame->getTracks(iROF); + auto number{tracks.size()}; + auto first{allTracks.size()}; + int offset = -rof.getFirstEntry(); // cluster entry!!! + rof.setFirstEntry(first); + rof.setNEntries(number); + + if (processingMask[iROF]) { + irFrames.emplace_back(rof.getBCData(), rof.getBCData() + nBCPerTF - 1).info = tracks.size(); + } + + allTrackLabels.reserve(mITSTimeFrame->getTracksLabel(iROF).size()); // should be 0 if not MC + std::copy(mITSTimeFrame->getTracksLabel(iROF).begin(), mITSTimeFrame->getTracksLabel(iROF).end(), std::back_inserter(allTrackLabels)); + // Some conversions that needs to be moved in the tracker internals + for (unsigned int iTrk{0}; iTrk < tracks.size(); ++iTrk) { + auto& trc{tracks[iTrk]}; + trc.setFirstClusterEntry(allClusIdx.size()); // before adding tracks, create final cluster indices + int ncl = trc.getNumberOfClusters(), nclf = 0; + for (int ic = o2::its::TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! + auto clid = trc.getClusterIndex(ic); + if (clid >= 0) { + allClusIdx.push_back(clid); + nclf++; + } + } + assert(ncl == nclf); + allTracks.emplace_back(trc); + } + } + LOGP(info, "ITSTracker pushed {} tracks and {} vertices", allTracks.size(), vertices.size()); + if (mSpecConfig.processMC) { + LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); + LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); + } + } + return 0; +} + +void GPURecoWorkflowSpec::initFunctionITS(InitContext& ic) +{ + std::transform(mITSMode.begin(), mITSMode.end(), mITSMode.begin(), [](unsigned char c) { return std::tolower(c); }); + o2::its::VertexerTraits* vtxTraits = nullptr; + o2::its::TrackerTraits* trkTraits = nullptr; + mGPUReco->GetITSTraits(trkTraits, vtxTraits, mITSTimeFrame); + mITSVertexer = std::make_unique(vtxTraits); + mITSTracker = std::make_unique(trkTraits); + mITSVertexer->adoptTimeFrame(*mITSTimeFrame); + mITSTracker->adoptTimeFrame(*mITSTimeFrame); + mITSRunVertexer = true; + mITSCosmicsProcessing = false; + std::vector trackParams; + + if (mITSMode == "async") { + trackParams.resize(3); + for (auto& param : trackParams) { + param.ZBins = 64; + param.PhiBins = 32; + } + trackParams[1].TrackletMinPt = 0.2f; + trackParams[1].CellDeltaTanLambdaSigma *= 2.; + trackParams[2].TrackletMinPt = 0.1f; + trackParams[2].CellDeltaTanLambdaSigma *= 4.; + trackParams[2].MinTrackLength = 4; + LOG(info) << "Initializing tracker in async. phase reconstruction with " << trackParams.size() << " passes"; + } else if (mITSMode == "sync") { + trackParams.resize(1); + trackParams[0].ZBins = 64; + trackParams[0].PhiBins = 32; + trackParams[0].MinTrackLength = 4; + LOG(info) << "Initializing tracker in sync. phase reconstruction with " << trackParams.size() << " passes"; + } else if (mITSMode == "cosmics") { + mITSCosmicsProcessing = true; + mITSRunVertexer = false; + trackParams.resize(1); + trackParams[0].MinTrackLength = 4; + trackParams[0].CellDeltaTanLambdaSigma *= 10; + trackParams[0].PhiBins = 4; + trackParams[0].ZBins = 16; + trackParams[0].PVres = 1.e5f; + trackParams[0].MaxChi2ClusterAttachment = 60.; + trackParams[0].MaxChi2NDF = 40.; + trackParams[0].TrackletsPerClusterLimit = 100.; + trackParams[0].CellsPerClusterLimit = 100.; + LOG(info) << "Initializing tracker in reconstruction for cosmics with " << trackParams.size() << " passes"; + } else { + throw std::runtime_error(fmt::format("Unsupported ITS tracking mode {:s} ", mITSMode)); + } + + for (auto& params : trackParams) { + params.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; + } + mITSTracker->setParameters(trackParams); +} + +void GPURecoWorkflowSpec::finaliseCCDBITS(ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } + // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level + if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { + LOG(info) << "Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + return; + } + if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { + LOGP(info, "mean vertex acquired"); + if (obj) { + mMeanVertex = (const o2::dataformats::MeanVertexObject*)obj; + } + return; + } +} + +bool GPURecoWorkflowSpec::fetchCalibsCCDBITS(ProcessingContext& pc) +{ + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + pc.inputs().get("itscldict"); // just to trigger the finaliseCCDB + pc.inputs().get*>("itsalppar"); + mITSVertexer->getGlobalConfiguration(); + mITSTracker->getGlobalConfiguration(); + if (mSpecConfig.itsOverrBeamEst) { + pc.inputs().get("meanvtx"); + } + } + return false; +} + +} // namespace o2::gpu diff --git a/GPU/Workflow/src/GPUWorkflowInternal.h b/GPU/Workflow/src/GPUWorkflowInternal.h new file mode 100644 index 0000000000000..513d2ec6f0e28 --- /dev/null +++ b/GPU/Workflow/src/GPUWorkflowInternal.h @@ -0,0 +1,102 @@ +// 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 GPUWorkflowInternal.h +/// @author David Rohr + +#ifndef O2_GPU_GPUWORKFLOWINTERNAL_H +#define O2_GPU_GPUWORKFLOWINTERNAL_H + +#include "GPUDataTypes.h" +#include +#include +#include +#include +#include +#include + +namespace o2::gpu +{ +namespace gpurecoworkflow_internals +{ + +struct GPURecoWorkflowSpec_TPCZSBuffers { + std::vector Pointers[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; + std::vector Sizes[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; + const void** Pointers2[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; + const unsigned int* Sizes2[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; +}; + +struct GPURecoWorkflow_QueueObject { + GPURecoWorkflowSpec_TPCZSBuffers tpcZSmeta; + GPUTrackingInOutZS tpcZS; + GPUSettingsTF tfSettings; + GPUTrackingInOutPointers ptrs; + o2::framework::DataProcessingHeader::StartTime timeSliceId; + + unsigned long mTFId; + + bool jobSubmitted = false; + bool jobFinished = false; + int jobReturnValue = 0; + std::mutex jobFinishedMutex; + std::condition_variable jobFinishedNotify; + bool jobInputFinal = false; + std::mutex jobInputFinalMutex; + std::condition_variable jobInputFinalNotify; + GPUTrackingInOutPointers* jobPtrs = nullptr; + GPUInterfaceOutputs* jobOutputRegions = nullptr; + std::unique_ptr jobInputUpdateCallback = nullptr; +}; + +struct GPURecoWorkflowSpec_PipelineInternals { + std::mutex mutexDecodeInput; + + fair::mq::Device* fmqDevice = nullptr; + + fair::mq::State fmqState = fair::mq::State::Undefined; + volatile bool endOfStreamReceived = false; + volatile bool runStarted = false; + volatile bool shouldTerminate = false; + std::mutex stateMutex; + std::condition_variable stateNotify; + + std::thread receiveThread; + + struct pipelineWorkerStruct { + std::thread thread; + std::queue inputQueue; + std::mutex inputQueueMutex; + std::condition_variable inputQueueNotify; + }; + std::array workers; + + std::queue> pipelineQueue; + std::mutex queueMutex; + std::condition_variable queueNotify; + + std::queue completionPolicyQueue; + volatile bool pipelineSenderTerminating = false; + std::mutex completionPolicyMutex; + std::condition_variable completionPolicyNotify; + + unsigned long mNTFReceived = 0; + + bool mayInject = true; + unsigned long mayInjectTFId = 0; + std::mutex mayInjectMutex; + std::condition_variable mayInjectCondition; +}; + +} // namespace gpurecoworkflow_internals +} // namespace o2::gpu + +#endif diff --git a/GPU/Workflow/src/GPUWorkflowPipeline.cxx b/GPU/Workflow/src/GPUWorkflowPipeline.cxx new file mode 100644 index 0000000000000..c81c5a5478d4e --- /dev/null +++ b/GPU/Workflow/src/GPUWorkflowPipeline.cxx @@ -0,0 +1,405 @@ +// 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 GPUWorkflowPipeline.cxx +/// @author David Rohr + +#include "GPUWorkflow/GPUWorkflowSpec.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUO2Interface.h" +#include "GPUDataTypes.h" +#include "GPUSettings.h" +#include "GPUWorkflowInternal.h" + +#include "Framework/WorkflowSpec.h" // o2::framework::mergeInputs +#include "Framework/DataRefUtils.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/SerializationMethods.h" +#include "Framework/Logger.h" +#include "Framework/CallbackService.h" +#include "Framework/DataProcessingContext.h" +#include "Framework/RawDeviceService.h" + +#include +#include +#include + +using namespace o2::framework; +using namespace o2::header; +using namespace o2::gpu; +using namespace o2::base; +using namespace o2::dataformats; +using namespace o2::gpu::gpurecoworkflow_internals; + +namespace o2::gpu +{ + +static const std::string GPURecoWorkflowSpec_FMQCallbackKey = "GPURecoWorkflowSpec_FMQCallbackKey"; + +struct pipelinePrepareMessage { + static constexpr size_t MAGIC_WORD = 0X8473957353424134; + size_t magicWord = MAGIC_WORD; + DataProcessingHeader::StartTime timeSliceId; + GPUSettingsTF tfSettings; + size_t pointerCounts[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; + size_t pointersTotal; + bool flagEndOfStream; +}; + +void GPURecoWorkflowSpec::initPipeline(o2::framework::InitContext& ic) +{ + if (mSpecConfig.enableDoublePipeline == 1) { + mPipeline->fmqDevice = ic.services().get().device(); + mPipeline->fmqDevice->SubscribeToStateChange(GPURecoWorkflowSpec_FMQCallbackKey, [this](fair::mq::State s) { receiveFMQStateCallback(s); }); + mPolicyOrder = [this](o2::framework::DataProcessingHeader::StartTime timeslice) { + std::unique_lock lk(mPipeline->completionPolicyMutex); + mPipeline->completionPolicyNotify.wait(lk, [pipeline = mPipeline.get()] { return pipeline->pipelineSenderTerminating || !pipeline->completionPolicyQueue.empty(); }); + if (mPipeline->completionPolicyQueue.front() == timeslice) { + mPipeline->completionPolicyQueue.pop(); + return true; + } + return false; + }; + mPipeline->receiveThread = std::thread([this]() { RunReceiveThread(); }); + for (unsigned int i = 0; i < mPipeline->workers.size(); i++) { + mPipeline->workers[i].thread = std::thread([this, i]() { RunWorkerThread(i); }); + } + } +} + +void GPURecoWorkflowSpec::RunWorkerThread(int id) +{ + LOG(debug) << "Running pipeline worker " << id; + auto& workerContext = mPipeline->workers[id]; + while (!mPipeline->shouldTerminate) { + GPURecoWorkflow_QueueObject* context; + { + std::unique_lock lk(workerContext.inputQueueMutex); + workerContext.inputQueueNotify.wait(lk, [this, &workerContext]() { return mPipeline->shouldTerminate || !workerContext.inputQueue.empty(); }); + if (workerContext.inputQueue.empty()) { + break; + } + context = workerContext.inputQueue.front(); + workerContext.inputQueue.pop(); + } + context->jobReturnValue = runMain(nullptr, context->jobPtrs, context->jobOutputRegions, id, context->jobInputUpdateCallback.get()); + { + std::lock_guard lk(context->jobFinishedMutex); + context->jobFinished = true; + } + context->jobFinishedNotify.notify_one(); + } +} + +void GPURecoWorkflowSpec::enqueuePipelinedJob(GPUTrackingInOutPointers* ptrs, GPUInterfaceOutputs* outputRegions, GPURecoWorkflow_QueueObject* context, bool inputFinal) +{ + { + std::unique_lock lk(mPipeline->mayInjectMutex); + mPipeline->mayInjectCondition.wait(lk, [this, context]() { return mPipeline->mayInject && mPipeline->mayInjectTFId == context->mTFId; }); + mPipeline->mayInjectTFId++; + mPipeline->mayInject = false; + } + context->jobSubmitted = true; + context->jobInputFinal = inputFinal; + context->jobPtrs = ptrs; + context->jobOutputRegions = outputRegions; + + context->jobInputUpdateCallback = std::make_unique(); + + if (!inputFinal) { + context->jobInputUpdateCallback->callback = [context](GPUTrackingInOutPointers*& data, GPUInterfaceOutputs*& outputs) { + std::unique_lock lk(context->jobInputFinalMutex); + context->jobInputFinalNotify.wait(lk, [context]() { return context->jobInputFinal; }); + data = context->jobPtrs; + outputs = context->jobOutputRegions; + }; + } + context->jobInputUpdateCallback->notifyCallback = [this]() { + { + std::lock_guard lk(mPipeline->mayInjectMutex); + mPipeline->mayInject = true; + } + mPipeline->mayInjectCondition.notify_one(); + }; + + mNextThreadIndex = (mNextThreadIndex + 1) % 2; + + { + std::lock_guard lk(mPipeline->workers[mNextThreadIndex].inputQueueMutex); + mPipeline->workers[mNextThreadIndex].inputQueue.emplace(context); + } + mPipeline->workers[mNextThreadIndex].inputQueueNotify.notify_one(); +} + +void GPURecoWorkflowSpec::finalizeInputPipelinedJob(GPUTrackingInOutPointers* ptrs, GPUInterfaceOutputs* outputRegions, GPURecoWorkflow_QueueObject* context) +{ + { + std::lock_guard lk(context->jobInputFinalMutex); + context->jobPtrs = ptrs; + context->jobOutputRegions = outputRegions; + context->jobInputFinal = true; + } + context->jobInputFinalNotify.notify_one(); +} + +int GPURecoWorkflowSpec::handlePipeline(ProcessingContext& pc, GPUTrackingInOutPointers& ptrs, GPURecoWorkflowSpec_TPCZSBuffers& tpcZSmeta, o2::gpu::GPUTrackingInOutZS& tpcZS, std::unique_ptr& context) +{ + mPipeline->runStarted = true; + mPipeline->stateNotify.notify_all(); + + auto* device = pc.services().get().device(); + const auto& tinfo = pc.services().get(); + if (mSpecConfig.enableDoublePipeline == 1) { + std::unique_lock lk(mPipeline->queueMutex); + mPipeline->queueNotify.wait(lk, [this] { return !mPipeline->pipelineQueue.empty(); }); + context = std::move(mPipeline->pipelineQueue.front()); + mPipeline->pipelineQueue.pop(); + lk.unlock(); + + if (context->timeSliceId != tinfo.timeslice) { + LOG(fatal) << "Prepare message for incorrect time frame received, time frames seem out of sync"; + } + + tpcZSmeta = std::move(context->tpcZSmeta); + tpcZS = context->tpcZS; + ptrs.tpcZS = &tpcZS; + } + if (mSpecConfig.enableDoublePipeline == 2) { + auto prepareBuffer = pc.outputs().make>(Output{gDataOriginGPU, "PIPELINEPREPARE", 0, Lifetime::Timeframe}, 0u); + + size_t ptrsTotal = 0; + const void* firstPtr = nullptr; + for (unsigned int i = 0; i < GPUTrackingInOutZS::NSLICES; i++) { + for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { + if (firstPtr == nullptr && ptrs.tpcZS->slice[i].count[j]) { + firstPtr = ptrs.tpcZS->slice[i].zsPtr[j][0]; + } + ptrsTotal += ptrs.tpcZS->slice[i].count[j]; + } + } + + size_t prepareBufferSize = sizeof(pipelinePrepareMessage) + ptrsTotal * sizeof(size_t) * 4; + std::vector messageBuffer(prepareBufferSize / sizeof(size_t)); + pipelinePrepareMessage& preMessage = *(pipelinePrepareMessage*)messageBuffer.data(); + preMessage.magicWord = preMessage.MAGIC_WORD; + preMessage.timeSliceId = tinfo.timeslice; + preMessage.pointersTotal = ptrsTotal; + preMessage.flagEndOfStream = false; + memcpy((void*)&preMessage.tfSettings, (const void*)ptrs.settingsTF, sizeof(preMessage.tfSettings)); + + size_t* ptrBuffer = messageBuffer.data() + sizeof(preMessage) / sizeof(size_t); + size_t ptrsCopied = 0; + int lastRegion = -1; + for (unsigned int i = 0; i < GPUTrackingInOutZS::NSLICES; i++) { + for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { + preMessage.pointerCounts[i][j] = ptrs.tpcZS->slice[i].count[j]; + for (unsigned int k = 0; k < ptrs.tpcZS->slice[i].count[j]; k++) { + const void* curPtr = ptrs.tpcZS->slice[i].zsPtr[j][k]; + bool regionFound = lastRegion != -1 && (size_t)curPtr >= (size_t)mRegionInfos[lastRegion].ptr && (size_t)curPtr < (size_t)mRegionInfos[lastRegion].ptr + mRegionInfos[lastRegion].size; + if (!regionFound) { + for (unsigned int l = 0; l < mRegionInfos.size(); l++) { + if ((size_t)curPtr >= (size_t)mRegionInfos[l].ptr && (size_t)curPtr < (size_t)mRegionInfos[l].ptr + mRegionInfos[l].size) { + lastRegion = l; + regionFound = true; + break; + } + } + } + if (!regionFound) { + LOG(fatal) << "Found a TPC ZS pointer outside of shared memory"; + } + ptrBuffer[ptrsCopied + k] = (size_t)curPtr - (size_t)mRegionInfos[lastRegion].ptr; + ptrBuffer[ptrsTotal + ptrsCopied + k] = ptrs.tpcZS->slice[i].nZSPtr[j][k]; + ptrBuffer[2 * ptrsTotal + ptrsCopied + k] = mRegionInfos[lastRegion].managed; + ptrBuffer[3 * ptrsTotal + ptrsCopied + k] = mRegionInfos[lastRegion].id; + } + ptrsCopied += ptrs.tpcZS->slice[i].count[j]; + } + } + + auto channel = device->GetChannels().find("gpu-prepare-channel"); + fair::mq::MessagePtr payload(device->NewMessage()); + LOG(info) << "Sending gpu-reco-workflow prepare message of size " << prepareBufferSize; + payload->Rebuild(messageBuffer.data(), prepareBufferSize, nullptr, nullptr); + channel->second[0].Send(payload); + return 2; + } + return 0; +} + +void GPURecoWorkflowSpec::handlePipelineEndOfStream(EndOfStreamContext& ec) +{ + if (mSpecConfig.enableDoublePipeline == 1) { + mPipeline->endOfStreamReceived = true; + mPipeline->stateNotify.notify_all(); + } + if (mSpecConfig.enableDoublePipeline == 2) { + auto* device = ec.services().get().device(); + pipelinePrepareMessage preMessage; + preMessage.flagEndOfStream = true; + auto channel = device->GetChannels().find("gpu-prepare-channel"); + fair::mq::MessagePtr payload(device->NewMessage()); + LOG(info) << "Sending end-of-stream message over out-of-bands channel"; + payload->Rebuild(&preMessage, sizeof(preMessage), nullptr, nullptr); + channel->second[0].Send(payload); + } +} + +void GPURecoWorkflowSpec::receiveFMQStateCallback(fair::mq::State newState) +{ + { + std::lock_guard lk(mPipeline->stateMutex); + if (mPipeline->fmqState != fair::mq::State::Running && newState == fair::mq::State::Running) { + mPipeline->endOfStreamReceived = false; + } + mPipeline->fmqState = newState; + if (newState == fair::mq::State::Exiting) { + mPipeline->fmqDevice->UnsubscribeFromStateChange(GPURecoWorkflowSpec_FMQCallbackKey); + } + } + mPipeline->stateNotify.notify_all(); +} + +void GPURecoWorkflowSpec::RunReceiveThread() +{ + auto* device = mPipeline->fmqDevice; + while (!mPipeline->shouldTerminate) { + bool received = false; + int recvTimeot = 1000; + fair::mq::MessagePtr msg; + LOG(debug) << "Waiting for out of band message"; + do { + { + std::unique_lock lk(mPipeline->stateMutex); + mPipeline->stateNotify.wait(lk, [this]() { return (mPipeline->fmqState == fair::mq::State::Running && !mPipeline->endOfStreamReceived) || mPipeline->shouldTerminate; }); // Do not check mPipeline->fmqDevice->NewStatePending() since we wait for EndOfStream! + } + if (mPipeline->shouldTerminate) { + break; + } + try { + msg = device->NewMessageFor("gpu-prepare-channel", 0, 0); + do { + received = device->Receive(msg, "gpu-prepare-channel", 0, recvTimeot) > 0; + } while (!received && !mPipeline->shouldTerminate); + } catch (...) { + usleep(1000000); + } + } while (!received && !mPipeline->shouldTerminate); + if (mPipeline->shouldTerminate) { + break; + } + if (msg->GetSize() < sizeof(pipelinePrepareMessage)) { + LOG(fatal) << "Received prepare message of invalid size " << msg->GetSize() << " < " << sizeof(pipelinePrepareMessage); + } + const pipelinePrepareMessage* m = (const pipelinePrepareMessage*)msg->GetData(); + if (m->magicWord != m->MAGIC_WORD) { + LOG(fatal) << "Prepare message corrupted, invalid magic word"; + } + if (m->flagEndOfStream) { + LOG(info) << "Received end-of-stream from out-of-band channel"; + std::lock_guard lk(mPipeline->stateMutex); + mPipeline->endOfStreamReceived = true; + mPipeline->mNTFReceived = 0; + mPipeline->runStarted = false; + continue; + } + + { + std::lock_guard lk(mPipeline->completionPolicyMutex); + mPipeline->completionPolicyQueue.emplace(m->timeSliceId); + } + mPipeline->completionPolicyNotify.notify_one(); + + { + std::unique_lock lk(mPipeline->stateMutex); + mPipeline->stateNotify.wait(lk, [this]() { return (mPipeline->runStarted && !mPipeline->endOfStreamReceived) || mPipeline->shouldTerminate; }); + if (!mPipeline->runStarted) { + continue; + } + } + + auto context = std::make_unique(); + context->timeSliceId = m->timeSliceId; + context->tfSettings = m->tfSettings; + + size_t ptrsCopied = 0; + size_t* ptrBuffer = (size_t*)msg->GetData() + sizeof(pipelinePrepareMessage) / sizeof(size_t); + context->tpcZSmeta.Pointers[0][0].resize(m->pointersTotal); + context->tpcZSmeta.Sizes[0][0].resize(m->pointersTotal); + int lastRegion = -1; + for (unsigned int i = 0; i < GPUTrackingInOutZS::NSLICES; i++) { + for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { + context->tpcZS.slice[i].count[j] = m->pointerCounts[i][j]; + for (unsigned int k = 0; k < context->tpcZS.slice[i].count[j]; k++) { + bool regionManaged = ptrBuffer[2 * m->pointersTotal + ptrsCopied + k]; + size_t regionId = ptrBuffer[3 * m->pointersTotal + ptrsCopied + k]; + bool regionFound = lastRegion != -1 && mRegionInfos[lastRegion].managed == regionManaged && mRegionInfos[lastRegion].id == regionId; + if (!regionFound) { + for (unsigned int l = 0; l < mRegionInfos.size(); l++) { + if (mRegionInfos[l].managed == regionManaged && mRegionInfos[l].id == regionId) { + lastRegion = l; + regionFound = true; + break; + } + } + } + if (!regionFound) { + LOG(fatal) << "Received ZS Ptr for SHM region (managed " << (int)regionManaged << ", id " << regionId << "), which was not registered for us"; + } + context->tpcZSmeta.Pointers[0][0][ptrsCopied + k] = (void*)(ptrBuffer[ptrsCopied + k] + (size_t)mRegionInfos[lastRegion].ptr); + context->tpcZSmeta.Sizes[0][0][ptrsCopied + k] = ptrBuffer[m->pointersTotal + ptrsCopied + k]; + } + context->tpcZS.slice[i].zsPtr[j] = context->tpcZSmeta.Pointers[0][0].data() + ptrsCopied; + context->tpcZS.slice[i].nZSPtr[j] = context->tpcZSmeta.Sizes[0][0].data() + ptrsCopied; + ptrsCopied += context->tpcZS.slice[i].count[j]; + } + } + context->ptrs.tpcZS = &context->tpcZS; + context->ptrs.settingsTF = &context->tfSettings; + context->mTFId = mPipeline->mNTFReceived; + if (mPipeline->mNTFReceived++ >= mPipeline->workers.size()) { // Do not inject the first workers.size() TFs, since we need a first round of calib updates from DPL before starting + enqueuePipelinedJob(&context->ptrs, nullptr, context.get(), false); + } + { + std::lock_guard lk(mPipeline->queueMutex); + mPipeline->pipelineQueue.emplace(std::move(context)); + } + mPipeline->queueNotify.notify_one(); + } + mPipeline->pipelineSenderTerminating = true; + mPipeline->completionPolicyNotify.notify_one(); +} + +void GPURecoWorkflowSpec::ExitPipeline() +{ + if (mSpecConfig.enableDoublePipeline == 1 && mPipeline->fmqDevice) { + mPipeline->fmqDevice = nullptr; + mPipeline->shouldTerminate = true; + mPipeline->stateNotify.notify_all(); + for (unsigned int i = 0; i < mPipeline->workers.size(); i++) { + mPipeline->workers[i].inputQueueNotify.notify_one(); + } + if (mPipeline->receiveThread.joinable()) { + mPipeline->receiveThread.join(); + } + for (unsigned int i = 0; i < mPipeline->workers.size(); i++) { + if (mPipeline->workers[i].thread.joinable()) { + mPipeline->workers[i].thread.join(); + } + } + } +} + +} // namespace o2::gpu diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 58a1ea70b2638..e308c5143f54c 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -10,7 +10,7 @@ // or submit itself to any jurisdiction. /// @file GPUWorkflowSpec.cxx -/// @author Matthias Richter +/// @author Matthias Richter, David Rohr /// @since 2018-04-18 /// @brief Processor spec for running TPC CA tracking @@ -27,6 +27,7 @@ #include "Framework/Logger.h" #include "Framework/CallbackService.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/RawDeviceService.h" #include "DataFormatsTPC/TPCSectorHeader.h" #include "DataFormatsTPC/ClusterNative.h" #include "DataFormatsTPC/CompressedClusters.h" @@ -70,20 +71,14 @@ #include "TRDBase/Geometry.h" #include "TRDBase/GeometryFlat.h" #include "ITSBase/GeometryTGeo.h" -#include "CommonUtils/VerbosityConfig.h" #include "CommonUtils/DebugStreamer.h" -#include -#include // for make_shared -#include -#include -#include -#include -#include -#include -#include -#include #include "GPUReconstructionConvert.h" #include "DetectorsRaw/RDHUtils.h" +#include "ITStracking/Tracker.h" +#include "ITStracking/Vertexer.h" +#include "GPUWorkflowInternal.h" +// #include "Framework/ThreadPool.h" + #include #include #include @@ -91,31 +86,28 @@ #include #include -// Includes needed for ITS tracking - // TODO: Move ITS tracking control code to an extra class, and move these includes there -#include "ITStracking/TimeFrame.h" -#include "ITStracking/Tracker.h" -#include "ITStracking/TrackerTraits.h" -#include "ITStracking/Vertexer.h" -#include "ITStracking/VertexerTraits.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "DataFormatsCalibration/MeanVertexObject.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITSMFT/PhysTrigger.h" -#include "CommonDataFormat/IRFrame.h" -#include "ITSReconstruction/FastMultEst.h" -#include "ITSReconstruction/FastMultEstConfig.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include using namespace o2::framework; using namespace o2::header; using namespace o2::gpu; using namespace o2::base; using namespace o2::dataformats; +using namespace o2::gpu::gpurecoworkflow_internals; namespace o2::gpu { -GPURecoWorkflowSpec::GPURecoWorkflowSpec(GPURecoWorkflowSpec::CompletionPolicyData* policyData, Config const& specconfig, std::vector const& tpcsectors, unsigned long tpcSectorMask, std::shared_ptr& ggr) : o2::framework::Task(), mPolicyData(policyData), mTPCSectorMask(tpcSectorMask), mTPCSectors(tpcsectors), mSpecConfig(specconfig), mGGR(ggr) +GPURecoWorkflowSpec::GPURecoWorkflowSpec(GPURecoWorkflowSpec::CompletionPolicyData* policyData, Config const& specconfig, std::vector const& tpcsectors, unsigned long tpcSectorMask, std::shared_ptr& ggr, std::function** gPolicyOrder) : o2::framework::Task(), mPolicyData(policyData), mTPCSectorMask(tpcSectorMask), mTPCSectors(tpcsectors), mSpecConfig(specconfig), mGGR(ggr) { if (mSpecConfig.outputCAClusters && !mSpecConfig.caClusterer && !mSpecConfig.decompressTPC) { throw std::runtime_error("inconsistent configuration: cluster output is only possible if CA clusterer is activated"); @@ -125,6 +117,11 @@ GPURecoWorkflowSpec::GPURecoWorkflowSpec(GPURecoWorkflowSpec::CompletionPolicyDa mConfParam.reset(new GPUSettingsO2); mTFSettings.reset(new GPUSettingsTF); mTimer.reset(new TStopwatch); + mPipeline.reset(new GPURecoWorkflowSpec_PipelineInternals); + + if (mSpecConfig.enableDoublePipeline == 1 && gPolicyOrder) { + *gPolicyOrder = &mPolicyOrder; + } } GPURecoWorkflowSpec::~GPURecoWorkflowSpec() = default; @@ -133,115 +130,126 @@ void GPURecoWorkflowSpec::init(InitContext& ic) { GRPGeomHelper::instance().setRequest(mGGR); GPUO2InterfaceConfiguration& config = *mConfig.get(); - { - mParser = std::make_unique>(); - mTracker = std::make_unique(); - - // Create configuration object and fill settings - mConfig->configGRP.solenoidBz = 0; - mTFSettings->hasSimStartOrbit = 1; - auto& hbfu = o2::raw::HBFUtils::Instance(); - mTFSettings->simStartOrbit = hbfu.getFirstIRofTF(o2::InteractionRecord(0, hbfu.orbitFirstSampled)).orbit; - - *mConfParam = mConfig->ReadConfigurableParam(); - if (mConfParam->display) { - mDisplayFrontend.reset(GPUDisplayFrontendInterface::getFrontend(mConfig->configDisplay.displayFrontend.c_str())); - mConfig->configProcessing.eventDisplay = mDisplayFrontend.get(); - if (mConfig->configProcessing.eventDisplay != nullptr) { - LOG(info) << "Event display enabled"; - } else { - throw std::runtime_error("GPU Event Display frontend could not be created!"); - } - } - mAutoSolenoidBz = mConfParam->solenoidBz == -1e6f; - mAutoContinuousMaxTimeBin = mConfig->configGRP.continuousMaxTimeBin == -1; - if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.continuousMaxTimeBin = (256 * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; - } - if (mConfig->configProcessing.deviceNum == -2) { - int myId = ic.services().get().inputTimesliceId; - int idMax = ic.services().get().maxInputTimeslices; - mConfig->configProcessing.deviceNum = myId; - LOG(info) << "GPU device number selected from pipeline id: " << myId << " / " << idMax; - } - if (mConfig->configProcessing.debugLevel >= 3 && mVerbosity == 0) { - mVerbosity = 1; - } - mConfig->configProcessing.runMC = mSpecConfig.processMC; - if (mSpecConfig.outputQA) { - if (!mSpecConfig.processMC && !mConfig->configQA.clusterRejectionHistograms) { - throw std::runtime_error("Need MC information to create QA plots"); - } - if (!mSpecConfig.processMC) { - mConfig->configQA.noMC = true; - } - mConfig->configQA.shipToQC = true; - if (!mConfig->configProcessing.runQA) { - mConfig->configQA.enableLocalOutput = false; - mQATaskMask = (mSpecConfig.processMC ? 15 : 0) | (mConfig->configQA.clusterRejectionHistograms ? 32 : 0); - mConfig->configProcessing.runQA = -mQATaskMask; - } - } - mConfig->configReconstruction.tpc.nWaysOuter = true; - mConfig->configInterface.outputToExternalBuffers = true; - if (mConfParam->synchronousProcessing) { - mConfig->configReconstruction.useMatLUT = false; - } - - // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) - if (mSpecConfig.outputTracks || mSpecConfig.outputCompClusters || mSpecConfig.outputCompClustersFlat) { - mConfig->configWorkflow.steps.set(GPUDataTypes::RecoStep::TPCConversion, - GPUDataTypes::RecoStep::TPCSliceTracking, - GPUDataTypes::RecoStep::TPCMerging); - mConfig->configWorkflow.outputs.set(GPUDataTypes::InOutType::TPCMergedTracks); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, mConfParam->rundEdx == -1 ? !mConfParam->synchronousProcessing : mConfParam->rundEdx); - } - if (mSpecConfig.outputCompClusters || mSpecConfig.outputCompClustersFlat) { - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, true); - } - mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCClusters); - if (mSpecConfig.caClusterer) { // Override some settings if we have raw data as input - mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCRaw); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCClusterFinding, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCClusters, true); - } - if (mSpecConfig.decompressTPC) { - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, false); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCDecompression, true); - mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCCompressedClusters); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCClusters, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, false); - if (mTPCSectorMask != 0xFFFFFFFFF) { - throw std::invalid_argument("Cannot run TPC decompression with a sector mask"); - } + // Create configuration object and fill settings + mConfig->configGRP.solenoidBz = 0; + mTFSettings->hasSimStartOrbit = 1; + auto& hbfu = o2::raw::HBFUtils::Instance(); + mTFSettings->simStartOrbit = hbfu.getFirstIRofTF(o2::InteractionRecord(0, hbfu.orbitFirstSampled)).orbit; + + *mConfParam = mConfig->ReadConfigurableParam(); + if (mConfParam->display) { + mDisplayFrontend.reset(GPUDisplayFrontendInterface::getFrontend(mConfig->configDisplay.displayFrontend.c_str())); + mConfig->configProcessing.eventDisplay = mDisplayFrontend.get(); + if (mConfig->configProcessing.eventDisplay != nullptr) { + LOG(info) << "Event display enabled"; + } else { + throw std::runtime_error("GPU Event Display frontend could not be created!"); } - if (mSpecConfig.runTRDTracking) { - mConfig->configWorkflow.inputs.setBits(GPUDataTypes::InOutType::TRDTracklets, true); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, true); + } + if (mSpecConfig.enableDoublePipeline) { + mConfig->configProcessing.doublePipeline = 1; + } + + mAutoSolenoidBz = mConfParam->solenoidBz == -1e6f; + mAutoContinuousMaxTimeBin = mConfig->configGRP.continuousMaxTimeBin == -1; + if (mAutoContinuousMaxTimeBin) { + mConfig->configGRP.continuousMaxTimeBin = (256 * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + } + if (mConfig->configProcessing.deviceNum == -2) { + int myId = ic.services().get().inputTimesliceId; + int idMax = ic.services().get().maxInputTimeslices; + mConfig->configProcessing.deviceNum = myId; + LOG(info) << "GPU device number selected from pipeline id: " << myId << " / " << idMax; + } + if (mConfig->configProcessing.debugLevel >= 3 && mVerbosity == 0) { + mVerbosity = 1; + } + mConfig->configProcessing.runMC = mSpecConfig.processMC; + if (mSpecConfig.outputQA) { + if (!mSpecConfig.processMC && !mConfig->configQA.clusterRejectionHistograms) { + throw std::runtime_error("Need MC information to create QA plots"); } - if (mSpecConfig.runITSTracking) { - mConfig->configWorkflow.inputs.setBits(GPUDataTypes::InOutType::ITSClusters, true); - mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::ITSTracks, true); - mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::ITSTracking, true); + if (!mSpecConfig.processMC) { + mConfig->configQA.noMC = true; } - if (mSpecConfig.outputSharedClusterMap) { - mConfig->configProcessing.outputSharedClusterMap = true; + mConfig->configQA.shipToQC = true; + if (!mConfig->configProcessing.runQA) { + mConfig->configQA.enableLocalOutput = false; + mQATaskMask = (mSpecConfig.processMC ? 15 : 0) | (mConfig->configQA.clusterRejectionHistograms ? 32 : 0); + mConfig->configProcessing.runQA = -mQATaskMask; } - mConfig->configProcessing.createO2Output = mSpecConfig.outputTracks ? 2 : 0; // Disable O2 TPC track format output if no track output requested + } + mConfig->configReconstruction.tpc.nWaysOuter = true; + mConfig->configInterface.outputToExternalBuffers = true; + if (mConfParam->synchronousProcessing) { + mConfig->configReconstruction.useMatLUT = false; + } - if (mConfParam->transformationFile.size() || mConfParam->transformationSCFile.size()) { - LOG(fatal) << "Deprecated configurable param options GPU_global.transformationFile or transformationSCFile used\n" - << "Instead, link the corresponding file as /TPC/Calib/CorrectionMap/snapshot.root and use it via\n" - << "--condition-remap file://=TPC/Calib/CorrectionMap option"; + // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) + if (mSpecConfig.outputTracks || mSpecConfig.outputCompClusters || mSpecConfig.outputCompClustersFlat) { + mConfig->configWorkflow.steps.set(GPUDataTypes::RecoStep::TPCConversion, + GPUDataTypes::RecoStep::TPCSliceTracking, + GPUDataTypes::RecoStep::TPCMerging); + mConfig->configWorkflow.outputs.set(GPUDataTypes::InOutType::TPCMergedTracks); + mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, mConfParam->rundEdx == -1 ? !mConfParam->synchronousProcessing : mConfParam->rundEdx); + } + if (mSpecConfig.outputCompClusters || mSpecConfig.outputCompClustersFlat) { + mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, true); + mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, true); + } + mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCClusters); + if (mSpecConfig.caClusterer) { // Override some settings if we have raw data as input + mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCRaw); + mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCClusterFinding, true); + mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCClusters, true); + } + if (mSpecConfig.decompressTPC) { + mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, false); + mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCDecompression, true); + mConfig->configWorkflow.inputs.set(GPUDataTypes::InOutType::TPCCompressedClusters); + mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCClusters, true); + mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::TPCCompressedClusters, false); + if (mTPCSectorMask != 0xFFFFFFFFF) { + throw std::invalid_argument("Cannot run TPC decompression with a sector mask"); } + } + if (mSpecConfig.runTRDTracking) { + mConfig->configWorkflow.inputs.setBits(GPUDataTypes::InOutType::TRDTracklets, true); + mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TRDTracking, true); + } + if (mSpecConfig.runITSTracking) { + mConfig->configWorkflow.inputs.setBits(GPUDataTypes::InOutType::ITSClusters, true); + mConfig->configWorkflow.outputs.setBits(GPUDataTypes::InOutType::ITSTracks, true); + mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::ITSTracking, true); + } + if (mSpecConfig.outputSharedClusterMap) { + mConfig->configProcessing.outputSharedClusterMap = true; + } + mConfig->configProcessing.createO2Output = mSpecConfig.outputTracks ? 2 : 0; // Disable O2 TPC track format output if no track output requested + mConfig->configProcessing.param.tpcTriggerHandling = mSpecConfig.tpcTriggerHandling; + + if (mConfParam->transformationFile.size() || mConfParam->transformationSCFile.size()) { + LOG(fatal) << "Deprecated configurable param options GPU_global.transformationFile or transformationSCFile used\n" + << "Instead, link the corresponding file as /TPC/Calib/CorrectionMap/snapshot.root and use it via\n" + << "--condition-remap file://=TPC/Calib/CorrectionMap option"; + } + /* if (config.configProcessing.doublePipeline && ic.services().get().poolSize != 2) { + throw std::runtime_error("double pipeline requires exactly 2 threads"); + } */ + if (config.configProcessing.doublePipeline && (mSpecConfig.readTRDtracklets || mSpecConfig.runITSTracking || !(mSpecConfig.zsOnTheFly || mSpecConfig.zsDecoder))) { + LOG(fatal) << "GPU two-threaded pipeline works only with TPC-only processing, and with ZS input"; + } + + if (mSpecConfig.enableDoublePipeline != 2) { + mGPUReco = std::make_unique(); // initialize TPC calib objects initFunctionTPCCalib(ic); - mConfig->configCalib.fastTransform = mFastTransformHelper->getCorrMap(); - mConfig->configCalib.fastTransformRef = mFastTransformHelper->getCorrMapRef(); - mConfig->configCalib.fastTransformHelper = mFastTransformHelper.get(); + + mConfig->configCalib.fastTransform = mCalibObjects.mFastTransformHelper->getCorrMap(); + mConfig->configCalib.fastTransformRef = mCalibObjects.mFastTransformHelper->getCorrMapRef(); + mConfig->configCalib.fastTransformHelper = mCalibObjects.mFastTransformHelper.get(); if (mConfig->configCalib.fastTransform == nullptr) { throw std::invalid_argument("GPU workflow: initialization of the TPC transformation failed"); } @@ -268,23 +276,27 @@ void GPURecoWorkflowSpec::init(InitContext& ic) } // Configuration is prepared, initialize the tracker. - if (mTracker->Initialize(config) != 0) { + if (mGPUReco->Initialize(config) != 0) { throw std::invalid_argument("GPU Reconstruction initialization failed"); } if (mSpecConfig.outputQA) { mQA = std::make_unique(mConfig.get()); } if (mSpecConfig.outputErrorQA) { - mTracker->setErrorCodeOutput(&mErrorQA); + mGPUReco->setErrorCodeOutput(&mErrorQA); } // initialize ITS if (mSpecConfig.runITSTracking) { initFunctionITS(ic); } + } - mTimer->Stop(); - mTimer->Reset(); + if (mSpecConfig.enableDoublePipeline) { + initPipeline(ic); + if (mConfParam->dump >= 2) { + LOG(fatal) << "Cannot use dump-only mode with multi-threaded pipeline"; + } } auto& callbacks = ic.services().get(); @@ -292,6 +304,12 @@ void GPURecoWorkflowSpec::init(InitContext& ic) if (info.size == 0) { return; } + if (mSpecConfig.enableDoublePipeline) { + mRegionInfos.emplace_back(info); + } + if (mSpecConfig.enableDoublePipeline == 2) { + return; + } if (mConfParam->registerSelectedSegmentIds != -1 && info.managed && info.id != (unsigned int)mConfParam->registerSelectedSegmentIds) { return; } @@ -311,7 +329,7 @@ void GPURecoWorkflowSpec::init(InitContext& ic) if (mConfParam->benchmarkMemoryRegistration) { start = std::chrono::high_resolution_clock::now(); } - if (mTracker->registerMemoryForGPU(info.ptr, info.size)) { + if (mGPUReco->registerMemoryForGPU(info.ptr, info.size)) { throw std::runtime_error("Error registering memory for GPU"); } if (mConfParam->benchmarkMemoryRegistration) { @@ -326,6 +344,9 @@ void GPURecoWorkflowSpec::init(InitContext& ic) close(fd); } }); + + mTimer->Stop(); + mTimer->Reset(); } void GPURecoWorkflowSpec::stop() @@ -335,13 +356,16 @@ void GPURecoWorkflowSpec::stop() void GPURecoWorkflowSpec::endOfStream(EndOfStreamContext& ec) { + handlePipelineEndOfStream(ec); } void GPURecoWorkflowSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) { - finaliseCCDBTPC(matcher, obj); - if (mSpecConfig.runITSTracking) { - finaliseCCDBITS(matcher, obj); + if (mSpecConfig.enableDoublePipeline != 2) { + finaliseCCDBTPC(matcher, obj); + if (mSpecConfig.runITSTracking) { + finaliseCCDBITS(matcher, obj); + } } if (GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { mGRPGeomUpdated = true; @@ -349,54 +373,20 @@ void GPURecoWorkflowSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& match } } -void GPURecoWorkflowSpec::run(ProcessingContext& pc) +template +void GPURecoWorkflowSpec::processInputs(ProcessingContext& pc, D& tpcZSmeta, E& inputZS, F& tpcZS, G& tpcZSonTheFlySizes, bool& debugTFDump, H& compClustersDummy, I& compClustersFlatDummy, J& pCompClustersFlat, K& tmpEmptyCompClusters) { + if (mSpecConfig.enableDoublePipeline == 1) { + return; + } constexpr static size_t NSectors = o2::tpc::Sector::MAXSECTOR; constexpr static size_t NEndpoints = o2::gpu::GPUTrackingInOutZS::NENDPOINTS; - auto cput = mTimer->CpuTime(); - auto realt = mTimer->RealTime(); - mTimer->Start(false); - mNTFs++; - - GRPGeomHelper::instance().checkUpdates(pc); - if (GRPGeomHelper::instance().getGRPECS()->isDetReadOut(o2::detectors::DetID::TPC) && mConfParam->tpcTriggeredMode ^ !GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::TPC)) { - LOG(fatal) << "configKeyValue tpcTriggeredMode does not match GRP isDetContinuousReadOut(TPC) setting"; - } - - std::vector> inputs; - - const o2::tpc::CompressedClustersFlat* pCompClustersFlat = nullptr; - size_t compClustersFlatDummyMemory[(sizeof(o2::tpc::CompressedClustersFlat) + sizeof(size_t) - 1) / sizeof(size_t)]; - o2::tpc::CompressedClustersFlat& compClustersFlatDummy = reinterpret_cast(compClustersFlatDummyMemory); - o2::tpc::CompressedClusters compClustersDummy; - o2::gpu::GPUTrackingInOutZS tpcZS; - std::vector tpcZSmetaPointers[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; - std::vector tpcZSmetaSizes[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; - const void** tpcZSmetaPointers2[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; - const unsigned int* tpcZSmetaSizes2[GPUTrackingInOutZS::NSLICES][GPUTrackingInOutZS::NENDPOINTS]; - std::array tpcZSonTheFlySizes; - gsl::span inputZS; - - bool getWorkflowTPCInput_clusters = false, getWorkflowTPCInput_mc = false, getWorkflowTPCInput_digits = false; - bool debugTFDump = false; - - // unsigned int totalZSPages = 0; - if (mSpecConfig.processMC) { - getWorkflowTPCInput_mc = true; - } - if (!mSpecConfig.decompressTPC && !mSpecConfig.caClusterer) { - getWorkflowTPCInput_clusters = true; - } - if (!mSpecConfig.decompressTPC && mSpecConfig.caClusterer && ((!mSpecConfig.zsOnTheFly || mSpecConfig.processMC) && !mSpecConfig.zsDecoder)) { - getWorkflowTPCInput_digits = true; - } - if (mSpecConfig.zsOnTheFly || mSpecConfig.zsDecoder) { for (unsigned int i = 0; i < GPUTrackingInOutZS::NSLICES; i++) { for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { - tpcZSmetaPointers[i][j].clear(); - tpcZSmetaSizes[i][j].clear(); + tpcZSmeta.Pointers[i][j].clear(); + tpcZSmeta.Sizes[i][j].clear(); } } } @@ -437,7 +427,6 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } } } - std::unique_ptr tmpEmptyCompClusters; if (mSpecConfig.zsDecoder) { std::vector filter = {{"check", ConcreteDataTypeMatcher{gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}}; auto isSameRdh = [](const char* left, const char* right) -> bool { @@ -453,24 +442,14 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) (feeLinkID == o2::tpc::rdh_utils::ILBZSLinkID && (rdhLink == o2::tpc::rdh_utils::UserLogicLinkID || rdhLink == o2::tpc::rdh_utils::ILBZSLinkID || rdhLink == 0)) || (feeLinkID == o2::tpc::rdh_utils::DLBZSLinkID && (rdhLink == o2::tpc::rdh_utils::UserLogicLinkID || rdhLink == o2::tpc::rdh_utils::DLBZSLinkID || rdhLink == 0))); }; - auto insertPages = [&tpcZSmetaPointers, &tpcZSmetaSizes, checkForZSData](const char* ptr, size_t count, uint32_t subSpec) -> void { - if (subSpec == 0xdeadbeef) { - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; - static int contDeadBeef = 0; - if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [TPC/RAWDATA/0xdeadbeef] assuming no payload for all links in this TF{}", contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); - } - return; - } + auto insertPages = [&tpcZSmeta, checkForZSData](const char* ptr, size_t count, uint32_t subSpec) -> void { if (checkForZSData(ptr, subSpec)) { int rawcru = o2::tpc::rdh_utils::getCRU(ptr); int rawendpoint = o2::tpc::rdh_utils::getEndPoint(ptr); - tpcZSmetaPointers[rawcru / 10][(rawcru % 10) * 2 + rawendpoint].emplace_back(ptr); - tpcZSmetaSizes[rawcru / 10][(rawcru % 10) * 2 + rawendpoint].emplace_back(count); + tpcZSmeta.Pointers[rawcru / 10][(rawcru % 10) * 2 + rawendpoint].emplace_back(ptr); + tpcZSmeta.Sizes[rawcru / 10][(rawcru % 10) * 2 + rawendpoint].emplace_back(count); } }; - // the sequencer processes all inputs matching the filter and finds sequences of consecutive - // raw pages based on the matcher predicate, and calls the inserter for each sequence if (DPLRawPageSequencer(pc.inputs(), filter)(isSameRdh, insertPages, checkForZSData)) { debugTFDump = true; static unsigned int nErrors = 0; @@ -483,12 +462,12 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) int totalCount = 0; for (unsigned int i = 0; i < GPUTrackingInOutZS::NSLICES; i++) { for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { - tpcZSmetaPointers2[i][j] = tpcZSmetaPointers[i][j].data(); - tpcZSmetaSizes2[i][j] = tpcZSmetaSizes[i][j].data(); - tpcZS.slice[i].zsPtr[j] = tpcZSmetaPointers2[i][j]; - tpcZS.slice[i].nZSPtr[j] = tpcZSmetaSizes2[i][j]; - tpcZS.slice[i].count[j] = tpcZSmetaPointers[i][j].size(); - totalCount += tpcZSmetaPointers[i][j].size(); + tpcZSmeta.Pointers2[i][j] = tpcZSmeta.Pointers[i][j].data(); + tpcZSmeta.Sizes2[i][j] = tpcZSmeta.Sizes[i][j].data(); + tpcZS.slice[i].zsPtr[j] = tpcZSmeta.Pointers2[i][j]; + tpcZS.slice[i].nZSPtr[j] = tpcZSmeta.Sizes2[i][j]; + tpcZS.slice[i].count[j] = tpcZSmeta.Pointers[i][j].size(); + totalCount += tpcZSmeta.Pointers[i][j].size(); } } } else if (mSpecConfig.decompressTPC) { @@ -509,9 +488,105 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) LOGF(info, "running tracking for sector(s) 0x%09x", mTPCSectorMask); } } +} + +int GPURecoWorkflowSpec::runMain(o2::framework::ProcessingContext* pc, GPUTrackingInOutPointers* ptrs, GPUInterfaceOutputs* outputRegions, int threadIndex, GPUInterfaceInputUpdate* inputUpdateCallback) +{ + int retVal = 0; + if (mConfParam->dump < 2) { + retVal = mGPUReco->RunTracking(ptrs, outputRegions, threadIndex, inputUpdateCallback); + + if (retVal == 0 && mSpecConfig.runITSTracking) { + retVal = runITSTracking(*pc); + } + } + + if (!mSpecConfig.enableDoublePipeline) { // TODO: Why is this needed for double-pipeline? + mGPUReco->Clear(false, threadIndex); // clean non-output memory used by GPU Reconstruction + } + return retVal; +} + +void GPURecoWorkflowSpec::cleanOldCalibsTPCPtrs(calibObjectStruct& oldCalibObjects) +{ + if (mOldCalibObjects.size() > 0) { + mOldCalibObjects.pop(); + } + mOldCalibObjects.emplace(std::move(oldCalibObjects)); +} + +void GPURecoWorkflowSpec::run(ProcessingContext& pc) +{ + constexpr static size_t NSectors = o2::tpc::Sector::MAXSECTOR; + constexpr static size_t NEndpoints = o2::gpu::GPUTrackingInOutZS::NENDPOINTS; + + auto cput = mTimer->CpuTime(); + auto realt = mTimer->RealTime(); + mTimer->Start(false); + mNTFs++; + + std::vector> inputs; + + const o2::tpc::CompressedClustersFlat* pCompClustersFlat = nullptr; + size_t compClustersFlatDummyMemory[(sizeof(o2::tpc::CompressedClustersFlat) + sizeof(size_t) - 1) / sizeof(size_t)]; + o2::tpc::CompressedClustersFlat& compClustersFlatDummy = reinterpret_cast(compClustersFlatDummyMemory); + o2::tpc::CompressedClusters compClustersDummy; + o2::gpu::GPUTrackingInOutZS tpcZS; + GPURecoWorkflowSpec_TPCZSBuffers tpcZSmeta; + std::array tpcZSonTheFlySizes; + gsl::span inputZS; + std::unique_ptr tmpEmptyCompClusters; + + bool getWorkflowTPCInput_clusters = false, getWorkflowTPCInput_mc = false, getWorkflowTPCInput_digits = false; + bool debugTFDump = false; + + if (mSpecConfig.processMC) { + getWorkflowTPCInput_mc = true; + } + if (!mSpecConfig.decompressTPC && !mSpecConfig.caClusterer) { + getWorkflowTPCInput_clusters = true; + } + if (!mSpecConfig.decompressTPC && mSpecConfig.caClusterer && ((!mSpecConfig.zsOnTheFly || mSpecConfig.processMC) && !mSpecConfig.zsDecoder)) { + getWorkflowTPCInput_digits = true; + } + + // ------------------------------ Handle inputs ------------------------------ + + auto lockDecodeInput = std::make_unique>(mPipeline->mutexDecodeInput); + + GRPGeomHelper::instance().checkUpdates(pc); + if (GRPGeomHelper::instance().getGRPECS()->isDetReadOut(o2::detectors::DetID::TPC) && mConfParam->tpcTriggeredMode ^ !GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::TPC)) { + LOG(fatal) << "configKeyValue tpcTriggeredMode does not match GRP isDetContinuousReadOut(TPC) setting"; + } - const auto& inputsClustersDigits = o2::tpc::getWorkflowTPCInput(pc, mVerbosity, getWorkflowTPCInput_mc, getWorkflowTPCInput_clusters, mTPCSectorMask, getWorkflowTPCInput_digits); GPUTrackingInOutPointers ptrs; + processInputs(pc, tpcZSmeta, inputZS, tpcZS, tpcZSonTheFlySizes, debugTFDump, compClustersDummy, compClustersFlatDummy, pCompClustersFlat, tmpEmptyCompClusters); // Process non-digit / non-cluster inputs + const auto& inputsClustersDigits = o2::tpc::getWorkflowTPCInput(pc, mVerbosity, getWorkflowTPCInput_mc, getWorkflowTPCInput_clusters, mTPCSectorMask, getWorkflowTPCInput_digits); // Process digit and cluster inputs + + const auto& tinfo = pc.services().get(); + mTFSettings->tfStartOrbit = tinfo.firstTForbit; + mTFSettings->hasTfStartOrbit = 1; + mTFSettings->hasNHBFPerTF = 1; + mTFSettings->nHBFPerTF = GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + mTFSettings->hasRunStartOrbit = 0; + if (mVerbosity) { + LOG(info) << "TF firstTForbit " << mTFSettings->tfStartOrbit << " nHBF " << mTFSettings->nHBFPerTF << " runStartOrbit " << mTFSettings->runStartOrbit << " simStartOrbit " << mTFSettings->simStartOrbit; + } + ptrs.settingsTF = mTFSettings.get(); + + if (mConfParam->checkFirstTfOrbit) { + static uint32_t lastFirstTFOrbit = -1; + static uint32_t lastTFCounter = -1; + if (lastFirstTFOrbit != -1 && lastTFCounter != -1) { + int diffOrbit = tinfo.firstTForbit - lastFirstTFOrbit; + int diffCounter = tinfo.tfCounter - lastTFCounter; + if (diffOrbit != diffCounter * mTFSettings->nHBFPerTF) { + LOG(error) << "Time frame has mismatching firstTfOrbit - Last orbit/counter: " << lastFirstTFOrbit << " " << lastTFCounter << " - Current: " << tinfo.firstTForbit << " " << tinfo.tfCounter; + } + } + lastFirstTFOrbit = tinfo.firstTForbit; + lastTFCounter = tinfo.tfCounter; + } o2::globaltracking::RecoContainer inputTracksTRD; decltype(o2::trd::getRecoInputContainer(pc, &ptrs, &inputTracksTRD)) trdInputContainer; @@ -543,6 +618,21 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) ptrs.clustersNative = &inputsClustersDigits->clusterIndex; } + if (mTPCSectorMask != 0xFFFFFFFFF) { + // Clean out the unused sectors, such that if they were present by chance, they are not processed, and if the values are uninitialized, we should not crash + for (unsigned int i = 0; i < NSectors; i++) { + if (!(mTPCSectorMask & (1ul << i))) { + if (ptrs.tpcZS) { + for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { + tpcZS.slice[i].zsPtr[j] = nullptr; + tpcZS.slice[i].nZSPtr[j] = nullptr; + tpcZS.slice[i].count[j] = 0; + } + } + } + } + } + GPUTrackingInOutDigits tpcDigitsMap; GPUTPCDigitsMCInput tpcDigitsMapMC; if (doInputDigits) { @@ -559,8 +649,6 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } } - // a byte size resizable vector object, the DataAllocator returns reference to internal object - // initialize optional pointer to the vector object o2::tpc::TPCSectorHeader clusterOutputSectorHeader{0}; if (mClusterOutputIds.size() > 0) { clusterOutputSectorHeader.sectorBits = mTPCSectorMask; @@ -568,6 +656,17 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) clusterOutputSectorHeader.activeSectors = mTPCSectorMask; } + // ------------------------------ Prepare stage for double-pipeline before normal output preparation ------------------------------ + + std::unique_ptr pipelineContext; + if (mSpecConfig.enableDoublePipeline) { + if (handlePipeline(pc, ptrs, tpcZSmeta, tpcZS, pipelineContext)) { + return; + } + } + + // ------------------------------ Prepare outputs ------------------------------ + GPUInterfaceOutputs outputRegions; using outputDataType = char; using outputBufferUninitializedVector = std::decay_t>(Output{"", "", 0}))>; @@ -639,50 +738,13 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) setOutputAllocator("TRACKS", mSpecConfig.outputTracks, outputRegions.tpcTracksO2, std::make_tuple(gDataOriginTPC, (DataDescription) "TRACKS", 0)); setOutputAllocator("CLUSREFS", mSpecConfig.outputTracks, outputRegions.tpcTracksO2ClusRefs, std::make_tuple(gDataOriginTPC, (DataDescription) "CLUSREFS", 0)); setOutputAllocator("TRACKSMCLBL", mSpecConfig.outputTracks && mSpecConfig.processMC, outputRegions.tpcTracksO2Labels, std::make_tuple(gDataOriginTPC, (DataDescription) "TRACKSMCLBL", 0)); + setOutputAllocator("TRIGGERWORDS", mSpecConfig.zsDecoder && mConfig->configProcessing.param.tpcTriggerHandling, outputRegions.tpcTriggerWords, std::make_tuple(gDataOriginTPC, (DataDescription) "TRIGGERWORDS", 0)); o2::tpc::ClusterNativeHelper::ConstMCLabelContainerViewWithBuffer clustersMCBuffer; if (mSpecConfig.processMC && mSpecConfig.caClusterer) { outputRegions.clusterLabels.allocator = [&clustersMCBuffer](size_t size) -> void* { return &clustersMCBuffer; }; } - const auto& tinfo = pc.services().get(); - mTFSettings->tfStartOrbit = tinfo.firstTForbit; - mTFSettings->hasTfStartOrbit = 1; - mTFSettings->hasNHBFPerTF = 1; - mTFSettings->nHBFPerTF = GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); - mTFSettings->hasRunStartOrbit = 0; - if (mVerbosity) { - LOG(info) << "TF firstTForbit " << mTFSettings->tfStartOrbit << " nHBF " << mTFSettings->nHBFPerTF << " runStartOrbit " << mTFSettings->runStartOrbit << " simStartOrbit " << mTFSettings->simStartOrbit; - } - ptrs.settingsTF = mTFSettings.get(); - - if (mConfParam->checkFirstTfOrbit) { - static uint32_t lastFirstTFOrbit = -1; - static uint32_t lastTFCounter = -1; - if (lastFirstTFOrbit != -1 && lastTFCounter != -1) { - int diffOrbit = tinfo.firstTForbit - lastFirstTFOrbit; - int diffCounter = tinfo.tfCounter - lastTFCounter; - if (diffOrbit != diffCounter * mTFSettings->nHBFPerTF) { - LOG(error) << "Time frame has mismatching firstTfOrbit - Last orbit/counter: " << lastFirstTFOrbit << " " << lastTFCounter << " - Current: " << tinfo.firstTForbit << " " << tinfo.tfCounter; - } - } - lastFirstTFOrbit = tinfo.firstTForbit; - lastTFCounter = tinfo.tfCounter; - } - - if (mTPCSectorMask != 0xFFFFFFFFF) { - // Clean out the unused sectors, such that if they were present by chance, they are not processed, and if the values are uninitialized, we should not crash - for (unsigned int i = 0; i < NSectors; i++) { - if (!(mTPCSectorMask & (1ul << i))) { - if (ptrs.tpcZS) { - for (unsigned int j = 0; j < GPUTrackingInOutZS::NENDPOINTS; j++) { - tpcZS.slice[i].zsPtr[j] = nullptr; - tpcZS.slice[i].nZSPtr[j] = nullptr; - tpcZS.slice[i].count[j] = 0; - } - } - } - } - } + // ------------------------------ Actual processing ------------------------------ if ((int)(ptrs.tpcZS != nullptr) + (int)(ptrs.tpcPackedDigits != nullptr && (ptrs.tpcZS == nullptr || ptrs.tpcPackedDigits->tpcDigitsMC == nullptr)) + (int)(ptrs.clustersNative != nullptr) + (int)(ptrs.tpcCompressedClusters != nullptr) != 1) { throw std::runtime_error("Invalid input for gpu tracking"); @@ -690,13 +752,16 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) const auto& holdData = o2::tpc::TPCTrackingDigitsPreCheck::runPrecheck(&ptrs, mConfig.get()); - doCalibUpdates(pc); + calibObjectStruct oldCalibObjects; + doCalibUpdates(pc, oldCalibObjects); + + lockDecodeInput.reset(); if (mConfParam->dump) { if (mNTFs == 1) { - mTracker->DumpSettings(); + mGPUReco->DumpSettings(); } - mTracker->DumpEvent(mNTFs - 1, &ptrs); + mGPUReco->DumpEvent(mNTFs - 1, &ptrs); } std::unique_ptr ptrsDump; if (mConfParam->dumpBadTFMode == 2) { @@ -705,24 +770,30 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } int retVal = 0; - if (mConfParam->dump < 2) { - retVal = mTracker->RunTracking(&ptrs, &outputRegions); - if (retVal != 0) { - debugTFDump = true; + if (mSpecConfig.enableDoublePipeline) { + if (!pipelineContext->jobSubmitted) { + enqueuePipelinedJob(&ptrs, &outputRegions, pipelineContext.get(), true); + } else { + finalizeInputPipelinedJob(&ptrs, &outputRegions, pipelineContext.get()); } - - if (retVal == 0 && mSpecConfig.runITSTracking) { - retVal = runITSTracking(pc); + std::unique_lock lk(pipelineContext->jobFinishedMutex); + pipelineContext->jobFinishedNotify.wait(lk, [context = pipelineContext.get()]() { return context->jobFinished; }); + retVal = pipelineContext->jobReturnValue; + } else { + // unsigned int threadIndex = pc.services().get().threadIndex; + unsigned int threadIndex = mNextThreadIndex; + if (mConfig->configProcessing.doublePipeline) { + mNextThreadIndex = (mNextThreadIndex + 1) % 2; } - } - // flushing debug output to file - o2::utils::DebugStreamer::instance()->flush(); - - // setting TPC calibration objects - storeUpdatedCalibsTPCPtrs(); + retVal = runMain(&pc, &ptrs, &outputRegions, threadIndex); + } + if (retVal != 0) { + debugTFDump = true; + } + cleanOldCalibsTPCPtrs(oldCalibObjects); - mTracker->Clear(false); + o2::utils::DebugStreamer::instance()->flush(); // flushing debug output to file if (debugTFDump && mNDebugDumps < mConfParam->dumpBadTFs) { mNDebugDumps++; @@ -740,13 +811,16 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } fclose(fp); } else if (mConfParam->dumpBadTFMode == 2) { - mTracker->DumpEvent(mNDebugDumps - 1, ptrsDump.get()); + mGPUReco->DumpEvent(mNDebugDumps - 1, ptrsDump.get()); } } if (mConfParam->dump == 2) { return; } + + // ------------------------------ Varios postprocessing steps ------------------------------ + bool createEmptyOutput = false; if (retVal != 0) { if (retVal == 3 && mConfig->configProcessing.ignoreNonFatalGPUErrors) { @@ -808,32 +882,10 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) downSizeBufferToSpan(outputRegions.tpcTracksO2Labels, spanOutputTracksMCTruth); // if requested, tune TPC tracks - using TrackTunePar = o2::globaltracking::TrackTuneParams; - const auto& trackTune = TrackTunePar::Instance(); - if (ptrs.nOutputTracksTPCO2 && trackTune.sourceLevelTPC && - (trackTune.useTPCInnerCorr || trackTune.useTPCOuterCorr || - trackTune.tpcCovInnerType != TrackTunePar::AddCovType::Disable || trackTune.tpcCovOuterType != TrackTunePar::AddCovType::Disable)) { - auto buffout = outputBuffers[outputRegions.getIndex(outputRegions.tpcTracksO2)].first->get().data(); - if (((const void*)ptrs.outputTracksTPCO2) != ((const void*)buffout)) { - throw std::runtime_error("Buffer does not match span"); - } - o2::tpc::TrackTPC* tpcTracks = reinterpret_cast(buffout); - for (unsigned int itr = 0; itr < ptrs.nOutputTracksTPCO2; itr++) { - auto& trc = tpcTracks[itr]; - if (trackTune.useTPCInnerCorr) { - trc.updateParams(trackTune.tpcParInner); - } - if (trackTune.tpcCovInnerType != TrackTunePar::AddCovType::Disable) { - trc.updateCov(trackTune.tpcCovInner, trackTune.tpcCovInnerType == TrackTunePar::AddCovType::WithCorrelations); - } - if (trackTune.useTPCOuterCorr) { - trc.getParamOut().updateParams(trackTune.tpcParOuter); - } - if (trackTune.tpcCovOuterType != TrackTunePar::AddCovType::Disable) { - trc.getParamOut().updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == TrackTunePar::AddCovType::WithCorrelations); - } - } + if (ptrs.nOutputTracksTPCO2) { + doTrackTuneTPC(ptrs, outputBuffers[outputRegions.getIndex(outputRegions.tpcTracksO2)].first->get().data()); } + if (mClusterOutputIds.size() > 0 && (void*)ptrs.clustersNative->clustersLinear != (void*)(outputBuffers[outputRegions.getIndex(outputRegions.clustersNative)].second + sizeof(o2::tpc::ClusterCountIndex))) { throw std::runtime_error("cluster native output ptrs out of sync"); // sanity check } @@ -908,204 +960,14 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) pc.outputs().snapshot({gDataOriginGPU, "ERRORQA", 0, Lifetime::Timeframe}, mErrorQA); mErrorQA.clear(); // FIXME: This is a race condition once we run multi-threaded! } + if (mSpecConfig.tpcTriggerHandling && !(mSpecConfig.zsOnTheFly || mSpecConfig.zsDecoder)) { + pc.outputs().make>(Output{gDataOriginTPC, "TRIGGERWORDS", 0, Lifetime::Timeframe}, 0u); + } mTimer->Stop(); LOG(info) << "GPU Reoncstruction time for this TF " << mTimer->CpuTime() - cput << " s (cpu), " << mTimer->RealTime() - realt << " s (wall)"; } -int GPURecoWorkflowSpec::runITSTracking(o2::framework::ProcessingContext& pc) -{ - using Vertex = o2::dataformats::Vertex>; - - auto compClusters = pc.inputs().get>("compClusters"); - gsl::span patterns = pc.inputs().get>("patterns"); - gsl::span physTriggers; - std::vector fromTRD; - if (mSpecConfig.itsTriggerType == 2) { // use TRD triggers - o2::InteractionRecord ir{0, pc.services().get().firstTForbit}; - auto trdTriggers = pc.inputs().get>("phystrig"); - for (const auto& trig : trdTriggers) { - if (trig.getBCData() >= ir && trig.getNumberOfTracklets()) { - ir = trig.getBCData(); - fromTRD.emplace_back(o2::itsmft::PhysTrigger{ir, 0}); - } - } - physTriggers = gsl::span(fromTRD.data(), fromTRD.size()); - } else if (mSpecConfig.itsTriggerType == 1) { // use Phys triggers from ITS stream - physTriggers = pc.inputs().get>("phystrig"); - } - - // code further down does assignment to the rofs and the altered object is used for output - // we therefore need a copy of the vector rather than an object created directly on the input data, - // the output vector however is created directly inside the message memory thus avoiding copy by - // snapshot - auto rofsinput = pc.inputs().get>("ROframes"); - auto& rofs = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0, Lifetime::Timeframe}, rofsinput.begin(), rofsinput.end()); - - auto& irFrames = pc.outputs().make>(Output{"ITS", "IRFRAMES", 0, Lifetime::Timeframe}); - - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); // RS: this should come from CCDB - int nBCPerTF = alpParams.roFrameLengthInBC; - - LOG(info) << "ITSTracker pulled " << compClusters.size() << " clusters, " << rofs.size() << " RO frames"; - - const dataformats::MCTruthContainer* labels = nullptr; - gsl::span mc2rofs; - if (mSpecConfig.processMC) { - labels = pc.inputs().get*>("itsmclabels").release(); - // get the array as read-only span, a snapshot is send forward - mc2rofs = pc.inputs().get>("ITSMC2ROframes"); - LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; - } - - std::vector tracks; - auto& allClusIdx = pc.outputs().make>(Output{"ITS", "TRACKCLSID", 0, Lifetime::Timeframe}); - std::vector trackLabels; - std::vector verticesLabels; - auto& allTracks = pc.outputs().make>(Output{"ITS", "TRACKS", 0, Lifetime::Timeframe}); - std::vector allTrackLabels; - std::vector allVerticesLabels; - - auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0, Lifetime::Timeframe}); - auto& vertices = pc.outputs().make>(Output{"ITS", "VERTICES", 0, Lifetime::Timeframe}); - - std::uint32_t roFrame = 0; - - bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS); - LOG(info) << "ITSTracker RO: continuous=" << continuous; - - if (mSpecConfig.itsOverrBeamEst) { - mITSTimeFrame->setBeamPosition(mMeanVertex->getX(), - mMeanVertex->getY(), - mMeanVertex->getSigmaY2(), - mITSTracker->getParameters()[0].LayerResolution[0], - mITSTracker->getParameters()[0].SystErrorY2[0]); - } - - mITSTracker->setBz(o2::base::Propagator::Instance()->getNominalBz()); - - gsl::span::iterator pattIt = patterns.begin(); - - gsl::span rofspan(rofs); - mITSTimeFrame->loadROFrameData(rofspan, compClusters, pattIt, mITSDict, labels); - pattIt = patterns.begin(); - std::vector savedROF; - auto logger = [&](std::string s) { LOG(info) << s; }; - auto errorLogger = [&](std::string s) { LOG(error) << s; }; - - o2::its::FastMultEst multEst; // mult estimator - std::vector processingMask; - int cutVertexMult{0}, cutRandomMult = int(rofs.size()) - multEst.selectROFs(rofs, compClusters, physTriggers, processingMask); - mITSTimeFrame->setMultiplicityCutMask(processingMask); - float vertexerElapsedTime{0.f}; - if (mITSRunVertexer) { - // Run seeding vertexer - vertexerElapsedTime = mITSVertexer->clustersToVertices(logger); - } else { // cosmics - mITSTimeFrame->resetRofPV(); - } - const auto& multEstConf = o2::its::FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - for (auto iRof{0}; iRof < rofspan.size(); ++iRof) { - std::vector vtxVecLoc; - auto& vtxROF = vertROFvec.emplace_back(rofspan[iRof]); - vtxROF.setFirstEntry(vertices.size()); - if (mITSRunVertexer) { - auto vtxSpan = mITSTimeFrame->getPrimaryVertices(iRof); - vtxROF.setNEntries(vtxSpan.size()); - bool selROF = vtxSpan.size() == 0; - for (auto iV{0}; iV < vtxSpan.size(); ++iV) { - auto& v = vtxSpan[iV]; - if (multEstConf.isVtxMultCutRequested() && !multEstConf.isPassingVtxMultCut(v.getNContributors())) { - continue; // skip vertex of unwanted multiplicity - } - selROF = true; - vertices.push_back(v); - if (mSpecConfig.processMC) { - auto vLabels = mITSTimeFrame->getPrimaryVerticesLabels(iRof)[iV]; - std::copy(vLabels.begin(), vLabels.end(), std::back_inserter(allVerticesLabels)); - } - } - if (processingMask[iRof] && !selROF) { // passed selection in clusters and not in vertex multiplicity - LOG(debug) << fmt::format("ROF {} rejected by the vertex multiplicity selection [{},{}]", - iRof, - multEstConf.cutMultVtxLow, - multEstConf.cutMultVtxHigh); - processingMask[iRof] = selROF; - cutVertexMult++; - } - } else { // cosmics - vtxVecLoc.emplace_back(Vertex()); - vtxVecLoc.back().setNContributors(1); - vtxROF.setNEntries(vtxVecLoc.size()); - for (auto& v : vtxVecLoc) { - vertices.push_back(v); - } - mITSTimeFrame->addPrimaryVertices(vtxVecLoc); - } - } - LOG(info) << fmt::format(" - rejected {}/{} ROFs: random/mult.sel:{} (seed {}), vtx.sel:{}", cutRandomMult + cutVertexMult, rofspan.size(), cutRandomMult, multEst.lastRandomSeed, cutVertexMult); - LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} vertices found in {} ROFs", vertexerElapsedTime, mITSTimeFrame->getPrimaryVerticesNum(), rofspan.size()); - - if (mSpecConfig.itsOverrBeamEst) { - LOG(info) << fmt::format(" - Beam position set to: {}, {} from meanvertex object", mITSTimeFrame->getBeamX(), mITSTimeFrame->getBeamY()); - } else { - LOG(info) << fmt::format(" - Beam position computed for the TF: {}, {}", mITSTimeFrame->getBeamX(), mITSTimeFrame->getBeamY()); - } - if (mITSCosmicsProcessing && compClusters.size() > 1500 * rofspan.size()) { - LOG(error) << "Cosmics processing was requested with an average detector occupancy exceeding 1.e-7, skipping TF processing."; - } else { - - mITSTimeFrame->setMultiplicityCutMask(processingMask); - // Run CA tracker - mITSTracker->clustersToTracks(logger, errorLogger); - if (mITSTimeFrame->hasBogusClusters()) { - LOG(warning) << fmt::format(" - The processed timeframe had {} clusters with wild z coordinates, check the dictionaries", mITSTimeFrame->hasBogusClusters()); - } - - for (unsigned int iROF{0}; iROF < rofs.size(); ++iROF) { - auto& rof{rofs[iROF]}; - tracks = mITSTimeFrame->getTracks(iROF); - trackLabels = mITSTimeFrame->getTracksLabel(iROF); - auto number{tracks.size()}; - auto first{allTracks.size()}; - int offset = -rof.getFirstEntry(); // cluster entry!!! - rof.setFirstEntry(first); - rof.setNEntries(number); - - if (processingMask[iROF]) { - irFrames.emplace_back(rof.getBCData(), rof.getBCData() + nBCPerTF - 1).info = tracks.size(); - } - - std::copy(trackLabels.begin(), trackLabels.end(), std::back_inserter(allTrackLabels)); - // Some conversions that needs to be moved in the tracker internals - for (unsigned int iTrk{0}; iTrk < tracks.size(); ++iTrk) { - auto& trc{tracks[iTrk]}; - trc.setFirstClusterEntry(allClusIdx.size()); // before adding tracks, create final cluster indices - int ncl = trc.getNumberOfClusters(), nclf = 0; - for (int ic = o2::its::TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! - auto clid = trc.getClusterIndex(ic); - if (clid >= 0) { - allClusIdx.push_back(clid); - nclf++; - } - } - assert(ncl == nclf); - allTracks.emplace_back(trc); - } - } - LOGP(info, "ITSTracker pushed {} tracks and {} vertices", allTracks.size(), vertices.size()); - if (mSpecConfig.processMC) { - LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); - LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); - - pc.outputs().snapshot(Output{"ITS", "TRACKSMCTR", 0, Lifetime::Timeframe}, allTrackLabels); - pc.outputs().snapshot(Output{"ITS", "VERTICESMCTR", 0, Lifetime::Timeframe}, allVerticesLabels); - pc.outputs().snapshot(Output{"ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe}, mc2rofs); - } - } - return 0; -} - -void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc) +void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, calibObjectStruct& oldCalibObjects) { GPUCalibObjectsConst newCalibObjects; GPUNewCalibValues newCalibValues; @@ -1157,19 +1019,30 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc) mTRDGeometryCreated = true; } } - needCalibUpdate = fetchCalibsCCDBTPC(pc, newCalibObjects) || needCalibUpdate; + needCalibUpdate = fetchCalibsCCDBTPC(pc, newCalibObjects, oldCalibObjects) || needCalibUpdate; if (mSpecConfig.runITSTracking) { needCalibUpdate = fetchCalibsCCDBITS(pc) || needCalibUpdate; } if (needCalibUpdate) { LOG(info) << "Updating GPUReconstruction calibration objects"; - mTracker->UpdateCalibration(newCalibObjects, newCalibValues); + mGPUReco->UpdateCalibration(newCalibObjects, newCalibValues); } } Options GPURecoWorkflowSpec::options() { Options opts; + if (mSpecConfig.enableDoublePipeline) { + bool send = mSpecConfig.enableDoublePipeline == 2; + char* o2jobid = getenv("O2JOBID"); + char* numaid = getenv("NUMAID"); + int chanid = o2jobid ? atoi(o2jobid) : (numaid ? atoi(numaid) : 0); + std::string chan = std::string("name=gpu-prepare-channel,type=") + (send ? "push" : "pull") + ",method=" + (send ? "connect" : "bind") + ",address=ipc://@gpu-prepare-channel-" + std::to_string(chanid) + "-{timeslice0},transport=shmem,rateLogging=0"; + opts.emplace_back(o2::framework::ConfigParamSpec{"channel-config", o2::framework::VariantType::String, chan, {"Out-of-band channel config"}}); + } + if (mSpecConfig.enableDoublePipeline == 2) { + return opts; + } if (mSpecConfig.outputTracks) { o2::tpc::CorrectionMapsLoader::addOptions(opts); } @@ -1179,6 +1052,22 @@ Options GPURecoWorkflowSpec::options() Inputs GPURecoWorkflowSpec::inputs() { Inputs inputs; + if (mSpecConfig.zsDecoder) { + // All ZS raw data is published with subspec 0 by the o2-raw-file-reader-workflow and DataDistribution + // creates subspec fom CRU and endpoint id, we create one single input route subscribing to all TPC/RAWDATA + inputs.emplace_back(InputSpec{"zsraw", ConcreteDataTypeMatcher{"TPC", "RAWDATA"}, Lifetime::Timeframe}); + if (mSpecConfig.askDISTSTF) { + inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); + } + } + if (mSpecConfig.enableDoublePipeline == 2) { + if (!mSpecConfig.zsDecoder) { + LOG(fatal) << "Double pipeline mode can only work with zsraw input"; + } + return inputs; + } else if (mSpecConfig.enableDoublePipeline == 1) { + inputs.emplace_back("pipelineprepare", gDataOriginGPU, "PIPELINEPREPARE", 0, Lifetime::Timeframe); + } if (mSpecConfig.outputTracks) { // loading calibration objects from the CCDB inputs.emplace_back("tpcgain", gDataOriginTPC, "PADGAINFULL", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalPadGainFull))); @@ -1188,7 +1077,7 @@ Inputs GPURecoWorkflowSpec::inputs() inputs.emplace_back("tpcthreshold", gDataOriginTPC, "PADTHRESHOLD", 0, Lifetime::Condition, ccdbParamSpec("TPC/Config/FEEPad")); o2::tpc::VDriftHelper::requestCCDBInputs(inputs); Options optsDummy; - mFastTransformHelper->requestCCDBInputs(inputs, optsDummy, mSpecConfig.requireCTPLumi, mSpecConfig.lumiScaleMode); // option filled here is lost + mCalibObjects.mFastTransformHelper->requestCCDBInputs(inputs, optsDummy, mSpecConfig.requireCTPLumi, mSpecConfig.lumiScaleMode); // option filled here is lost } if (mSpecConfig.decompressTPC) { inputs.emplace_back(InputSpec{"input", ConcreteDataTypeMatcher{gDataOriginTPC, mSpecConfig.decompressTPCFromROOT ? o2::header::DataDescription("COMPCLUSTERS") : o2::header::DataDescription("COMPCLUSTERSFLAT")}, Lifetime::Timeframe}); @@ -1219,14 +1108,6 @@ Inputs GPURecoWorkflowSpec::inputs() } } - if (mSpecConfig.zsDecoder) { - // All ZS raw data is published with subspec 0 by the o2-raw-file-reader-workflow and DataDistribution - // creates subspec fom CRU and endpoint id, we create one single input route subscribing to all TPC/RAWDATA - inputs.emplace_back(InputSpec{"zsraw", ConcreteDataTypeMatcher{"TPC", "RAWDATA"}, Lifetime::Optional}); - if (mSpecConfig.askDISTSTF) { - inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); - } - } if (mSpecConfig.zsOnTheFly) { inputs.emplace_back(InputSpec{"zsinput", ConcreteDataTypeMatcher{"TPC", "TPCZS"}, Lifetime::Timeframe}); inputs.emplace_back(InputSpec{"zsinputsizes", ConcreteDataTypeMatcher{"TPC", "ZSSIZES"}, Lifetime::Timeframe}); @@ -1266,6 +1147,10 @@ Outputs GPURecoWorkflowSpec::outputs() { constexpr static size_t NSectors = o2::tpc::Sector::MAXSECTOR; std::vector outputSpecs; + if (mSpecConfig.enableDoublePipeline == 2) { + outputSpecs.emplace_back(gDataOriginGPU, "PIPELINEPREPARE", 0, Lifetime::Timeframe); + return outputSpecs; + } if (mSpecConfig.outputTracks) { outputSpecs.emplace_back(gDataOriginTPC, "TRACKS", 0, Lifetime::Timeframe); outputSpecs.emplace_back(gDataOriginTPC, "CLUSREFS", 0, Lifetime::Timeframe); @@ -1304,6 +1189,9 @@ Outputs GPURecoWorkflowSpec::outputs() if (mSpecConfig.outputSharedClusterMap) { outputSpecs.emplace_back(gDataOriginTPC, "CLSHAREDMAP", 0, Lifetime::Timeframe); } + if (mSpecConfig.tpcTriggerHandling) { + outputSpecs.emplace_back(gDataOriginTPC, "TRIGGERWORDS", 0, Lifetime::Timeframe); + } if (mSpecConfig.outputQA) { outputSpecs.emplace_back(gDataOriginTPC, "TRACKINGQA", 0, Lifetime::Timeframe); } @@ -1312,401 +1200,29 @@ Outputs GPURecoWorkflowSpec::outputs() } if (mSpecConfig.runITSTracking) { - outputSpecs.emplace_back("ITS", "TRACKS", 0, Lifetime::Timeframe); - outputSpecs.emplace_back("ITS", "TRACKCLSID", 0, Lifetime::Timeframe); - outputSpecs.emplace_back("ITS", "ITSTrackROF", 0, Lifetime::Timeframe); - outputSpecs.emplace_back("ITS", "VERTICES", 0, Lifetime::Timeframe); - outputSpecs.emplace_back("ITS", "VERTICESROF", 0, Lifetime::Timeframe); - outputSpecs.emplace_back("ITS", "IRFRAMES", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "TRACKS", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "TRACKCLSID", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "ITSTrackROF", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "VERTICES", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "VERTICESROF", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "IRFRAMES", 0, Lifetime::Timeframe); if (mSpecConfig.processMC) { - outputSpecs.emplace_back("ITS", "VERTICESMCTR", 0, Lifetime::Timeframe); - outputSpecs.emplace_back("ITS", "TRACKSMCTR", 0, Lifetime::Timeframe); - outputSpecs.emplace_back("ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "VERTICESMCTR", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "TRACKSMCTR", 0, Lifetime::Timeframe); + outputSpecs.emplace_back(gDataOriginITS, "ITSTrackMC2ROF", 0, Lifetime::Timeframe); } } return outputSpecs; }; -void GPURecoWorkflowSpec::initFunctionITS(InitContext& ic) -{ - std::transform(mITSMode.begin(), mITSMode.end(), mITSMode.begin(), [](unsigned char c) { return std::tolower(c); }); - o2::its::VertexerTraits* vtxTraits = nullptr; - o2::its::TrackerTraits* trkTraits = nullptr; - mTracker->GetITSTraits(trkTraits, vtxTraits, mITSTimeFrame); - mITSVertexer = std::make_unique(vtxTraits); - mITSTracker = std::make_unique(trkTraits); - mITSVertexer->adoptTimeFrame(*mITSTimeFrame); - mITSTracker->adoptTimeFrame(*mITSTimeFrame); - mITSRunVertexer = true; - mITSCosmicsProcessing = false; - std::vector trackParams; - - if (mITSMode == "async") { - trackParams.resize(3); - for (auto& param : trackParams) { - param.ZBins = 64; - param.PhiBins = 32; - } - trackParams[1].TrackletMinPt = 0.2f; - trackParams[1].CellDeltaTanLambdaSigma *= 2.; - trackParams[2].TrackletMinPt = 0.1f; - trackParams[2].CellDeltaTanLambdaSigma *= 4.; - trackParams[2].MinTrackLength = 4; - LOG(info) << "Initializing tracker in async. phase reconstruction with " << trackParams.size() << " passes"; - } else if (mITSMode == "sync") { - trackParams.resize(1); - trackParams[0].ZBins = 64; - trackParams[0].PhiBins = 32; - trackParams[0].MinTrackLength = 4; - LOG(info) << "Initializing tracker in sync. phase reconstruction with " << trackParams.size() << " passes"; - } else if (mITSMode == "cosmics") { - mITSCosmicsProcessing = true; - mITSRunVertexer = false; - trackParams.resize(1); - trackParams[0].MinTrackLength = 4; - trackParams[0].CellDeltaTanLambdaSigma *= 10; - trackParams[0].PhiBins = 4; - trackParams[0].ZBins = 16; - trackParams[0].PVres = 1.e5f; - trackParams[0].MaxChi2ClusterAttachment = 60.; - trackParams[0].MaxChi2NDF = 40.; - trackParams[0].TrackletsPerClusterLimit = 100.; - trackParams[0].CellsPerClusterLimit = 100.; - LOG(info) << "Initializing tracker in reconstruction for cosmics with " << trackParams.size() << " passes"; - } else { - throw std::runtime_error(fmt::format("Unsupported ITS tracking mode {:s} ", mITSMode)); - } - - for (auto& params : trackParams) { - params.CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; - } - mITSTracker->setParameters(trackParams); -} - -void GPURecoWorkflowSpec::initFunctionTPCCalib(InitContext& ic) -{ - mdEdxCalibContainer.reset(new o2::tpc::CalibdEdxContainer()); - mTPCVDriftHelper.reset(new o2::tpc::VDriftHelper()); - mFastTransformHelper.reset(new o2::tpc::CorrectionMapsLoader()); - mFastTransform = std::move(o2::tpc::TPCFastTransformHelperO2::instance()->create(0)); - mFastTransformRef = std::move(o2::tpc::TPCFastTransformHelperO2::instance()->create(0)); - mFastTransformHelper->setCorrMap(mFastTransform.get()); // just to reserve the space - mFastTransformHelper->setCorrMapRef(mFastTransformRef.get()); - mFastTransformHelper->setLumiScaleMode(mSpecConfig.lumiScaleMode); - if (mSpecConfig.outputTracks) { - mFastTransformHelper->init(ic); - } - if (mConfParam->dEdxDisableTopologyPol) { - LOGP(info, "Disabling loading of track topology correction using polynomials from CCDB"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol); - } - - if (mConfParam->dEdxDisableThresholdMap) { - LOGP(info, "Disabling loading of threshold map from CCDB"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalThresholdMap); - } - - if (mConfParam->dEdxDisableGainMap) { - LOGP(info, "Disabling loading of gain map from CCDB"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalGainMap); - } - - if (mConfParam->dEdxDisableResidualGainMap) { - LOGP(info, "Disabling loading of residual gain map from CCDB"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalResidualGainMap); - } - - if (mConfParam->dEdxDisableResidualGain) { - LOGP(info, "Disabling loading of residual gain calibration from CCDB"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTimeGain); - } - - if (mConfParam->dEdxUseFullGainMap) { - LOGP(info, "Using the full gain map for correcting the cluster charge during calculation of the dE/dx"); - mdEdxCalibContainer->setUsageOfFullGainMap(true); - } - - if (mConfParam->gainCalibDisableCCDB) { - LOGP(info, "Disabling loading the TPC pad gain calibration from the CCDB"); - mUpdateGainMapCCDB = false; - } - - // load from file - if (!mConfParam->dEdxPolTopologyCorrFile.empty() || !mConfParam->dEdxCorrFile.empty() || !mConfParam->dEdxSplineTopologyCorrFile.empty()) { - if (!mConfParam->dEdxPolTopologyCorrFile.empty()) { - LOGP(info, "Loading dE/dx polynomial track topology correction from file: {}", mConfParam->dEdxPolTopologyCorrFile); - mdEdxCalibContainer->loadPolTopologyCorrectionFromFile(mConfParam->dEdxPolTopologyCorrFile); - - LOGP(info, "Disabling loading of track topology correction using polynomials from CCDB as it was already loaded from input file"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol); - - if (std::filesystem::exists(mConfParam->thresholdCalibFile)) { - LOG(info) << "Loading tpc zero supression map from file " << mConfParam->thresholdCalibFile; - const auto* thresholdMap = o2::tpc::utils::readCalPads(mConfParam->thresholdCalibFile, "ThresholdMap")[0]; - mdEdxCalibContainer->setZeroSupresssionThreshold(*thresholdMap); - - LOGP(info, "Disabling loading of threshold map from CCDB as it was already loaded from input file"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalThresholdMap); - } else { - if (not mConfParam->thresholdCalibFile.empty()) { - LOG(warn) << "Couldn't find tpc zero supression file " << mConfParam->thresholdCalibFile << ". Not setting any zero supression."; - } - LOG(info) << "Setting default zero supression map"; - mdEdxCalibContainer->setDefaultZeroSupresssionThreshold(); - } - } else if (!mConfParam->dEdxSplineTopologyCorrFile.empty()) { - LOGP(info, "Loading dE/dx spline track topology correction from file: {}", mConfParam->dEdxSplineTopologyCorrFile); - mdEdxCalibContainer->loadSplineTopologyCorrectionFromFile(mConfParam->dEdxSplineTopologyCorrFile); - - LOGP(info, "Disabling loading of track topology correction using polynomials from CCDB as splines were loaded from input file"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol); - } - if (!mConfParam->dEdxCorrFile.empty()) { - LOGP(info, "Loading dEdx correction from file: {}", mConfParam->dEdxCorrFile); - mdEdxCalibContainer->loadResidualCorrectionFromFile(mConfParam->dEdxCorrFile); - - LOGP(info, "Disabling loading of residual gain calibration from CCDB as it was already loaded from input file"); - mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTimeGain); - } - } - - if (mConfParam->dEdxPolTopologyCorrFile.empty() && mConfParam->dEdxSplineTopologyCorrFile.empty()) { - // setting default topology correction to allocate enough memory - LOG(info) << "Setting default dE/dx polynomial track topology correction to allocate enough memory"; - mdEdxCalibContainer->setDefaultPolTopologyCorrection(); - } - - GPUO2InterfaceConfiguration& config = *mConfig.get(); - mConfig->configCalib.dEdxCalibContainer = mdEdxCalibContainer.get(); - - if (std::filesystem::exists(mConfParam->gainCalibFile)) { - LOG(info) << "Loading tpc gain correction from file " << mConfParam->gainCalibFile; - const auto* gainMap = o2::tpc::utils::readCalPads(mConfParam->gainCalibFile, "GainMap")[0]; - mTPCPadGainCalib = GPUO2Interface::getPadGainCalib(*gainMap); - - LOGP(info, "Disabling loading the TPC gain correction map from the CCDB as it was already loaded from input file"); - mUpdateGainMapCCDB = false; - } else { - if (not mConfParam->gainCalibFile.empty()) { - LOG(warn) << "Couldn't find tpc gain correction file " << mConfParam->gainCalibFile << ". Not applying any gain correction."; - } - mTPCPadGainCalib = GPUO2Interface::getPadGainCalibDefault(); - mTPCPadGainCalib->getGainCorrection(30, 5, 5); - } - mConfig->configCalib.tpcPadGain = mTPCPadGainCalib.get(); - - mTPCZSLinkMapping.reset(new TPCZSLinkMapping{tpc::Mapper::instance()}); - mConfig->configCalib.tpcZSLinkMapping = mTPCZSLinkMapping.get(); -} - -void GPURecoWorkflowSpec::finaliseCCDBITS(ConcreteDataMatcher& matcher, void* obj) -{ - if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated"; - mITSDict = (const o2::itsmft::TopologyDictionary*)obj; - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("GLO", "MEANVERTEX", 0)) { - LOGP(info, "mean vertex acquired"); - if (obj) { - mMeanVertex = (const o2::dataformats::MeanVertexObject*)obj; - } - return; - } -} - -void GPURecoWorkflowSpec::finaliseCCDBTPC(ConcreteDataMatcher& matcher, void* obj) -{ - const o2::tpc::CalibdEdxContainer* dEdxCalibContainer = mdEdxCalibContainer.get(); - - auto copyCalibsToBuffer = [this, dEdxCalibContainer]() { - if (!(mdEdxCalibContainerBufferNew)) { - mdEdxCalibContainerBufferNew = std::make_unique(); - mdEdxCalibContainerBufferNew->cloneFromObject(*dEdxCalibContainer, nullptr); - } - }; - - if (matcher == ConcreteDataMatcher(gDataOriginTPC, "PADGAINFULL", 0)) { - LOGP(info, "Updating gain map from CCDB"); - const auto* gainMap = static_cast*>(obj); - - if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalGainMap) && mSpecConfig.outputTracks) { - copyCalibsToBuffer(); - const float minGain = 0; - const float maxGain = 2; - mdEdxCalibContainerBufferNew.get()->setGainMap(*gainMap, minGain, maxGain); - } - - if (mUpdateGainMapCCDB && mSpecConfig.caClusterer) { - mTPCPadGainCalibBufferNew = GPUO2Interface::getPadGainCalib(*gainMap); - } - - } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "PADGAINRESIDUAL", 0)) { - LOGP(info, "Updating residual gain map from CCDB"); - copyCalibsToBuffer(); - const auto* gainMapResidual = static_cast>*>(obj); - const float minResidualGain = 0.7f; - const float maxResidualGain = 1.3f; - mdEdxCalibContainerBufferNew.get()->setGainMapResidual(gainMapResidual->at("GainMap"), minResidualGain, maxResidualGain); - } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "PADTHRESHOLD", 0)) { - LOGP(info, "Updating threshold map from CCDB"); - copyCalibsToBuffer(); - const auto* thresholdMap = static_cast>*>(obj); - mdEdxCalibContainerBufferNew.get()->setZeroSupresssionThreshold(thresholdMap->at("ThresholdMap")); - } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "TOPOLOGYGAIN", 0) && !(dEdxCalibContainer->isTopologyCorrectionSplinesSet())) { - LOGP(info, "Updating Q topology correction from CCDB"); - copyCalibsToBuffer(); - const auto* topologyCorr = static_cast(obj); - o2::tpc::CalibdEdxTrackTopologyPol calibTrackTopology; - calibTrackTopology.setFromContainer(*topologyCorr); - mdEdxCalibContainerBufferNew->setPolTopologyCorrection(calibTrackTopology); - } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "TIMEGAIN", 0)) { - LOGP(info, "Updating residual gain correction from CCDB"); - copyCalibsToBuffer(); - const auto* residualCorr = static_cast(obj); - mdEdxCalibContainerBufferNew->setResidualCorrection(*residualCorr); - } else if (mTPCVDriftHelper->accountCCDBInputs(matcher, obj)) { - } else if (mFastTransformHelper->accountCCDBInputs(matcher, obj)) { - } -} - -bool GPURecoWorkflowSpec::fetchCalibsCCDBITS(ProcessingContext& pc) -{ - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("itscldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("itsalppar"); - mITSVertexer->getGlobalConfiguration(); - mITSTracker->getGlobalConfiguration(); - if (mSpecConfig.itsOverrBeamEst) { - pc.inputs().get("meanvtx"); - } - } - return false; -} - -template -bool GPURecoWorkflowSpec::fetchCalibsCCDBTPC(ProcessingContext& pc, T& newCalibObjects) -{ - // update calibrations for clustering and tracking - mMustUpdateFastTransform = false; - if ((mSpecConfig.outputTracks || mSpecConfig.caClusterer) && !mConfParam->disableCalibUpdates) { - const o2::tpc::CalibdEdxContainer* dEdxCalibContainer = mdEdxCalibContainer.get(); - - // this calibration is defined for clustering and tracking - if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalGainMap) || mUpdateGainMapCCDB) { - pc.inputs().get*>("tpcgain"); - } - - // these calibrations are only defined for the tracking - if (mSpecConfig.outputTracks) { - // update the calibration objects in case they changed in the CCDB - if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalThresholdMap)) { - pc.inputs().get>*>("tpcthreshold"); - } - - if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalResidualGainMap)) { - pc.inputs().get>*>("tpcgainresidual"); - } - - if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol)) { - pc.inputs().get("tpctopologygain"); - } - - if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalTimeGain)) { - pc.inputs().get("tpctimegain"); - } - - if (mSpecConfig.outputTracks) { - mTPCVDriftHelper->extractCCDBInputs(pc); - mFastTransformHelper->extractCCDBInputs(pc); - } - if (mTPCVDriftHelper->isUpdated() || mFastTransformHelper->isUpdated()) { - const auto& vd = mTPCVDriftHelper->getVDriftObject(); - LOGP(info, "Updating{}TPC fast transform map and/or VDrift factor of {} wrt reference {} and TDrift offset {} wrt reference {} from source {}", - mFastTransformHelper->isUpdated() ? " new " : " old ", - vd.corrFact, vd.refVDrift, vd.timeOffsetCorr, vd.refTimeOffset, mTPCVDriftHelper->getSourceName()); - - if (mTPCVDriftHelper->isUpdated() || mFastTransformHelper->isUpdatedMap()) { - mFastTransformNew.reset(new TPCFastTransform); - mFastTransformNew->cloneFromObject(*mFastTransformHelper->getCorrMap(), nullptr); - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mFastTransformNew, 0, vd.corrFact, vd.refVDrift, vd.getTimeOffset()); - newCalibObjects.fastTransform = mFastTransformNew.get(); - } - if (mTPCVDriftHelper->isUpdated() || mFastTransformHelper->isUpdatedMapRef()) { - mFastTransformRefNew.reset(new TPCFastTransform); - mFastTransformRefNew->cloneFromObject(*mFastTransformHelper->getCorrMapRef(), nullptr); - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mFastTransformRefNew, 0, vd.corrFact, vd.refVDrift, vd.getTimeOffset()); - newCalibObjects.fastTransformRef = mFastTransformRefNew.get(); - } - if (mFastTransformNew || mFastTransformRefNew || mFastTransformHelper->isUpdatedLumi()) { - mFastTransformHelperNew.reset(new o2::tpc::CorrectionMapsLoader); - mFastTransformHelperNew->setInstLumi(mFastTransformHelper->getInstLumi(), false); - mFastTransformHelperNew->setMeanLumi(mFastTransformHelper->getMeanLumi(), false); - mFastTransformHelperNew->setUseCTPLumi(mFastTransformHelper->getUseCTPLumi()); - mFastTransformHelperNew->setMeanLumiOverride(mFastTransformHelper->getMeanLumiOverride()); - mFastTransformHelperNew->setInstLumiOverride(mFastTransformHelper->getInstLumiOverride()); - mFastTransformHelperNew->setLumiScaleMode(mFastTransformHelper->getLumiScaleMode()); - mFastTransformHelperNew->setCorrMap(mFastTransformNew ? mFastTransformNew.get() : mFastTransform.get()); - mFastTransformHelperNew->setCorrMapRef(mFastTransformRefNew ? mFastTransformRefNew.get() : mFastTransformRef.get()); - mFastTransformHelperNew->acknowledgeUpdate(); - newCalibObjects.fastTransformHelper = mFastTransformHelperNew.get(); - } - mMustUpdateFastTransform = true; - mTPCVDriftHelper->acknowledgeUpdate(); - mFastTransformHelper->acknowledgeUpdate(); - } - } - - if (mdEdxCalibContainerBufferNew) { - newCalibObjects.dEdxCalibContainer = mdEdxCalibContainerBufferNew.get(); - } - - if (mTPCPadGainCalibBufferNew) { - newCalibObjects.tpcPadGain = mTPCPadGainCalibBufferNew.get(); - } - - return mdEdxCalibContainerBufferNew || mTPCPadGainCalibBufferNew || mMustUpdateFastTransform; - } - return false; -} - -void GPURecoWorkflowSpec::storeUpdatedCalibsTPCPtrs() -{ - if (mdEdxCalibContainerBufferNew) { - mdEdxCalibContainer = std::move(mdEdxCalibContainerBufferNew); - } - - if (mTPCPadGainCalibBufferNew) { - mTPCPadGainCalib = std::move(mTPCPadGainCalibBufferNew); - } - - if (mFastTransformNew) { - mFastTransform = std::move(mFastTransformNew); - } - if (mFastTransformRefNew) { - mFastTransformRef = std::move(mFastTransformRefNew); - } - if (mFastTransformHelperNew) { - mFastTransformHelper = std::move(mFastTransformHelperNew); - } -} - void GPURecoWorkflowSpec::deinitialize() { + ExitPipeline(); mQA.reset(nullptr); mDisplayFrontend.reset(nullptr); - mTracker.reset(nullptr); + mGPUReco.reset(nullptr); } } // namespace o2::gpu diff --git a/GPU/Workflow/src/GPUWorkflowTPC.cxx b/GPU/Workflow/src/GPUWorkflowTPC.cxx new file mode 100644 index 0000000000000..3bc36716f8607 --- /dev/null +++ b/GPU/Workflow/src/GPUWorkflowTPC.cxx @@ -0,0 +1,390 @@ +// 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 GPUWorkflowTPC.cxx +/// @author David Rohr + +#include "GPUWorkflow/GPUWorkflowSpec.h" +#include "Headers/DataHeader.h" +#include "Framework/WorkflowSpec.h" // o2::framework::mergeInputs +#include "Framework/DataRefUtils.h" +#include "Framework/DataSpecUtils.h" +#include "Framework/DeviceSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/SerializationMethods.h" +#include "Framework/Logger.h" +#include "Framework/CallbackService.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsTPC/TPCSectorHeader.h" +#include "DataFormatsTPC/ClusterNative.h" +#include "DataFormatsTPC/CompressedClusters.h" +#include "DataFormatsTPC/Helpers.h" +#include "DataFormatsTPC/ZeroSuppression.h" +#include "DataFormatsTPC/RawDataTypes.h" +#include "DataFormatsTPC/WorkflowHelper.h" +#include "DataFormatsGlobalTracking/TrackTuneParams.h" +#include "TPCReconstruction/TPCTrackingDigitsPreCheck.h" +#include "TPCReconstruction/TPCFastTransformHelperO2.h" +#include "DataFormatsTPC/Digit.h" +#include "TPCFastTransform.h" +#include "DetectorsBase/MatLayerCylSet.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GeometryManager.h" +#include "DetectorsRaw/HBFUtils.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "CommonUtils/NameConf.h" +#include "TPCBase/RDHUtils.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUO2InterfaceQA.h" +#include "GPUO2Interface.h" +#include "CalibdEdxContainer.h" +#include "GPUNewCalibValues.h" +#include "TPCPadGainCalib.h" +#include "TPCZSLinkMapping.h" +#include "display/GPUDisplayInterface.h" +#include "TPCBase/Sector.h" +#include "TPCBase/Utils.h" +#include "TPCBase/CDBInterface.h" +#include "TPCCalibration/VDriftHelper.h" +#include "CorrectionMapsHelper.h" +#include "TPCCalibration/CorrectionMapsLoader.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "Algorithm/Parser.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DataFormatsTRD/RecoInputContainer.h" +#include "TRDBase/Geometry.h" +#include "TRDBase/GeometryFlat.h" +#include "ITSBase/GeometryTGeo.h" +#include "CommonUtils/VerbosityConfig.h" +#include "CommonUtils/DebugStreamer.h" +#include +#include // for make_shared +#include +#include +#include +#include +#include +#include +#include +#include +#include "GPUReconstructionConvert.h" +#include "DetectorsRaw/RDHUtils.h" +#include +#include +#include +#include +#include +#include + +using namespace o2::framework; +using namespace o2::header; +using namespace o2::gpu; +using namespace o2::base; +using namespace o2::dataformats; + +namespace o2::gpu +{ + +void GPURecoWorkflowSpec::initFunctionTPCCalib(InitContext& ic) +{ + mCalibObjects.mdEdxCalibContainer.reset(new o2::tpc::CalibdEdxContainer()); + mTPCVDriftHelper.reset(new o2::tpc::VDriftHelper()); + mCalibObjects.mFastTransformHelper.reset(new o2::tpc::CorrectionMapsLoader()); + mCalibObjects.mFastTransform = std::move(o2::tpc::TPCFastTransformHelperO2::instance()->create(0)); + mCalibObjects.mFastTransformRef = std::move(o2::tpc::TPCFastTransformHelperO2::instance()->create(0)); + mCalibObjects.mFastTransformHelper->setCorrMap(mCalibObjects.mFastTransform.get()); // just to reserve the space + mCalibObjects.mFastTransformHelper->setCorrMapRef(mCalibObjects.mFastTransformRef.get()); + mCalibObjects.mFastTransformHelper->setLumiScaleMode(mSpecConfig.lumiScaleMode); + if (mSpecConfig.outputTracks) { + mCalibObjects.mFastTransformHelper->init(ic); + } + if (mConfParam->dEdxDisableTopologyPol) { + LOGP(info, "Disabling loading of track topology correction using polynomials from CCDB"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol); + } + + if (mConfParam->dEdxDisableThresholdMap) { + LOGP(info, "Disabling loading of threshold map from CCDB"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalThresholdMap); + } + + if (mConfParam->dEdxDisableGainMap) { + LOGP(info, "Disabling loading of gain map from CCDB"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalGainMap); + } + + if (mConfParam->dEdxDisableResidualGainMap) { + LOGP(info, "Disabling loading of residual gain map from CCDB"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalResidualGainMap); + } + + if (mConfParam->dEdxDisableResidualGain) { + LOGP(info, "Disabling loading of residual gain calibration from CCDB"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTimeGain); + } + + if (mConfParam->dEdxUseFullGainMap) { + LOGP(info, "Using the full gain map for correcting the cluster charge during calculation of the dE/dx"); + mCalibObjects.mdEdxCalibContainer->setUsageOfFullGainMap(true); + } + + if (mConfParam->gainCalibDisableCCDB) { + LOGP(info, "Disabling loading the TPC pad gain calibration from the CCDB"); + mUpdateGainMapCCDB = false; + } + + // load from file + if (!mConfParam->dEdxPolTopologyCorrFile.empty() || !mConfParam->dEdxCorrFile.empty() || !mConfParam->dEdxSplineTopologyCorrFile.empty()) { + if (!mConfParam->dEdxPolTopologyCorrFile.empty()) { + LOGP(info, "Loading dE/dx polynomial track topology correction from file: {}", mConfParam->dEdxPolTopologyCorrFile); + mCalibObjects.mdEdxCalibContainer->loadPolTopologyCorrectionFromFile(mConfParam->dEdxPolTopologyCorrFile); + + LOGP(info, "Disabling loading of track topology correction using polynomials from CCDB as it was already loaded from input file"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol); + + if (std::filesystem::exists(mConfParam->thresholdCalibFile)) { + LOG(info) << "Loading tpc zero supression map from file " << mConfParam->thresholdCalibFile; + const auto* thresholdMap = o2::tpc::utils::readCalPads(mConfParam->thresholdCalibFile, "ThresholdMap")[0]; + mCalibObjects.mdEdxCalibContainer->setZeroSupresssionThreshold(*thresholdMap); + + LOGP(info, "Disabling loading of threshold map from CCDB as it was already loaded from input file"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalThresholdMap); + } else { + if (not mConfParam->thresholdCalibFile.empty()) { + LOG(warn) << "Couldn't find tpc zero supression file " << mConfParam->thresholdCalibFile << ". Not setting any zero supression."; + } + LOG(info) << "Setting default zero supression map"; + mCalibObjects.mdEdxCalibContainer->setDefaultZeroSupresssionThreshold(); + } + } else if (!mConfParam->dEdxSplineTopologyCorrFile.empty()) { + LOGP(info, "Loading dE/dx spline track topology correction from file: {}", mConfParam->dEdxSplineTopologyCorrFile); + mCalibObjects.mdEdxCalibContainer->loadSplineTopologyCorrectionFromFile(mConfParam->dEdxSplineTopologyCorrFile); + + LOGP(info, "Disabling loading of track topology correction using polynomials from CCDB as splines were loaded from input file"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol); + } + if (!mConfParam->dEdxCorrFile.empty()) { + LOGP(info, "Loading dEdx correction from file: {}", mConfParam->dEdxCorrFile); + mCalibObjects.mdEdxCalibContainer->loadResidualCorrectionFromFile(mConfParam->dEdxCorrFile); + + LOGP(info, "Disabling loading of residual gain calibration from CCDB as it was already loaded from input file"); + mCalibObjects.mdEdxCalibContainer->disableCorrectionCCDB(o2::tpc::CalibsdEdx::CalTimeGain); + } + } + + if (mConfParam->dEdxPolTopologyCorrFile.empty() && mConfParam->dEdxSplineTopologyCorrFile.empty()) { + // setting default topology correction to allocate enough memory + LOG(info) << "Setting default dE/dx polynomial track topology correction to allocate enough memory"; + mCalibObjects.mdEdxCalibContainer->setDefaultPolTopologyCorrection(); + } + + GPUO2InterfaceConfiguration& config = *mConfig.get(); + mConfig->configCalib.dEdxCalibContainer = mCalibObjects.mdEdxCalibContainer.get(); + + if (std::filesystem::exists(mConfParam->gainCalibFile)) { + LOG(info) << "Loading tpc gain correction from file " << mConfParam->gainCalibFile; + const auto* gainMap = o2::tpc::utils::readCalPads(mConfParam->gainCalibFile, "GainMap")[0]; + mCalibObjects.mTPCPadGainCalib = GPUO2Interface::getPadGainCalib(*gainMap); + + LOGP(info, "Disabling loading the TPC gain correction map from the CCDB as it was already loaded from input file"); + mUpdateGainMapCCDB = false; + } else { + if (not mConfParam->gainCalibFile.empty()) { + LOG(warn) << "Couldn't find tpc gain correction file " << mConfParam->gainCalibFile << ". Not applying any gain correction."; + } + mCalibObjects.mTPCPadGainCalib = GPUO2Interface::getPadGainCalibDefault(); + mCalibObjects.mTPCPadGainCalib->getGainCorrection(30, 5, 5); + } + mConfig->configCalib.tpcPadGain = mCalibObjects.mTPCPadGainCalib.get(); + + mTPCZSLinkMapping.reset(new TPCZSLinkMapping{tpc::Mapper::instance()}); + mConfig->configCalib.tpcZSLinkMapping = mTPCZSLinkMapping.get(); +} + +void GPURecoWorkflowSpec::finaliseCCDBTPC(ConcreteDataMatcher& matcher, void* obj) +{ + const o2::tpc::CalibdEdxContainer* dEdxCalibContainer = mCalibObjects.mdEdxCalibContainer.get(); + + auto copyCalibsToBuffer = [this, dEdxCalibContainer]() { + if (!(mdEdxCalibContainerBufferNew)) { + mdEdxCalibContainerBufferNew = std::make_unique(); + mdEdxCalibContainerBufferNew->cloneFromObject(*dEdxCalibContainer, nullptr); + } + }; + + if (matcher == ConcreteDataMatcher(gDataOriginTPC, "PADGAINFULL", 0)) { + LOGP(info, "Updating gain map from CCDB"); + const auto* gainMap = static_cast*>(obj); + + if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalGainMap) && mSpecConfig.outputTracks) { + copyCalibsToBuffer(); + const float minGain = 0; + const float maxGain = 2; + mdEdxCalibContainerBufferNew.get()->setGainMap(*gainMap, minGain, maxGain); + } + + if (mUpdateGainMapCCDB && mSpecConfig.caClusterer) { + mTPCPadGainCalibBufferNew = GPUO2Interface::getPadGainCalib(*gainMap); + } + + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "PADGAINRESIDUAL", 0)) { + LOGP(info, "Updating residual gain map from CCDB"); + copyCalibsToBuffer(); + const auto* gainMapResidual = static_cast>*>(obj); + const float minResidualGain = 0.7f; + const float maxResidualGain = 1.3f; + mdEdxCalibContainerBufferNew.get()->setGainMapResidual(gainMapResidual->at("GainMap"), minResidualGain, maxResidualGain); + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "PADTHRESHOLD", 0)) { + LOGP(info, "Updating threshold map from CCDB"); + copyCalibsToBuffer(); + const auto* thresholdMap = static_cast>*>(obj); + mdEdxCalibContainerBufferNew.get()->setZeroSupresssionThreshold(thresholdMap->at("ThresholdMap")); + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "TOPOLOGYGAIN", 0) && !(dEdxCalibContainer->isTopologyCorrectionSplinesSet())) { + LOGP(info, "Updating Q topology correction from CCDB"); + copyCalibsToBuffer(); + const auto* topologyCorr = static_cast(obj); + o2::tpc::CalibdEdxTrackTopologyPol calibTrackTopology; + calibTrackTopology.setFromContainer(*topologyCorr); + mdEdxCalibContainerBufferNew->setPolTopologyCorrection(calibTrackTopology); + } else if (matcher == ConcreteDataMatcher(gDataOriginTPC, "TIMEGAIN", 0)) { + LOGP(info, "Updating residual gain correction from CCDB"); + copyCalibsToBuffer(); + const auto* residualCorr = static_cast(obj); + mdEdxCalibContainerBufferNew->setResidualCorrection(*residualCorr); + } else if (mTPCVDriftHelper->accountCCDBInputs(matcher, obj)) { + } else if (mCalibObjects.mFastTransformHelper->accountCCDBInputs(matcher, obj)) { + } +} + +template <> +bool GPURecoWorkflowSpec::fetchCalibsCCDBTPC(ProcessingContext& pc, GPUCalibObjectsConst& newCalibObjects, calibObjectStruct& oldCalibObjects) +{ + // update calibrations for clustering and tracking + bool mustUpdate = false; + if ((mSpecConfig.outputTracks || mSpecConfig.caClusterer) && !mConfParam->disableCalibUpdates) { + const o2::tpc::CalibdEdxContainer* dEdxCalibContainer = mCalibObjects.mdEdxCalibContainer.get(); + + // this calibration is defined for clustering and tracking + if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalGainMap) || mUpdateGainMapCCDB) { + pc.inputs().get*>("tpcgain"); + } + + // these calibrations are only defined for the tracking + if (mSpecConfig.outputTracks) { + // update the calibration objects in case they changed in the CCDB + if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalThresholdMap)) { + pc.inputs().get>*>("tpcthreshold"); + } + + if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalResidualGainMap)) { + pc.inputs().get>*>("tpcgainresidual"); + } + + if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalTopologyPol)) { + pc.inputs().get("tpctopologygain"); + } + + if (dEdxCalibContainer->isCorrectionCCDB(o2::tpc::CalibsdEdx::CalTimeGain)) { + pc.inputs().get("tpctimegain"); + } + + if (mSpecConfig.outputTracks) { + mTPCVDriftHelper->extractCCDBInputs(pc); + mCalibObjects.mFastTransformHelper->extractCCDBInputs(pc); + } + if (mTPCVDriftHelper->isUpdated() || mCalibObjects.mFastTransformHelper->isUpdated()) { + const auto& vd = mTPCVDriftHelper->getVDriftObject(); + LOGP(info, "Updating{}TPC fast transform map and/or VDrift factor of {} wrt reference {} and TDrift offset {} wrt reference {} from source {}", + mCalibObjects.mFastTransformHelper->isUpdated() ? " new " : " old ", + vd.corrFact, vd.refVDrift, vd.timeOffsetCorr, vd.refTimeOffset, mTPCVDriftHelper->getSourceName()); + + bool mustUpdateHelper = false; + if (mTPCVDriftHelper->isUpdated() || mCalibObjects.mFastTransformHelper->isUpdatedMap()) { + oldCalibObjects.mFastTransform = std::move(mCalibObjects.mFastTransform); + mCalibObjects.mFastTransform.reset(new TPCFastTransform); + mCalibObjects.mFastTransform->cloneFromObject(*mCalibObjects.mFastTransformHelper->getCorrMap(), nullptr); + o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCalibObjects.mFastTransform, 0, vd.corrFact, vd.refVDrift, vd.getTimeOffset()); + newCalibObjects.fastTransform = mCalibObjects.mFastTransform.get(); + mustUpdateHelper = true; + } + if (mTPCVDriftHelper->isUpdated() || mCalibObjects.mFastTransformHelper->isUpdatedMapRef()) { + oldCalibObjects.mFastTransformRef = std::move(mCalibObjects.mFastTransformRef); + mCalibObjects.mFastTransformRef.reset(new TPCFastTransform); + mCalibObjects.mFastTransformRef->cloneFromObject(*mCalibObjects.mFastTransformHelper->getCorrMapRef(), nullptr); + o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCalibObjects.mFastTransformRef, 0, vd.corrFact, vd.refVDrift, vd.getTimeOffset()); + newCalibObjects.fastTransformRef = mCalibObjects.mFastTransformRef.get(); + mustUpdateHelper = true; + } + if (mustUpdateHelper || mCalibObjects.mFastTransformHelper->isUpdatedLumi()) { + oldCalibObjects.mFastTransformHelper = std::move(mCalibObjects.mFastTransformHelper); + mCalibObjects.mFastTransformHelper.reset(new o2::tpc::CorrectionMapsLoader); + mCalibObjects.mFastTransformHelper->copySettings(*oldCalibObjects.mFastTransformHelper); + mCalibObjects.mFastTransformHelper->setCorrMap(mCalibObjects.mFastTransform.get()); + mCalibObjects.mFastTransformHelper->setCorrMapRef(mCalibObjects.mFastTransformRef.get()); + mCalibObjects.mFastTransformHelper->acknowledgeUpdate(); + newCalibObjects.fastTransformHelper = mCalibObjects.mFastTransformHelper.get(); + } + mustUpdate = true; + mTPCVDriftHelper->acknowledgeUpdate(); + mCalibObjects.mFastTransformHelper->acknowledgeUpdate(); + } + } + + if (mdEdxCalibContainerBufferNew) { + oldCalibObjects.mdEdxCalibContainer = std::move(mCalibObjects.mdEdxCalibContainer); + mCalibObjects.mdEdxCalibContainer = std::move(mdEdxCalibContainerBufferNew); + newCalibObjects.dEdxCalibContainer = mCalibObjects.mdEdxCalibContainer.get(); + mustUpdate = true; + } + + if (mTPCPadGainCalibBufferNew) { + oldCalibObjects.mTPCPadGainCalib = std::move(mCalibObjects.mTPCPadGainCalib); + mCalibObjects.mTPCPadGainCalib = std::move(mTPCPadGainCalibBufferNew); + newCalibObjects.tpcPadGain = mCalibObjects.mTPCPadGainCalib.get(); + mustUpdate = true; + } + } + return mustUpdate; +} + +void GPURecoWorkflowSpec::doTrackTuneTPC(GPUTrackingInOutPointers& ptrs, char* buffout) +{ + using TrackTunePar = o2::globaltracking::TrackTuneParams; + const auto& trackTune = TrackTunePar::Instance(); + if (ptrs.nOutputTracksTPCO2 && trackTune.sourceLevelTPC && + (trackTune.useTPCInnerCorr || trackTune.useTPCOuterCorr || + trackTune.tpcCovInnerType != TrackTunePar::AddCovType::Disable || trackTune.tpcCovOuterType != TrackTunePar::AddCovType::Disable)) { + if (((const void*)ptrs.outputTracksTPCO2) != ((const void*)buffout)) { + throw std::runtime_error("Buffer does not match span"); + } + o2::tpc::TrackTPC* tpcTracks = reinterpret_cast(buffout); + for (unsigned int itr = 0; itr < ptrs.nOutputTracksTPCO2; itr++) { + auto& trc = tpcTracks[itr]; + if (trackTune.useTPCInnerCorr) { + trc.updateParams(trackTune.tpcParInner); + } + if (trackTune.tpcCovInnerType != TrackTunePar::AddCovType::Disable) { + trc.updateCov(trackTune.tpcCovInner, trackTune.tpcCovInnerType == TrackTunePar::AddCovType::WithCorrelations); + } + if (trackTune.useTPCOuterCorr) { + trc.getParamOut().updateParams(trackTune.tpcParOuter); + } + if (trackTune.tpcCovOuterType != TrackTunePar::AddCovType::Disable) { + trc.getParamOut().updateCov(trackTune.tpcCovOuter, trackTune.tpcCovOuterType == TrackTunePar::AddCovType::WithCorrelations); + } + } + } +} + +} // namespace o2::gpu diff --git a/GPU/Workflow/src/gpu-reco-workflow.cxx b/GPU/Workflow/src/gpu-reco-workflow.cxx index d73408e984862..006785ec02318 100644 --- a/GPU/Workflow/src/gpu-reco-workflow.cxx +++ b/GPU/Workflow/src/gpu-reco-workflow.cxx @@ -38,6 +38,7 @@ using namespace o2::gpu; using CompletionPolicyData = std::vector; static CompletionPolicyData gPolicyData; static constexpr unsigned long gTpcSectorMask = 0xFFFFFFFFF; +static std::function* gPolicyOrderCheck; static std::shared_ptr gTask; void customize(std::vector& policies) @@ -50,13 +51,14 @@ void customize(std::vector& workflowOptions) std::vector options{ {"input-type", VariantType::String, "digits", {"digitizer, digits, zsraw, zsonthefly, clustersnative, compressed-clusters-root, compressed-clusters-ctf, trd-tracklets"}}, - {"output-type", VariantType::String, "tracks", {"clustersnative, tracks, compressed-clusters-ctf, qa, no-shared-cluster-map, send-clusters-per-sector, trd-tracks, error-qa"}}, + {"output-type", VariantType::String, "tracks", {"clustersnative, tracks, compressed-clusters-ctf, qa, no-shared-cluster-map, send-clusters-per-sector, trd-tracks, error-qa, tpc-triggers"}}, {"require-ctp-lumi", VariantType::Bool, false, {"require CTP lumi for TPC correction scaling"}}, {"disable-root-input", VariantType::Bool, true, {"disable root-files input reader"}}, {"disable-mc", VariantType::Bool, false, {"disable sending of MC information"}}, {"ignore-dist-stf", VariantType::Bool, false, {"do not subscribe to FLP/DISTSUBTIMEFRAME/0 message (no lost TF recovery)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCHwClusterer.peakChargeThreshold=4;...')"}}, - {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}}; + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"enableDoublePipeline", VariantType::Bool, false, {"enable GPU double pipeline mode"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -70,7 +72,7 @@ void customize(std::vector& policies) void customize(std::vector& policies) { - policies.push_back(o2::tpc::TPCSectorCompletionPolicy("gpu-reconstruction.*", o2::tpc::TPCSectorCompletionPolicy::Config::RequireAll, &gPolicyData, &gTpcSectorMask)()); + policies.push_back(o2::tpc::TPCSectorCompletionPolicy("gpu-reconstruction.*", o2::tpc::TPCSectorCompletionPolicy::Config::RequireAll, &gPolicyData, &gTpcSectorMask, &gPolicyOrderCheck)()); } void customize(o2::framework::OnWorkflowTerminationHook& hook) @@ -100,7 +102,8 @@ enum struct ioType { Digits, NoSharedMap, SendClustersPerSector, ITSClusters, - ITSTracks }; + ITSTracks, + TPCTriggers }; static const std::unordered_map InputMap{ {"digits", ioType::Digits}, @@ -121,7 +124,8 @@ static const std::unordered_map OutputMap{ {"no-shared-cluster-map", ioType::NoSharedMap}, {"send-clusters-per-sector", ioType::SendClustersPerSector}, {"trd-tracks", ioType::TRDTracks}, - {"its-tracks", ioType::ITSTracks}}; + {"its-tracks", ioType::ITSTracks}, + {"tpc-triggers", ioType::TPCTriggers}}; WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) { @@ -169,13 +173,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) cfg.askDISTSTF = !cfgc.options().get("ignore-dist-stf"); cfg.readTRDtracklets = isEnabled(inputTypes, ioType::TRDTracklets); cfg.runTRDTracking = isEnabled(outputTypes, ioType::TRDTracks); + cfg.tpcTriggerHandling = isEnabled(outputTypes, ioType::TPCTriggers) || cfg.caClusterer; + cfg.enableDoublePipeline = cfgc.options().get("enableDoublePipeline"); Inputs ggInputs; auto ggRequest = std::make_shared(false, true, false, true, true, o2::base::GRPGeomRequest::Aligned, ggInputs, true); - auto task = std::make_shared(&gPolicyData, cfg, tpcSectors, gTpcSectorMask, ggRequest); + auto task = std::make_shared(&gPolicyData, cfg, tpcSectors, gTpcSectorMask, ggRequest, &gPolicyOrderCheck); Inputs taskInputs = task->inputs(); - Options taskOptions = task->options(); std::move(ggInputs.begin(), ggInputs.end(), std::back_inserter(taskInputs)); gTask = task; @@ -184,7 +189,22 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) taskInputs, task->outputs(), AlgorithmSpec{adoptTask(task)}, - taskOptions}); + task->options()}); + + if (cfg.enableDoublePipeline) { + cfg.enableDoublePipeline = 2; + Inputs ggInputsPrepare; + auto ggRequestPrepare = std::make_shared(false, true, false, false, false, o2::base::GRPGeomRequest::None, ggInputsPrepare, true); + auto taskPrepare = std::make_shared(&gPolicyData, cfg, tpcSectors, gTpcSectorMask, ggRequestPrepare); + Inputs taskInputsPrepare = taskPrepare->inputs(); + std::move(ggInputsPrepare.begin(), ggInputsPrepare.end(), std::back_inserter(taskInputsPrepare)); + specs.emplace_back(DataProcessorSpec{ + "gpu-reconstruction-prepare", + taskInputsPrepare, + taskPrepare->outputs(), + AlgorithmSpec{adoptTask(taskPrepare)}, + taskPrepare->options()}); + } if (!cfgc.options().get("ignore-dist-stf")) { GlobalTrackID::mask_t srcTrk = GlobalTrackID::getSourcesMask("none"); diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 0b0f92955a651..f8551a9e5e8ba 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -29,6 +29,8 @@ o2_add_library(Generators src/GeneratorExternalParam.cxx src/GeneratorFromFile.cxx src/GeneratorFromO2KineParam.cxx + src/GeneratorFileOrCmd.cxx + src/GeneratorFileOrCmdParam.cxx src/PrimaryGenerator.cxx src/PrimaryGeneratorParam.cxx src/TriggerExternalParam.cxx @@ -38,6 +40,8 @@ o2_add_library(Generators src/GenCosmicsParam.cxx src/GeneratorFactory.cxx src/GeneratorGeantinos.cxx + src/GeneratorTParticle.cxx + src/GeneratorTParticleParam.cxx $<$:src/GeneratorPythia6.cxx> $<$:src/GeneratorPythia6Param.cxx> $<$:src/GeneratorPythia8.cxx> @@ -71,6 +75,8 @@ set(headers include/Generators/GeneratorExternalParam.h include/Generators/GeneratorFromFile.h include/Generators/GeneratorFromO2KineParam.h + include/Generators/GeneratorFileOrCmd.h + include/Generators/GeneratorFileOrCmdParam.h include/Generators/PrimaryGenerator.h include/Generators/PrimaryGeneratorParam.h include/Generators/TriggerExternalParam.h @@ -79,6 +85,8 @@ set(headers include/Generators/QEDGenParam.h include/Generators/GenCosmicsParam.h include/Generators/GeneratorGeantinos.h + include/Generators/GeneratorTParticle.h + include/Generators/GeneratorTParticleParam.h ) if (pythia6_FOUND) diff --git a/Generators/include/Generators/GeneratorFileOrCmd.h b/Generators/include/Generators/GeneratorFileOrCmd.h new file mode 100644 index 0000000000000..0c9c618928549 --- /dev/null +++ b/Generators/include/Generators/GeneratorFileOrCmd.h @@ -0,0 +1,243 @@ +// 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. + +/// @author Christian Holm Christensen + +#ifndef ALICEO2_EVENTGEN_GENERATORFILEORCMD_H_ +#define ALICEO2_EVENTGEN_GENERATORFILEORCMD_H_ +#include +#include +#include + +namespace o2 +{ +namespace conf +{ +class SimConfig; +} +namespace eventgen +{ + +/** Service class for either reading from a file or executing a + program writing to a specific file */ +struct GeneratorFileOrCmd { + /** + * Configure the generator from parameters and the general + * simulation configuration. This is implemented as a member + * function so as to better facilitate changes. */ + void setup(const GeneratorFileOrCmdParam& param, + const conf::SimConfig& config); + /** + * Set command to execute in bacground rather than reading from + * existing file(s) + * + * @param cmd Command line. Can include options for the program to + * execute, but should not include pipes. + */ + void setCmd(const std::string& cmd) { mCmd = cmd; } + /** + * Set the number of events that a background command should + * generate. This should come from @c SimConfig::getNEents. + * + * @param nev Number of events to generate. This is passed via @c + * mNEventsSwitch to the command line. + */ + void setNEvents(unsigned int nev) { mNEvents = nev; } + /** + * Set the random number seed that a background command should use. + * This should come from @c SimConfig::getStartSeed + * + * @param seed Random number seed. Will be passed to the + * commandline using the @c mSeedSwitch. + */ + void setSeed(unsigned long seed) { mSeed = seed; } + /** + * Set the maximum impact parameter to sample by a background + * command. This should come from @c SimConfig::getBMax() + * + * @param bmax Maximum impact parameter, in fm, to sample. This is + * passed to the command line via the @c mBmaxSwitch. + */ + void setBmax(float bmax) { mBmax = bmax; } + /** + * Set the file names to read from. + * + * @param filenames A comma seperated list of files to read from. + */ + void setFileNames(const std::string& filenames); + /** + * Set the output switch. + * + * @param opt Command line switch (e.g., @c -o) to specify output + * file name. */ + void setOutputSwitch(const std::string& opt) { mOutputSwitch = opt; } + /** + * Set the seed switch. + * + * @param opt Command line switch (e.g., @c -s) to specify the + * random number seed to use when generating events. + */ + void setSeedSwitch(const std::string& opt) { mSeedSwitch = opt; } + /** + * Set the nevents switch. + * + * @param opt Command line switch (e.g., @c -n) to specify the + * number of events to generate. + */ + void setNEventsSwitch(const std::string& opt) { mNEventsSwitch = opt; } + /** + * Set the maximum impact parameter switch. + * + * @param opt Command line switch (e.g., @c -b) to specify the + * maximum impact parameter (in fm) to sample when generating + * events. + */ + void setBmaxSwitch(const std::string& opt) { mBmaxSwitch = opt; } + /** + * Set the background switch. + * + * @param opt Command line switch (e.g., @c &) to detach and send + * the event generator program into the background. + */ + void setBackgroundSwitch(const std::string& opt) { mBackgroundSwitch = opt; } + /** Set the wait time, in miliseconds, when waiting for data */ + void setWait(int miliseconds = 500) { mWait = miliseconds; } + + protected: + /** + * Format a command line using the set command line, option flags, + * and option values. + * + * @return formatted command line. + */ + virtual std::string makeCmdLine() const; + /** + * Execute a command line (presumably formatted by @c makeCmdLine). + * If the command failed to execute, then make it a fatal error. + * + * @param cmd Command line to execute, presumabley formatted by @c + * makeCmdLine. + + * @return true if the background command line was executed, false + * otherwise. + */ + virtual bool executeCmdLine(const std::string& cmd) const; + /** + * Create a temporary file (and close it immediately). On success, + * the list of file names is cleared and the name of the temporary + * file set as the sole element in that list. + * + * @return true if the temporary file name was generated + * successfully. + */ + virtual bool makeTemp(); + /** + * Remove the temporary file if it was set and it exists. + * + * @return true if the temporary file was removed. + */ + virtual bool removeTemp() const; + /** + * Make a fifo at the location of the first element of the list of + * file names (presumably a temporary file as created by + * makeTemp). + * + * @return true if the FIFo was made correctly + */ + virtual bool makeFifo() const; + /** + * Ensure that all files in the list of file names exists. If @e + * any of te file names point to a net resource (e.g., @c alien://or + * @c https://) then this member function should @e not be called + * + * The file names registered are replaced with their canonical path + * equivalents. + * + * @return true if all currently registered file names can be found. + */ + virtual bool ensureFiles(); + /** + * Wait for data to be available in case we're executing a + * background command + */ + virtual void waitForData(const std::string& filename) const; + /** + * Possible command line to execute. The command executed must + * accept the switches defined below. Note if @c mOutputSwitch is + * set to @c ">", then the program @e must write data to standard + * output + */ + std::string mCmd = ""; + /** + * List of file names to read. In case we're executing a command, + * then there will only be one file name in the list + */ + std::list mFileNames; + /** + * Name of temporary file, if it was created. + */ + std::string mTemporary; + /** + * Number of events to generate in case we're executing a command. + * This is passed to the program via the switch @c + * mNEventsSwitch + */ + unsigned int mNEvents = 0; + /** + * Random number seed to use in case we're executing a command. + * This is passed to the program via the switch @c + * mSeedSwitch + */ + unsigned long mSeed = 0; + /** + * Maximum impact parameter to sample in case we're executing a + * command. IF negative, then it is not passed to the command. + * This is passed to the command via the switch @c mBmaxSwitch + */ + float mBmax = -1.f; + /** + * Switch to direct output to specified file. In case of a fifo, + * this should often be @c ">" - i.e., the standard output of the + * program is redirected to the fifo. + */ + std::string mOutputSwitch = ">"; + /** + * The switch specify the random number seed to the program + * executed + */ + std::string mSeedSwitch = "-s"; + /** + * The switch to specify the number of events to generate to the + * program being executed + */ + std::string mNEventsSwitch = "-n"; + /** + * The switch to specify maximum impact parameter to sample by the + * program being executed. + */ + std::string mBmaxSwitch = "-b"; + /** + * The "switch" to put the program being executed in the + * background. + */ + std::string mBackgroundSwitch = "&"; + /** + * Time in miliseconds between each wait for data + */ + int mWait = 500; +}; + +} // namespace eventgen +} // namespace o2 +#endif +// Local Variables: +// mode: C++ +// End: diff --git a/Generators/include/Generators/GeneratorFileOrCmdParam.h b/Generators/include/Generators/GeneratorFileOrCmdParam.h new file mode 100644 index 0000000000000..fe6dfa3a80722 --- /dev/null +++ b/Generators/include/Generators/GeneratorFileOrCmdParam.h @@ -0,0 +1,45 @@ +// Copyright 2023-2099 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. + +/// @author Christian Holm Christensen + +#ifndef ALICEO2_EVENTGEN_GENERATORFILEORCMDPARAM_H_ +#define ALICEO2_EVENTGEN_GENERATORFILEORCMDPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include + +namespace o2 +{ +namespace eventgen +{ + +/** + ** a parameter class/struct to keep the settings of + ** the Fileorcmd event generator and + ** allow the user to modify them + **/ +struct GeneratorFileOrCmdParam : public o2::conf::ConfigurableParamHelper { + std::string fileNames = ""; + std::string cmd = ""; // Program command line to spawn + std::string outputSwitch = ">"; + std::string seedSwitch = "-s"; + std::string bMaxSwitch = "-b"; + std::string nEventsSwitch = "-n"; + std::string backgroundSwitch = "&"; + O2ParamDef(GeneratorFileOrCmdParam, "GeneratorFileOrCmd"); +}; + +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_GENERATORFILEORCMDPARAM_H_ diff --git a/Generators/include/Generators/GeneratorHepMC.h b/Generators/include/Generators/GeneratorHepMC.h index 341f4b8a0e38a..9a5c607f209da 100644 --- a/Generators/include/Generators/GeneratorHepMC.h +++ b/Generators/include/Generators/GeneratorHepMC.h @@ -15,7 +15,8 @@ #define ALICEO2_EVENTGEN_GENERATORHEPMC_H_ #include "Generators/Generator.h" -#include +#include "Generators/GeneratorFileOrCmd.h" +#include "Generators/GeneratorHepMCParam.h" #ifdef GENERATORS_WITH_HEPMC3_DEPRECATED namespace HepMC @@ -35,33 +36,51 @@ class FourVector; namespace o2 { +namespace conf +{ +class SimConfig; +} namespace eventgen { /*****************************************************************/ /*****************************************************************/ -class GeneratorHepMC : public Generator +class GeneratorHepMC : public Generator, public GeneratorFileOrCmd { public: /** default constructor **/ GeneratorHepMC(); /** constructor **/ - GeneratorHepMC(const Char_t* name, const Char_t* title = "ALICEo2 HepMC Generator"); + GeneratorHepMC(const Char_t* name, + const Char_t* title = "ALICEo2 HepMC Generator"); /** destructor **/ ~GeneratorHepMC() override; - /** Initialize the generator if needed **/ + /** Initialize the generator. **/ Bool_t Init() override; - /** methods to override **/ + /** + * Configure the generator from parameters and the general + * simulation configuration. This is implemented as a member + * function so as to better facilitate changes. */ + void setup(const GeneratorFileOrCmdParam& param0, + const GeneratorHepMCParam& param, + const conf::SimConfig& config); + /** + * Generate a single event. The event is read in from the current + * input file. Returns false if a new event could not be read. + **/ Bool_t generateEvent() override; + /** + * Import particles from the last read event into a vector + * TParticle. Returns false if no particles could be exported to + * the vector. + */ Bool_t importParticles() override; /** setters **/ - void setVersion(Int_t val) { mVersion = val; }; - void setFileName(std::string val) { mFileName = val; }; void setEventsToSkip(uint64_t val) { mEventsToSkip = val; }; protected: @@ -77,18 +96,17 @@ class GeneratorHepMC : public Generator const HepMC3::FourVector getBoostedVector(const HepMC3::FourVector& vector, Double_t boost); #endif + /** methods that can be overridded **/ + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override; + /** Make our reader */ + bool makeReader(); + /** HepMC interface **/ - std::ifstream mStream; //! - std::string mFileName; - Int_t mVersion; - uint64_t mEventsToSkip; -#ifdef GENERATORS_WITH_HEPMC3_DEPRECATED - HepMC::Reader* mReader; //! - HepMC::GenEvent* mEvent; //! -#else - HepMC3::Reader* mReader; //! - HepMC3::GenEvent* mEvent; //! -#endif + uint64_t mEventsToSkip = 0; + int mVersion = 0; + std::shared_ptr mReader; + /** Event structure */ + HepMC3::GenEvent* mEvent = nullptr; ClassDefOverride(GeneratorHepMC, 1); diff --git a/Generators/include/Generators/GeneratorHepMCParam.h b/Generators/include/Generators/GeneratorHepMCParam.h index 4c53918c70634..5df4013489f95 100644 --- a/Generators/include/Generators/GeneratorHepMCParam.h +++ b/Generators/include/Generators/GeneratorHepMCParam.h @@ -30,9 +30,9 @@ namespace eventgen **/ struct GeneratorHepMCParam : public o2::conf::ConfigurableParamHelper { - std::string fileName = ""; - int version = 2; + int version = 0; uint64_t eventsToSkip = 0; + std::string fileName = ""; O2ParamDef(GeneratorHepMCParam, "HepMC"); }; diff --git a/Generators/include/Generators/GeneratorTParticle.h b/Generators/include/Generators/GeneratorTParticle.h new file mode 100644 index 0000000000000..e4ddb5fa1f340 --- /dev/null +++ b/Generators/include/Generators/GeneratorTParticle.h @@ -0,0 +1,119 @@ +// Copyright 2023-2099 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. + +/// @author: Christian Holm Christensen +#ifndef ALICEO2_GENERATORTPARTICLE_H_ +#define ALICEO2_GENERATORTPARTICLE_H_ +#include +#include +#include +#include + +// Forward decls +class TChain; +class TParticle; +class TClonesArray; + +namespace o2 +{ +namespace conf +{ +class SimConfig; +} +namespace eventgen +{ +/// A class that reads in particles of class @c TParticle from a +/// branch in a @c TChain. +/// +/// Optionally, a program that generates such a @c TTree can be +/// spawn, and the @c TParticles written to a file from which this +/// object reads them in. This is done with +/// +/// --configKeyValues "TParticle.progCmd=" +/// +/// which will execute the specified EG program with the given +/// options. The EG program _must_ support the command line options +/// +/// -n NUMBER Number of events to generate +/// -o FILENAME Name of file to write to +/// +/// The tree name and particle branch names can be configured. +/// +/// --configKeyValues "TParticle.treeName=T,TParticle.branchName=P" +/// +/// File(s) to read are also configurable +/// +/// --configKeyValues "TParticle.fileNames=foo.root,bar.root" +/// +class GeneratorTParticle : public Generator, public GeneratorFileOrCmd +{ + public: + /** CTOR */ + GeneratorTParticle(); + /** CTOR */ + GeneratorTParticle(const std::string& name) + : Generator(name.c_str(), "ALICEo2 TParticle Generator") + { + } + /** DTOR */ + virtual ~GeneratorTParticle(); + + /** Initialize this generator. This will set up the chain. + * Optionally, if a command line was specified by @c + * TParticle.progCmd then that command line is executed in the + * background and events are read from the output file of that + * program */ + Bool_t Init() override; + + /** + * Configure the generator from parameters and the general + * simulation configuration. This is implemented as a member + * function so as to better facilitate changes. */ + void setup(const GeneratorFileOrCmdParam& param0, + const GeneratorTParticleParam& param, + const conf::SimConfig& config); + /** Read in the next entry from the chain. Returns false in case of + * errors or no more entries to read. */ + Bool_t generateEvent() override; + + /** Import the read-in particles into the steer particle stack */ + Bool_t importParticles() override; + + /** Set the name of the tree in the files. The tree _must_ reside + * in the top-level directory of the files. */ + void setTreeName(const std::string& val) { mTreeName = val; } + /** Set the branch name of the branch that holds a @c TClonesArray + * of @c TParticle objects */ + void setBranchName(const std::string& val) { mBranchName = val; } + + protected: + /** Name of the tree to read */ + std::string mTreeName = "T"; + /** Name of branch containing a TClonesArray of TParticle */ + std::string mBranchName = "Particles"; + /** Current entry */ + unsigned int mEntry = 0; + /** Chain of files */ + TChain* mChain = 0; + /** Array to read particles into */ + TClonesArray* mTParticles; + + ClassDefOverride(GeneratorTParticle, 1); +}; +} // namespace eventgen +} // namespace o2 +#endif +// Local Variables: +// mode: C++ +// End: +// +// EOF +// diff --git a/Generators/include/Generators/GeneratorTParticleParam.h b/Generators/include/Generators/GeneratorTParticleParam.h new file mode 100644 index 0000000000000..98059f3b0e1e6 --- /dev/null +++ b/Generators/include/Generators/GeneratorTParticleParam.h @@ -0,0 +1,39 @@ +// Copyright 2023-2099 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. + +// @author: Christian Holm Christensen +#ifndef ALICEO2_EVENTGEN_GENERATORTPARTICLEPARAM_H_ +#define ALICEO2_EVENTGEN_GENERATORTPARTICLEPARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include +namespace o2 +{ +namespace eventgen +{ + +/** + * a parameter class/struct to keep the settings of the TGenerator + * event generator and allow the user to modify them + */ +struct GeneratorTParticleParam : public o2::conf::ConfigurableParamHelper { + std::string treeName = "T"; + std::string branchName = "Particles"; + O2ParamDef(GeneratorTParticleParam, "GeneratorTParticle"); +}; +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_GENERATORHEPMCPARAM_H_ +// Local Variables: +// mode: C++ +// End: diff --git a/Generators/share/egconfig/pythia8_powheg.cfg b/Generators/share/egconfig/pythia8_powheg.cfg new file mode 100644 index 0000000000000..ed41ab695bb48 --- /dev/null +++ b/Generators/share/egconfig/pythia8_powheg.cfg @@ -0,0 +1,79 @@ +Beams:eCM 13000. # GeV +### processes +Next:numberShowLHA = 1 +Next:numberShowInfo = 1 +Next:numberShowProcess = 1 +Next:numberShowEvent = 1 +Main:timesAllowErrors = 10 +! Read LHE file from POWHEG +Beams:frameType = 4 +Beams:LHEF = powheg.lhe + +! Number of outgoing particles of POWHEG Born level process +! (i.e. not counting additional POWHEG radiation) +POWHEG:nFinal = 2 + +! How vetoing is performed: +! 0 - No vetoing is performed (userhooks are not loaded) +! 1 - Showers are started at the kinematical limit. +! Emissions are vetoed if pTemt > pThard. +! See also POWHEG:vetoCount below +POWHEG:veto = 1 + +! After 'vetoCount' accepted emissions in a row, no more emissions +! are checked. 'vetoCount = 0' means that no emissions are checked. +! Use a very large value, e.g. 10000, to have all emissions checked. +POWHEG:vetoCount = 3 + +! Selection of pThard (note, for events where there is no +! radiation, pThard is always set to be SCALUP): +! 0 - pThard = SCALUP (of the LHA/LHEF standard) +! 1 - the pT of the POWHEG emission is tested against all other +! incoming and outgoing partons, with the minimal value chosen +! 2 - the pT of all final-state partons is tested against all other +! incoming and outgoing partons, with the minimal value chosen +POWHEG:pThard = 2 + +! Selection of pTemt: +! 0 - pTemt is pT of the emitted parton w.r.t. radiating parton +! 1 - pT of the emission is checked against all incoming and outgoing +! partons. pTemt is set to the minimum of these values +! 2 - the pT of all final-state partons is tested against all other +! incoming and outgoing partons, with the minimal value chosen +! WARNING: the choice here can give significant variations in the final +! distributions, notably in the tail to large pT values. +POWHEG:pTemt = 0 + +! Selection of emitted parton for FSR +! 0 - Pythia definition of emitted +! 1 - Pythia definition of radiator +! 2 - Random selection of emitted or radiator +! 3 - Both are emitted and radiator are tried +POWHEG:emitted = 0 + +! pT definitions +! 0 - POWHEG ISR pT definition is used for both ISR and FSR +! 1 - POWHEG ISR pT and FSR d_ij definitions +! 2 - Pythia definitions +POWHEG:pTdef = 1 + +! MPI vetoing +! 0 - No MPI vetoing is done +! 1 - When there is no radiation, MPIs with a scale above pT_1 are vetoed, +! else MPIs with a scale above (pT_1 + pT_2 + pT_3) / 2 are vetoed +POWHEG:MPIveto = 0 +! Note that POWHEG:MPIveto = 1 should be combined with +! MultipartonInteractions:pTmaxMatch = 2 +! which here is taken care of in main31.cc. + +! QED vetoing +! 0 - No QED vetoing is done for pTemt > 0. +! 1 - QED vetoing is done for pTemt > 0. +! 2 - QED vetoing is done for pTemt > 0. If a photon is found +! with pT>pThard from the Born level process, the event is accepted +! and no further veto of this event is allowed (for any pTemt). +POWHEG:QEDveto = 2 + +### decays +ParticleDecays:limitTau0 on +ParticleDecays:tau0Max 10. diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 90f0d2dd3a5df..99a00f154c268 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -222,7 +222,7 @@ Bool_t void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) { - if (mSubGeneratorId < 0) { + if (subGeneratorId < 0) { LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; } mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); diff --git a/Generators/src/GeneratorFactory.cxx b/Generators/src/GeneratorFactory.cxx index ae47ede14f4a3..80e80dbefa091 100644 --- a/Generators/src/GeneratorFactory.cxx +++ b/Generators/src/GeneratorFactory.cxx @@ -18,6 +18,8 @@ #include #include #include +#include +#include #ifdef GENERATORS_WITH_PYTHIA6 #include #include @@ -86,6 +88,7 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair o2::O2DatabasePDG::addALICEParticles(TDatabasePDG::Instance()); auto genconfig = conf.getGenerator(); + LOG(info) << "** Generator to use: '" << genconfig << "'"; if (genconfig.compare("boxgen") == 0) { // a simple "box" generator configurable via BoxGunparam auto& boxparam = BoxGunParam::Instance(); @@ -156,16 +159,28 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair } } LOG(info) << "using external O2 kinematics"; + } else if (genconfig.compare("tparticle") == 0) { + // External ROOT file(s) with tree of TParticle in clones array, + // or external program generating such a file + auto& param0 = GeneratorFileOrCmdParam::Instance(); + auto& param = GeneratorTParticleParam::Instance(); + LOG(info) << "Init 'GeneratorTParticle' with the following parameters"; + LOG(info) << param0; + LOG(info) << param; + auto tgen = new o2::eventgen::GeneratorTParticle(); + tgen->setup(param0, param, conf); + primGen->AddGenerator(tgen); #ifdef GENERATORS_WITH_HEPMC3 } else if (genconfig.compare("hepmc") == 0) { - // external HepMC file + // external HepMC file, or external program writing HepMC event + // records to standard output. + auto& param0 = GeneratorFileOrCmdParam::Instance(); auto& param = GeneratorHepMCParam::Instance(); LOG(info) << "Init \'GeneratorHepMC\' with following parameters"; + LOG(info) << param0; LOG(info) << param; auto hepmcGen = new o2::eventgen::GeneratorHepMC(); - hepmcGen->setFileName(param.fileName); - hepmcGen->setVersion(param.version); - hepmcGen->setEventsToSkip(param.eventsToSkip); + hepmcGen->setup(param0, param, conf); primGen->AddGenerator(hepmcGen); #endif #ifdef GENERATORS_WITH_PYTHIA6 @@ -216,6 +231,11 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair auto py8config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_hi.cfg"; auto py8 = makePythia8Gen(py8config); primGen->AddGenerator(py8); + } else if (genconfig.compare("pythia8powheg") == 0) { + // pythia8 with powheg + auto py8config = std::string(std::getenv("O2_ROOT")) + "/share/Generators/egconfig/pythia8_powheg.cfg"; + auto py8 = makePythia8Gen(py8config); + primGen->AddGenerator(py8); #endif } else if (genconfig.compare("external") == 0 || genconfig.compare("extgen") == 0) { // external generator via configuration macro diff --git a/Generators/src/GeneratorFileOrCmd.cxx b/Generators/src/GeneratorFileOrCmd.cxx new file mode 100644 index 0000000000000..9ff45bcd9c867 --- /dev/null +++ b/Generators/src/GeneratorFileOrCmd.cxx @@ -0,0 +1,214 @@ +// 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. + +/// @author Christian Holm Christensen + +#include "SimulationDataFormat/MCUtils.h" +#include "Generators/GeneratorFileOrCmd.h" +// For fifo's and system call +#include +#include // POSIX only +#include // POISX only +#include +// For filesystem operations +#include +// Waits +#include +// Log messages +#include +#include + +namespace +{ +std::string ltrim(const std::string& s, const std::string& what = "\" ' ") +{ + auto start = s.find_first_not_of(what); + if (start == std::string::npos) { + return ""; + } + + return s.substr(start); +} + +std::string rtrim(const std::string& s, const std::string& what = "\"' ") +{ + auto end = s.find_last_not_of(what); + return s.substr(0, end + 1); +} + +std::string trim(const std::string& s, const std::string& what = "\"' ") +{ + return rtrim(ltrim(s, what), what); +} +} // namespace + +namespace o2 +{ +namespace eventgen +{ +// ----------------------------------------------------------------- +void GeneratorFileOrCmd::setup(const GeneratorFileOrCmdParam& param, + const conf::SimConfig& config) +{ + setFileNames(param.fileNames); + setCmd(param.cmd); + setOutputSwitch(trim(param.outputSwitch)); + setSeedSwitch(trim(param.seedSwitch)); + setBmaxSwitch(trim(param.bMaxSwitch)); + setNEventsSwitch(trim(param.nEventsSwitch)); + setBackgroundSwitch(trim(param.backgroundSwitch)); + setSeed(config.getStartSeed()); + setNEvents(config.getNEvents()); + setBmax(config.getBMax()); +} +// ----------------------------------------------------------------- +void GeneratorFileOrCmd::setFileNames(const std::string& filenames) +{ + std::stringstream s(filenames); + std::string f; + while (std::getline(s, f, ',')) { + mFileNames.push_back(f); + } +} +// ----------------------------------------------------------------- +std::string GeneratorFileOrCmd::makeCmdLine() const +{ + std::string fileName = mFileNames.front(); + std::stringstream s; + s << mCmd << " "; + if (not mSeedSwitch.empty() and mSeedSwitch != "none") { + s << mSeedSwitch << " " << mSeed << " "; + } + if (not mNEventsSwitch.empty() and mNEventsSwitch != "none") { + s << mNEventsSwitch << " " << mNEvents << " "; + } + if (not mBmaxSwitch.empty() and mBmax >= 0 and mBmaxSwitch != "none") { + s << mBmaxSwitch.c_str() << " " << mBmax << " "; + } + + s << mOutputSwitch << " " << fileName << " " + << mBackgroundSwitch; + return s.str(); +} +// ----------------------------------------------------------------- +bool GeneratorFileOrCmd::executeCmdLine(const std::string& cmd) const +{ + LOG(info) << "Command line to execute: \"" << cmd << "\""; + int ret = std::system(cmd.c_str()); + if (ret != 0) { + LOG(fatal) << "Failed to spawn \"" << cmd << "\""; + return false; + } + return true; +} +// ----------------------------------------------------------------- +bool GeneratorFileOrCmd::makeTemp() +{ + mFileNames.clear(); + char buf[] = "generatorFifoXXXXXX"; + auto fp = mkstemp(buf); + if (fp < 0) { + LOG(fatal) << "Failed to make temporary file: " + << std::strerror(errno); + return false; + } + mTemporary = std::string(buf); + mFileNames.push_back(mTemporary); + close(fp); + return true; +} +// ----------------------------------------------------------------- +bool GeneratorFileOrCmd::removeTemp() const +{ + if (mTemporary.empty()) { + LOG(info) << "Temporary file name empty, nothing to remove"; + return false; + } + + // Get the file we're reading from + std::filesystem::path p(mTemporary); + + // Check if the file exists + if (not std::filesystem::exists(p)) { + LOG(info) << "Temporary file " << p << " does not exist"; + return true; + } + + // Remove temporary file + std::error_code ec; + std::filesystem::remove(p, ec); + if (ec) { + LOG(warn) << "When removing " << p << ": " << ec.message(); + } + + // Ignore errors when removing the temporary file + return true; +} +// ----------------------------------------------------------------- +bool GeneratorFileOrCmd::makeFifo() const +{ + // First remove the temporary file if it exists, + // otherwise we may not be able to make the FIFO + removeTemp(); + + std::string fileName = mFileNames.front(); + + int ret = mkfifo(fileName.c_str(), 0600); + if (ret != 0) { + LOG(fatal) << "Failed to make fifo \"" << fileName << "\": " + << std::strerror(errno); + return false; + } + + return true; +} +// ----------------------------------------------------------------- +bool GeneratorFileOrCmd::ensureFiles() +{ + try { + for (auto& f : mFileNames) { + auto c = std::filesystem::canonical(std::filesystem::path(f)); + f = c.c_str(); + } + } catch (std::exception& e) { + LOG(error) << e.what(); + return false; + } + return true; +} +// ----------------------------------------------------------------- +void GeneratorFileOrCmd::waitForData(const std::string& filename) const +{ + using namespace std::chrono_literals; + + // Get the file we're reading from + std::filesystem::path p(filename); + + LOG(debug) << "Waiting for data on " << p; + + // Wait until child process creates the file + while (not std::filesystem::exists(p)) { + std::this_thread::sleep_for(mWait * 1ms); + } + + // Wait until we have more data in the file than just the file + // header + while (std::filesystem::file_size(p) <= 256) { + std::this_thread::sleep_for(mWait * 1ms); + } + + // Give the child process 1 second to post the data to the file + LOG(debug) << "Got data in " << p << ", sleeping for a while"; + std::this_thread::sleep_for(mWait * 2ms); +} + +} /* namespace eventgen */ +} /* namespace o2 */ diff --git a/Generators/src/GeneratorFileOrCmdParam.cxx b/Generators/src/GeneratorFileOrCmdParam.cxx new file mode 100644 index 0000000000000..e452f272c3735 --- /dev/null +++ b/Generators/src/GeneratorFileOrCmdParam.cxx @@ -0,0 +1,18 @@ +// Copyright 2023-2099 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. + +/// @author Christian Holm Christensen + +#include "Generators/GeneratorFileOrCmdParam.h" +O2ParamImpl(o2::eventgen::GeneratorFileOrCmdParam); +// +// EOF +// diff --git a/Generators/src/GeneratorHepMC.cxx b/Generators/src/GeneratorHepMC.cxx index 6c062268bfacc..3fec13ae490d2 100644 --- a/Generators/src/GeneratorHepMC.cxx +++ b/Generators/src/GeneratorHepMC.cxx @@ -14,14 +14,15 @@ #include "SimulationDataFormat/MCUtils.h" #include "Generators/GeneratorHepMC.h" #include "Generators/GeneratorHepMCParam.h" -#include "HepMC3/ReaderAscii.h" -#include "HepMC3/ReaderAsciiHepMC2.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimConfig/SimConfig.h" +#include "HepMC3/ReaderFactory.h" #include "HepMC3/GenEvent.h" #include "HepMC3/GenParticle.h" #include "HepMC3/GenVertex.h" #include "HepMC3/FourVector.h" +#include "HepMC3/Version.h" #include "TParticle.h" -#include "TSystem.h" #include #include "FairPrimaryGenerator.h" @@ -36,19 +37,14 @@ namespace eventgen /*****************************************************************/ GeneratorHepMC::GeneratorHepMC() - : Generator("ALICEo2", "ALICEo2 HepMC Generator"), mStream(), mFileName(), mVersion(3), mReader(nullptr), mEvent(nullptr) + : GeneratorHepMC("ALICEo2", "ALICEo2 HepMC Generator") { - /** default constructor **/ - - mEvent = new HepMC3::GenEvent(); - mInterface = reinterpret_cast(mEvent); - mInterfaceName = "hepmc"; } /*****************************************************************/ GeneratorHepMC::GeneratorHepMC(const Char_t* name, const Char_t* title) - : Generator(name, title), mStream(), mFileName(), mVersion(3), mReader(nullptr), mEvent(nullptr) + : Generator(name, title) { /** constructor **/ @@ -62,36 +58,66 @@ GeneratorHepMC::GeneratorHepMC(const Char_t* name, const Char_t* title) GeneratorHepMC::~GeneratorHepMC() { /** default destructor **/ - - if (mStream.is_open()) { - mStream.close(); - } + LOG(info) << "Destructing GeneratorHepMC"; if (mReader) { mReader->close(); - delete mReader; } if (mEvent) { delete mEvent; } + removeTemp(); } /*****************************************************************/ +void GeneratorHepMC::setup(const GeneratorFileOrCmdParam& param0, + const GeneratorHepMCParam& param, + const conf::SimConfig& config) +{ + if (not param.fileName.empty()) { + LOG(warn) << "The use of the key \"HepMC.fileName\" is " + << "deprecated, use \"GeneratorFileOrCmd.fileNames\" instead"; + } + + GeneratorFileOrCmd::setup(param0, config); + if (not param.fileName.empty()) { + setFileNames(param.fileName); + } + mVersion = param.version; + setEventsToSkip(param.eventsToSkip); + + if (param.version != 0 and mCmd.empty()) { + LOG(warn) << "The key \"HepMC.version\" is no longer used when " + << "reading from files. The format version of the input files " + << "are automatically deduced."; + } +} + +/*****************************************************************/ Bool_t GeneratorHepMC::generateEvent() { + LOG(debug) << "Generating an event"; /** generate event **/ + int tries = 0; + do { + LOG(debug) << " try # " << ++tries; + if (not mReader and not makeReader()) { + return false; + } - /** clear and read event **/ - mEvent->clear(); - mReader->read_event(*mEvent); - if (mReader->failed()) { - return kFALSE; - } - /** set units to desired output **/ - mEvent->set_units(HepMC3::Units::GEV, HepMC3::Units::MM); + /** clear and read event **/ + mEvent->clear(); + mReader->read_event(*mEvent); + if (not mReader->failed()) { + /** set units to desired output **/ + mEvent->set_units(HepMC3::Units::GEV, HepMC3::Units::MM); + LOG(debug) << "Read one event " << mEvent->event_number(); + return true; + } + } while (true); - /** success **/ - return kTRUE; + /** failure **/ + return false; } /*****************************************************************/ @@ -143,6 +169,213 @@ Bool_t GeneratorHepMC::importParticles() return kTRUE; } +namespace +{ +template +bool putAttributeInfoImpl(o2::dataformats::MCEventHeader* eventHeader, + const std::string& name, + const std::shared_ptr& a) +{ + if (auto* p = dynamic_cast(a.get())) { + eventHeader->putInfo(name, p->value()); + return true; + } + return false; +} + +void putAttributeInfo(o2::dataformats::MCEventHeader* eventHeader, + const std::string& name, + const std::shared_ptr& a) +{ + using IntAttribute = HepMC3::IntAttribute; + using LongAttribute = HepMC3::LongAttribute; + using FloatAttribute = HepMC3::FloatAttribute; + using DoubleAttribute = HepMC3::DoubleAttribute; + using StringAttribute = HepMC3::StringAttribute; + using CharAttribute = HepMC3::CharAttribute; + using LongLongAttribute = HepMC3::LongLongAttribute; + using LongDoubleAttribute = HepMC3::LongDoubleAttribute; + using UIntAttribute = HepMC3::UIntAttribute; + using ULongAttribute = HepMC3::ULongAttribute; + using ULongLongAttribute = HepMC3::ULongLongAttribute; + using BoolAttribute = HepMC3::BoolAttribute; + + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } + if (putAttributeInfoImpl(eventHeader, name, a)) { + return; + } +} +} // namespace + +/*****************************************************************/ + +void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) +{ + /** update header **/ + using Key = o2::dataformats::MCInfoKeys; + + eventHeader->putInfo(Key::generator, "hepmc"); + eventHeader->putInfo(Key::generatorVersion, HEPMC3_VERSION_CODE); + + auto xSection = mEvent->cross_section(); + auto pdfInfo = mEvent->pdf_info(); + auto hiInfo = mEvent->heavy_ion(); + + // Set default cross-section + if (xSection) { + eventHeader->putInfo(Key::xSection, xSection->xsec()); + eventHeader->putInfo(Key::xSectionError, xSection->xsec_err()); + eventHeader->putInfo(Key::acceptedEvents, + xSection->get_accepted_events()); + eventHeader->putInfo(Key::attemptedEvents, + xSection->get_attempted_events()); + } + + // Set weights and cross sections + size_t iw = 0; + for (auto w : mEvent->weights()) { + std::string post = (iw > 0 ? "_" + std::to_string(iw) : ""); + eventHeader->putInfo(Key::weight + post, w); + if (xSection) { + eventHeader->putInfo(Key::xSection, xSection->xsec(iw)); + eventHeader->putInfo(Key::xSectionError, xSection->xsec_err(iw)); + } + iw++; + } + + // Set the PDF information + if (pdfInfo) { + eventHeader->putInfo(Key::pdfParton1Id, pdfInfo->parton_id[0]); + eventHeader->putInfo(Key::pdfParton2Id, pdfInfo->parton_id[1]); + eventHeader->putInfo(Key::pdfX1, pdfInfo->x[0]); + eventHeader->putInfo(Key::pdfX2, pdfInfo->x[1]); + eventHeader->putInfo(Key::pdfScale, pdfInfo->scale); + eventHeader->putInfo(Key::pdfXF1, pdfInfo->xf[0]); + eventHeader->putInfo(Key::pdfXF2, pdfInfo->xf[1]); + eventHeader->putInfo(Key::pdfCode1, pdfInfo->pdf_id[0]); + eventHeader->putInfo(Key::pdfCode2, pdfInfo->pdf_id[1]); + } + + // Set heavy-ion information + if (hiInfo) { + eventHeader->putInfo(Key::impactParameter, + hiInfo->impact_parameter); + eventHeader->putInfo(Key::nPart, + hiInfo->Npart_proj + hiInfo->Npart_targ); + eventHeader->putInfo(Key::nPartProjectile, hiInfo->Npart_proj); + eventHeader->putInfo(Key::nPartTarget, hiInfo->Npart_targ); + eventHeader->putInfo(Key::nColl, hiInfo->Ncoll); + eventHeader->putInfo(Key::nCollHard, hiInfo->Ncoll_hard); + eventHeader->putInfo(Key::nCollNNWounded, + hiInfo->N_Nwounded_collisions); + eventHeader->putInfo(Key::nCollNWoundedN, + hiInfo->Nwounded_N_collisions); + eventHeader->putInfo(Key::nCollNWoundedNwounded, + hiInfo->Nwounded_Nwounded_collisions); + eventHeader->putInfo(Key::planeAngle, + hiInfo->event_plane_angle); + eventHeader->putInfo(Key::sigmaInelNN, + hiInfo->sigma_inel_NN); + eventHeader->putInfo(Key::centrality, hiInfo->centrality); + eventHeader->putInfo(Key::nSpecProjectileProton, hiInfo->Nspec_proj_p); + eventHeader->putInfo(Key::nSpecProjectileNeutron, hiInfo->Nspec_proj_n); + eventHeader->putInfo(Key::nSpecTargetProton, hiInfo->Nspec_targ_p); + eventHeader->putInfo(Key::nSpecTargetNeutron, hiInfo->Nspec_targ_n); + } + + for (auto na : mEvent->attributes()) { + std::string name = na.first; + if (name == "GenPdfInfo" || + name == "GenCrossSection" || + name == "GenHeavyIon") { + continue; + } + + for (auto ia : na.second) { + int no = ia.first; + auto at = ia.second; + std::string post = (no == 0 ? "" : std::to_string(no)); + + putAttributeInfo(eventHeader, name + post, at); + } + } +} + +/*****************************************************************/ + +bool GeneratorHepMC::makeReader() +{ + // Reset the reader smart pointer + LOG(debug) << "Reseting the reader"; + mReader.reset(); + + // Check that we have any file names left + if (mFileNames.size() < 1) { + LOG(debug) << "No more files to read, return false"; + return false; + } + + // If we have file names left, pop the top of the list (LIFO) + auto filename = mFileNames.front(); + mFileNames.pop_front(); + + LOG(debug) << "Next file to read: \"" << filename << "\" " + << mFileNames.size() << " left"; + + if (not mCmd.empty()) { + // For FIFO reading, we assume straight ASCII output always. + // Unfortunately, the HepMC3::deduce_reader `stat`s the filename + // which isn't supported on a FIFO, so we have to use the reader + // directly. Here, we allow for version 2 formats if the user + // specifies that + LOG(info) << "Creating ASCII reader of " << filename; + if (mVersion == 2) { + mReader.reset(new HepMC3::ReaderAsciiHepMC2(filename)); + } else { + mReader.reset(new HepMC3::ReaderAscii(filename)); + } + } else { + LOG(info) << "Deduce a reader of " << filename; + mReader = HepMC3::deduce_reader(filename); + } + + bool ret = bool(mReader) and not mReader->failed(); + LOG(info) << "Reader is " << mReader.get() << " " << ret; + return ret; +} + /*****************************************************************/ Bool_t GeneratorHepMC::Init() @@ -152,41 +385,81 @@ Bool_t GeneratorHepMC::Init() /** init base class **/ Generator::Init(); - /** open file **/ - std::string filename = gSystem->ExpandPathName(mFileName.c_str()); - mStream.open(filename); - if (!mStream.is_open()) { - LOG(fatal) << "Cannot open input file: " << filename << std::endl; - return kFALSE; - } - - /** create reader according to HepMC version **/ - switch (mVersion) { - case 2: - mStream.close(); - mReader = new HepMC3::ReaderAsciiHepMC2(filename); - break; - case 3: - mReader = new HepMC3::ReaderAscii(mStream); - break; - default: - LOG(fatal) << "Unsupported HepMC version: " << mVersion << std::endl; - return kFALSE; - } - - // skip events at the beginning - if (!mReader->failed()) { - LOGF(info, "%i events to skip.", mEventsToSkip); - for (auto ind = 0; ind < mEventsToSkip; ind++) { - if (!generateEvent()) { - LOGF(error, "The file %s only contains %i events!", mFileName, ind); - break; - } + // If a EG command line is given, then we make a fifo on a temporary + // file, and directs the EG to write to that fifo. We will then set + // up the HepMC3 reader to read from that fifo. + // + // o2-sim -g hepmc --configKeyValues "HepMC.progCmd=" ... + // + // where is the command line to run an event generator. The + // event generator should output HepMC event records to standard + // output. Nothing else, but the HepMC event record may be output + // to standard output. If the EG has other output to standard + // output, then a filter can be set-up. For example + // + // crmc -n 3 -o hepmc3 -c /optsw/inst/etc/crmc.param -f /dev/stdout \ + // | sed -n 's/^\(HepMC::\|[EAUWVP] \)/\1/p' + // + // What's more, the event generator program _must_ accept the + // following command line argument + // + // `-n NEVENTS` to set the number of events to produce. + // + // Optionally, the command line should also accept + // + // `-s SEED` to set the random number seed + // `-b FM` to set the maximum impact parameter to sample + // `-o OUTPUT` to set the output file name + // + // All of this can conviniently be achieved via a wrapper script + // around the actual EG program. + if (not mCmd.empty()) { + // Set filename to be a temporary name + if (not makeTemp()) { + return false; + } + + // Make a fifo + if (not makeFifo()) { + return false; + } + + // Build command line, rediret stdout to our fifo and put + std::string cmd = makeCmdLine(); + LOG(debug) << "EG command line is \"" << cmd << "\""; + + // Execute the command line + if (not executeCmdLine(cmd)) { + LOG(fatal) << "Failed to spawn \"" << cmd << "\""; + return false; + } + } else { + // If no command line was given, ensure that all files are present + // on the system. Note, in principle, HepMC3 can read from remote + // files + // + // root:// XRootD served + // http[s]:// Web served + // gsidcap:// DCap served + // + // These will all be handled in HepMC3 via ROOT's TFile protocol + // and the files are assumed to contain a TTree named + // `hepmc3_tree` and that tree has the branches + // + // `hepmc3_event` with object of type `HepMC3::GenEventData` + // `GenRunInfo` with object of type `HepMC3::GenRunInfoData` + // + // where the last branch is optional. + // + // However, here we will assume system local files. If _any_ of + // the listed files do not exist, then we fail. + if (not ensureFiles()) { + return false; } } - /** success **/ - return !mReader->failed(); + // Create reader for current (first) file + return true; } /*****************************************************************/ diff --git a/Generators/src/GeneratorPythia8.cxx b/Generators/src/GeneratorPythia8.cxx index 13c17003ee612..fb5e1da3499ac 100644 --- a/Generators/src/GeneratorPythia8.cxx +++ b/Generators/src/GeneratorPythia8.cxx @@ -22,6 +22,7 @@ #include "SimulationDataFormat/MCGenProperties.h" #include "SimulationDataFormat/ParticleStatus.h" #include "Pythia8/HIUserHooks.h" +#include "Pythia8Plugins/PowhegHooks.h" #include "TSystem.h" #include "ZDCBase/FragmentParam.h" @@ -107,7 +108,28 @@ Bool_t GeneratorPythia8::Init() /** inhibit hadron decays **/ mPythia.readString("HadronLevel:Decay off"); #endif - + if (mPythia.settings.mode("Beams:frameType") == 4) { + // Hook for POWHEG + // Read in key POWHEG merging settings + int vetoMode = mPythia.settings.mode("POWHEG:veto"); + int MPIvetoMode = mPythia.settings.mode("POWHEG:MPIveto"); + bool loadHooks = (vetoMode > 0 || MPIvetoMode > 0); + // Add in user hooks for shower vetoing + std::shared_ptr powhegHooks; + if (loadHooks) { + // Set ISR and FSR to start at the kinematical limit + if (vetoMode > 0) { + mPythia.readString("SpaceShower:pTmaxMatch = 2"); + mPythia.readString("TimeShower:pTmaxMatch = 2"); + } + // Set MPI to start at the kinematical limit + if (MPIvetoMode > 0) { + mPythia.readString("MultipartonInteractions:pTmaxMatch = 2"); + } + powhegHooks = std::make_shared(); + mPythia.setUserHooksPtr((Pythia8::UserHooksPtr)powhegHooks); + } + } /** initialise **/ if (!mPythia.init()) { LOG(fatal) << "Failed to init \'Pythia8\': init returned with error"; @@ -123,8 +145,6 @@ Bool_t GeneratorPythia8::Init() Bool_t GeneratorPythia8::generateEvent() { - /** generate event **/ - /** generate event **/ if (!mPythia.next()) { return false; @@ -202,12 +222,39 @@ Bool_t void GeneratorPythia8::updateHeader(o2::dataformats::MCEventHeader* eventHeader) { /** update header **/ - - eventHeader->putInfo("generator", "pythia8"); - eventHeader->putInfo("version", PYTHIA_VERSION_INTEGER); - eventHeader->putInfo("processName", mPythia.info.name()); - eventHeader->putInfo("processCode", mPythia.info.code()); - eventHeader->putInfo("weight", mPythia.info.weight()); + using Key = o2::dataformats::MCInfoKeys; + + eventHeader->putInfo(Key::generator, "pythia8"); + eventHeader->putInfo(Key::generatorVersion, PYTHIA_VERSION_INTEGER); + eventHeader->putInfo(Key::processName, mPythia.info.name()); + eventHeader->putInfo(Key::processCode, mPythia.info.code()); + eventHeader->putInfo(Key::weight, mPythia.info.weight()); + + auto& info = mPythia.info; + + // Set PDF information + eventHeader->putInfo(Key::pdfParton1Id, info.id1pdf()); + eventHeader->putInfo(Key::pdfParton2Id, info.id2pdf()); + eventHeader->putInfo(Key::pdfX1, info.x1pdf()); + eventHeader->putInfo(Key::pdfX2, info.x2pdf()); + eventHeader->putInfo(Key::pdfScale, info.QFac()); + eventHeader->putInfo(Key::pdfXF1, info.pdf1()); + eventHeader->putInfo(Key::pdfXF2, info.pdf2()); + + // Set cross section + eventHeader->putInfo(Key::xSection, info.sigmaGen() * 1e9); + eventHeader->putInfo(Key::xSectionError, info.sigmaErr() * 1e9); + + // Set weights (overrides cross-section for each weight) + size_t iw = 0; + auto xsecErr = info.weightContainerPtr->getTotalXsecErr(); + for (auto w : info.weightContainerPtr->getTotalXsec()) { + std::string post = (iw == 0 ? "" : "_" + std::to_string(iw)); + eventHeader->putInfo(Key::weight + post, info.weightValueByIndex(iw)); + eventHeader->putInfo(Key::xSection + post, w * 1e9); + eventHeader->putInfo(Key::xSectionError + post, xsecErr[iw] * 1e9); + iw++; + } #if PYTHIA_VERSION_INTEGER < 8300 auto hiinfo = mPythia.info.hiinfo; @@ -218,7 +265,7 @@ void GeneratorPythia8::updateHeader(o2::dataformats::MCEventHeader* eventHeader) if (hiinfo) { /** set impact parameter **/ eventHeader->SetB(hiinfo->b()); - eventHeader->putInfo("Bimpact", hiinfo->b()); + eventHeader->putInfo(Key::impactParameter, hiinfo->b()); auto bImp = hiinfo->b(); /** set Ncoll, Npart and Nremn **/ int nColl, nPart; @@ -230,7 +277,8 @@ void GeneratorPythia8::updateHeader(o2::dataformats::MCEventHeader* eventHeader) getNpart(nPartProtonProj, nPartNeutronProj, nPartProtonTarg, nPartNeutronTarg); getNremn(nRemnProtonProj, nRemnNeutronProj, nRemnProtonTarg, nRemnNeutronTarg); getNfreeSpec(nFreeNeutronProj, nFreeProtonProj, nFreeNeutronTarg, nFreeProtonTarg); - eventHeader->putInfo("Ncoll", nColl); + eventHeader->putInfo(Key::nColl, nColl); + // These are all non-HepMC3 fields - of limited use eventHeader->putInfo("Npart", nPart); eventHeader->putInfo("Npart_proj_p", nPartProtonProj); eventHeader->putInfo("Npart_proj_n", nPartNeutronProj); @@ -244,6 +292,18 @@ void GeneratorPythia8::updateHeader(o2::dataformats::MCEventHeader* eventHeader) eventHeader->putInfo("Nfree_proj_p", nFreeProtonProj); eventHeader->putInfo("Nfree_targ_n", nFreeNeutronTarg); eventHeader->putInfo("Nfree_targ_p", nFreeProtonTarg); + + // --- HepMC3 conforming information --- + // This is how the Pythia authors define Ncoll + // eventHeader->putInfo(Key::nColl, + // hiinfo->nAbsProj() + hiinfo->nDiffProj() + + // hiinfo->nAbsTarg() + hiinfo->nDiffTarg() - + // hiiinfo->nCollND() - hiinfo->nCollDD()); + eventHeader->putInfo(Key::nPartProjectile, + hiinfo->nAbsProj() + hiinfo->nDiffProj()); + eventHeader->putInfo(Key::nPartTarget, + hiinfo->nAbsTarg() + hiinfo->nDiffTarg()); + eventHeader->putInfo(Key::nCollHard, hiinfo->nCollNDTot()); } } @@ -302,6 +362,10 @@ void GeneratorPythia8::getNcoll(const Pythia8::Info& info, int& nColl) auto hiinfo = info.hiInfo; #endif + // This is how the Pythia authors define Ncoll + nColl = (hiinfo->nAbsProj() + hiinfo->nDiffProj() + + hiinfo->nAbsTarg() + hiinfo->nDiffTarg() - + hiinfo->nCollND() - hiinfo->nCollDD()); nColl = 0; if (!hiinfo) { @@ -330,6 +394,17 @@ void GeneratorPythia8::getNpart(const Pythia8::Info& info, int& nPart) /** compute number of participants as the sum of all participants nucleons **/ + // This is how the Pythia authors calculate Npart +#if PYTHIA_VERSION_INTEGER < 8300 + auto hiinfo = info.hiinfo; +#else + auto hiinfo = info.hiInfo; +#endif + if (hiinfo) { + nPart = (hiinfo->nAbsProj() + hiinfo->nDiffProj() + + hiinfo->nAbsTarg() + hiinfo->nDiffTarg()); + } + int nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg; getNpart(info, nProtonProj, nNeutronProj, nProtonTarg, nNeutronTarg); nPart = nProtonProj + nNeutronProj + nProtonTarg + nNeutronTarg; diff --git a/Generators/src/GeneratorTParticle.cxx b/Generators/src/GeneratorTParticle.cxx new file mode 100644 index 0000000000000..ab68f7f39b1bf --- /dev/null +++ b/Generators/src/GeneratorTParticle.cxx @@ -0,0 +1,154 @@ +// Copyright 2023-2099 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. + +/// @author Christian Holm Christensen +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace eventgen +{ +/*****************************************************************/ +GeneratorTParticle::GeneratorTParticle() +{ + setOutputSwitch("-o"); +} + +/*****************************************************************/ +GeneratorTParticle::~GeneratorTParticle() +{ + if (mChain) { + TFile* file = mChain->GetCurrentFile(); + if (file) { + mChain->RecursiveRemove(file); + } + delete mChain; + } + if (mCmd.empty()) { + return; + } + + removeTemp(); +} +/*****************************************************************/ +Bool_t GeneratorTParticle::Init() +{ + mChain = new TChain(mTreeName.c_str()); + mTParticles = new TClonesArray("TParticle"); + mChain->SetBranchAddress(mBranchName.c_str(), &mTParticles); + + if (not mCmd.empty()) { + // Set filename to be a temporary name + if (not makeTemp()) { + return false; + } + + // Build command line, Assumes command line parameter + std::string cmd = makeCmdLine(); + LOG(info) << "EG command line is \"" << cmd << "\""; + + // Execute the background command + if (not executeCmdLine(cmd)) { + LOG(fatal) << "Failed to spawn \"" << cmd << "\""; + return false; + } + } + for (auto filename : mFileNames) { + mChain->AddFile(filename.c_str()); + } + + // Clear the array of file names + mFileNames.clear(); + + return true; +} + +/*****************************************************************/ +void GeneratorTParticle::setup(const GeneratorFileOrCmdParam& param0, + const GeneratorTParticleParam& param, + const conf::SimConfig& config) +{ + GeneratorFileOrCmd::setup(param0, config); + setTreeName(param.treeName); + setBranchName(param.branchName); +} + +/*****************************************************************/ +Bool_t GeneratorTParticle::generateEvent() +{ + // If this is the first entry, and we're executing a command, then + // wait until the input file exists and actually contain some data. + if (mEntry == 0 and not mCmd.empty()) { + waitForData(mTemporary); + } + + // Read in the next entry in the chain + int read = mChain->GetEntry(mEntry); + mEntry++; + + // If we got an error while reading, then give error message + if (read < 0) { + LOG(error) << "Failed to read entry " << mEntry << " of chain"; + } + + // If we had an error or nothing was read back, then return false + if (read <= 0) { + return false; + } + + return true; +} + +Bool_t GeneratorTParticle::importParticles() +{ + for (auto* object : *mTParticles) { + TParticle* particle = static_cast(object); + auto statusCode = particle->GetStatusCode(); + if (!mcgenstatus::isEncoded(statusCode)) { + statusCode = mcgenstatus::MCGenStatusEncoding(statusCode, 0) + .fullEncoding; + } + + mParticles.emplace_back(particle->GetPdgCode(), + statusCode, + particle->GetFirstMother(), + particle->GetSecondMother(), + particle->GetFirstDaughter(), + particle->GetLastDaughter(), + particle->Px(), + particle->Py(), + particle->Pz(), + particle->Energy(), + particle->Vx(), + particle->Vy(), + particle->Vz(), + particle->T()); + auto& tgt = mParticles[mParticles.size() - 1]; + tgt.SetPolarTheta(particle->GetPolarTheta()); + tgt.SetPolarPhi(particle->GetPolarPhi()); + tgt.SetCalcMass(particle->GetCalcMass()); + tgt.SetWeight(particle->GetWeight()); + } + return true; +} +} // namespace eventgen +} // namespace o2 +// +// EOF +// diff --git a/Generators/src/GeneratorTParticleParam.cxx b/Generators/src/GeneratorTParticleParam.cxx new file mode 100644 index 0000000000000..abb2d39681a3e --- /dev/null +++ b/Generators/src/GeneratorTParticleParam.cxx @@ -0,0 +1,18 @@ +// Copyright 2023-2099 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. + +/// @author Christian Holm Christensen + +#include "Generators/GeneratorTParticleParam.h" +O2ParamImpl(o2::eventgen::GeneratorTParticleParam); +// +// EOF +// diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 0d42b2ce505b6..35e77febee172 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -67,5 +67,10 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::QEDGenParam> + ; #pragma link C++ class o2::eventgen::GenCosmicsParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GenCosmicsParam> + ; +#pragma link C++ class o2::eventgen::GeneratorTParticle + ; +#pragma link C++ class o2::eventgen::GeneratorTParticleParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorTParticleParam> + ; +#pragma link C++ class o2::eventgen::GeneratorFileOrCmdParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorFileOrCmdParam> + ; #endif diff --git a/Steer/DigitizerWorkflow/CMakeLists.txt b/Steer/DigitizerWorkflow/CMakeLists.txt index 43f84726dbe7c..fce6103573609 100644 --- a/Steer/DigitizerWorkflow/CMakeLists.txt +++ b/Steer/DigitizerWorkflow/CMakeLists.txt @@ -47,8 +47,8 @@ o2_add_executable(digitizer-workflow O2::ITSMFTWorkflow O2::MCHSimulation O2::MCHMappingImpl4 - O2::DataFormatsMCH O2::MCHIO + O2::MCHDigitFiltering O2::MFTSimulation O2::MIDSimulation O2::PHOSSimulation @@ -106,6 +106,7 @@ o2_add_executable(digitizer-workflow O2::MCHSimulation O2::MCHMappingImpl4 O2::MCHIO + O2::MCHDigitFiltering O2::MFTSimulation O2::MIDSimulation O2::PHOSSimulation diff --git a/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx index bd9e607950189..fc92020f25b21 100644 --- a/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/MCHDigitizerSpec.cxx @@ -21,6 +21,7 @@ #include "Framework/DataRefUtils.h" #include "Framework/Lifetime.h" #include "Framework/Task.h" +#include "MCHDigitFiltering/DigitFilterParam.h" #include "MCHSimulation/Detector.h" #include "MCHSimulation/Digitizer.h" #include "MCHSimulation/DigitizerParam.h" @@ -79,12 +80,18 @@ class MCHDPLDigitizerTask : public o2::base::BaseDPLDigitizer auto context = pc.inputs().get("collisioncontext"); context->initSimChains(o2::detectors::DetID::MCH, mSimChains); const auto& eventRecords = context->getEventRecords(); + auto timeOffset = DigitFilterParam::Instance().timeOffset; // generate signals produced by every hits if (!DigitizerParam::Instance().onlyNoise) { const auto& eventParts = context->getEventParts(); for (auto i = 0; i < eventRecords.size(); i++) { auto ir = eventRecords[i]; + // apply time offset, discarding events going to negative IR + if (ir.toLong() < timeOffset) { + continue; + } + ir -= timeOffset; for (const auto& part : eventParts[i]) { std::vector hits{}; context->retrieveHits(mSimChains, "MCHHit", part.sourceID, part.entryID, &hits); @@ -94,8 +101,8 @@ class MCHDPLDigitizerTask : public o2::base::BaseDPLDigitizer } // generate noise-only signals between first and last collisions ± 100 BC (= 25 ADC samples) - auto firstIR = InteractionRecord::long2IR(std::max(int64_t(0), eventRecords.front().toLong() - 100)); - auto lastIR = eventRecords.back() + 100; + auto firstIR = InteractionRecord::long2IR(std::max(int64_t(0), eventRecords.front().toLong() - timeOffset - 100)); + auto lastIR = InteractionRecord::long2IR(std::max(int64_t(0), eventRecords.back().toLong() - timeOffset + 100)); mDigitizer->addNoise(firstIR, lastIR); // digitize diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index a059a40ddb1c3..79555249dd64c 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -195,7 +195,7 @@ void customize(std::vector& workflowOptions) workflowOptions.push_back(ConfigParamSpec{"use-ccdb-ft0", o2::framework::VariantType::Bool, false, {"enable access to ccdb ft0 calibration objects"}}); // option to use/not use CCDB for EMCAL - workflowOptions.push_back(ConfigParamSpec{"use-ccdb-emc", o2::framework::VariantType::Bool, false, {"enable access to ccdb EMCAL simulation objects"}}); + workflowOptions.push_back(ConfigParamSpec{"no-use-ccdb-emc", o2::framework::VariantType::Bool, false, {"Disable access to ccdb EMCAL simulation objects"}}); // option to use or not use the Trap Simulator after digitisation (debate of digitization or reconstruction is for others) workflowOptions.push_back(ConfigParamSpec{"disable-trd-trapsim", VariantType::Bool, false, {"disable the trap simulation of the TRD"}}); @@ -650,7 +650,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // the EMCal part if (isEnabled(o2::detectors::DetID::EMC)) { - auto useCCDB = configcontext.options().get("use-ccdb-emc"); + auto useCCDB = !configcontext.options().get("no-use-ccdb-emc"); detList.emplace_back(o2::detectors::DetID::EMC); // connect the EMCal digitization digitizerSpecs.emplace_back(o2::emcal::getEMCALDigitizerSpec(fanoutsize++, mctruth, useCCDB)); diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index b59ca220af118..3f78959dd627c 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -45,6 +45,7 @@ struct Options { bool printContext = false; std::string bcpatternfile; int tfid = 0; // tfid -> used to calculate start orbit for collisions + int firstOrbit = 0; // first orbit in run (orbit offset) int orbitsPerTF = 256; // number of orbits per timeframe --> used to calculate start orbit for collisions bool useexistingkinematics = false; bool noEmptyTF = false; // prevent empty timeframes; the first interaction will be shifted backwards to fall within the range given by Options.orbits @@ -195,6 +196,7 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) "orbitsPerTF", bpo::value(&optvalues.orbitsPerTF)->default_value(256), "Orbits per timeframes")( "use-existing-kine", "Read existing kinematics to adjust event counts")( "timeframeID", bpo::value(&optvalues.tfid)->default_value(0), "Timeframe id of the first timeframe int this context. Allows to generate contexts for different start orbits")( + "first-orbit", bpo::value(&optvalues.firstOrbit)->default_value(0), "First orbit in the run (HBFUtils.firstOrbit)")( "maxCollsPerTF", bpo::value(&optvalues.maxCollsPerTF)->default_value(-1), "Maximal number of MC collisions to put into one timeframe. By default no constraint.")( "noEmptyTF", bpo::bool_switch(&optvalues.noEmptyTF), "Enforce to have at least one collision")("configKeyValues", bpo::value(&optvalues.configKeyValues)->default_value(""), "Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")("with-vertices", "Assign vertices to collisions.")("timestamp", bpo::value(&optvalues.timestamp)->default_value(-1L), "Timestamp for CCDB queries / anchoring"); @@ -279,7 +281,7 @@ int main(int argc, char* argv[]) if (!options.bcpatternfile.empty()) { setBCFillingHelper(sampler, options.bcpatternfile); } - auto orbitstart = options.tfid * options.orbitsPerTF; + auto orbitstart = options.firstOrbit + options.tfid * options.orbitsPerTF; o2::InteractionTimeRecord record; // this loop makes sure that the first collision is within the range of orbits asked (if noEmptyTF is enabled) do { diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 118374751ee69..64fe2281ed1b5 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -163,7 +163,7 @@ void O2MCApplicationBase::InitGeometry() bool O2MCApplicationBase::MisalignGeometry() { - for (auto det : listActiveDetectors) { + for (auto det : listDetectors) { if (dynamic_cast(det)) { ((o2::base::Detector*)det)->addAlignableVolumes(); } @@ -197,6 +197,7 @@ void O2MCApplicationBase::finishEventCommon() auto header = static_cast(fMCEventHeader); header->getMCEventStats().setNSteps(mStepCounter); + header->setDetId2HitBitLUT(o2::base::Detector::getDetId2HitBitIndex()); static_cast(GetStack())->updateEventStats(); } @@ -265,9 +266,9 @@ void addSpecialParticles() TVirtualMC::GetMC()->DefineParticle(-1010020041, "AntiHyperhelium4*", kPTHadron, 3.9231, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); // Lithium 4 ground state - TVirtualMC::GetMC()->DefineParticle(1000030040, "Lithium4", kPTHadron, 3.74976, 3.0, 9.1e-23, "Ion", 0.005, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + TVirtualMC::GetMC()->DefineParticle(1000030040, "Lithium4", kPTHadron, 3.7513, 3.0, 9.1e-23, "Ion", 0.003, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); // Anti-Lithium 4 ground state - TVirtualMC::GetMC()->DefineParticle(-1000030040, "AntiLithium4", kPTHadron, 3.74976, 3.0, 9.1e-23, "Ion", 0.005, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + TVirtualMC::GetMC()->DefineParticle(-1000030040, "AntiLithium4", kPTHadron, 3.7513, 3.0, 9.1e-23, "Ion", 0.003, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); //Hyper helium 5 TVirtualMC::GetMC()->DefineParticle(1010020050, "Hyperhelium5", kPTHadron, 4.841, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 5, kFALSE); diff --git a/Utilities/DataSampling/src/DataSamplingReadoutAdapter.cxx b/Utilities/DataSampling/src/DataSamplingReadoutAdapter.cxx index 69add6139aed6..66a872e265baf 100644 --- a/Utilities/DataSampling/src/DataSamplingReadoutAdapter.cxx +++ b/Utilities/DataSampling/src/DataSamplingReadoutAdapter.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "DataSampling/DataSamplingReadoutAdapter.h" #include "Framework/DataProcessingHeader.h" +#include "Framework/RawDeviceService.h" #include "Headers/DataHeader.h" #include "Framework/DataSpecUtils.h" #include @@ -23,7 +24,9 @@ using DataHeader = o2::header::DataHeader; InjectorFunction dataSamplingReadoutAdapter(OutputSpec const& spec) { - return [spec](TimingInfo&, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + return [spec](TimingInfo&, ServiceRegistryRef const& ref, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) { + auto *device = ref.get().device(); + for (size_t i = 0; i < parts.Size(); ++i) { DataHeader dh; @@ -36,8 +39,9 @@ InjectorFunction dataSamplingReadoutAdapter(OutputSpec const& spec) DataProcessingHeader dph{newTimesliceId, 0}; o2::header::Stack headerStack{dh, dph}; - sendOnChannel(device, std::move(headerStack), std::move(parts.At(i)), spec, channelRetriever); + sendOnChannel(*device, std::move(headerStack), std::move(parts.At(i)), spec, channelRetriever); } + return parts.Size() != 0; }; } diff --git a/Utilities/rANS/CMakeLists.txt b/Utilities/rANS/CMakeLists.txt index 1e929d204c3a7..710fe7879571c 100644 --- a/Utilities/rANS/CMakeLists.txt +++ b/Utilities/rANS/CMakeLists.txt @@ -10,6 +10,8 @@ # or submit itself to any jurisdiction. option(RANS_ENABLE_JSON "compile librans with rapidjson" ON) +option(RANS_LOG_PROCESSED_DATA "logs data processed by encoders/decoders as JSON array to log sink" OFF) + set(RANS_ARCH "" CACHE STRING "Compile librans for a given CPU type,\ see https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html.") set(RANS_COMPILE_OPTIONS "" CACHE STRING "Compile librans with these compiler option") @@ -37,6 +39,9 @@ if(${RANS_ENABLE_JSON}) target_compile_definitions(${LIBRANS} INTERFACE RANS_ENABLE_JSON) target_link_libraries(${LIBRANS} INTERFACE RapidJSON::RapidJSON) endif() +if(${RANS_LOG_PROCESSED_DATA}) + target_compile_definitions(${LIBRANS} INTERFACE RANS_LOG_PROCESSED_DATA) +endif() if (OpenMP_CXX_FOUND AND ( NOT APPLE ) ) target_compile_definitions(${LIBRANS} INTERFACE RANS_OPENMP) target_link_libraries(${LIBRANS} INTERFACE OpenMP::OpenMP_CXX) @@ -182,13 +187,15 @@ o2_add_test(Serialize LABELS utils) target_compile_options(${TEST_SERIALIZE} PRIVATE ${RANS_TEST_ARCH}) -if(${RANS_ENABLE_JSON}) - if (TARGET benchmark::benchmark) - +if (TARGET benchmark::benchmark) o2_add_header_only_library(libransBenchmark TARGETVARNAME LIB_RANS_BENCHMARK - INTERFACE_LINK_LIBRARIES O2::rANS RapidJSON::RapidJSON benchmark::benchmark TBB::tbb) + INTERFACE_LINK_LIBRARIES O2::rANS benchmark::benchmark) target_compile_options(${LIB_RANS_BENCHMARK} INTERFACE -O3 ${RANS_OPTIONS}) + if(TARGET TBB::tbb) + target_compile_definitions(${LIB_RANS_BENCHMARK} INTERFACE RANS_ENABLE_PARALLEL_STL) + target_link_libraries(${LIB_RANS_BENCHMARK} INTERFACE TBB::tbb) + endif() if(${ENABLE_VTUNE_PROFILER}) target_compile_definitions(${LIB_RANS_BENCHMARK} INTERFACE ENABLE_VTUNE_PROFILER) target_link_libraries(${LIB_RANS_BENCHMARK} INTERFACE PkgConfig::Vtune) @@ -248,6 +255,7 @@ if(${RANS_ENABLE_JSON}) IS_BENCHMARK PUBLIC_LINK_LIBRARIES O2::libransBenchmark) + if(${RANS_ENABLE_JSON}) o2_add_executable(TPCEncodeDecode SOURCES benchmarks/bench_ransTPC.cxx COMPONENT_NAME rANS diff --git a/Utilities/rANS/benchmarks/bench_ransDecode.cxx b/Utilities/rANS/benchmarks/bench_ransDecode.cxx index 6e85d7c7cd50c..a642b58a3431d 100644 --- a/Utilities/rANS/benchmarks/bench_ransDecode.cxx +++ b/Utilities/rANS/benchmarks/bench_ransDecode.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief compares performance of different encoders +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -51,11 +52,11 @@ inline constexpr size_t MessageSize = 1ull << 22; // std::binomial_distribution dist(draws, probability); // const size_t sourceSize = messageSize / sizeof(source_T) + 1; // mSourceMessage.resize(sourceSize); -// #ifdef __cpp_lib_execution +// #ifdef RANS_PARALLEL_STL // std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); // #else // std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); -// #endif +// #endif // RANS_PARALLEL_STL // } // } @@ -82,11 +83,11 @@ class SourceMessageProxyUniform std::uniform_int_distribution dist(min, max); const size_t sourceSize = messageSize / sizeof(source_T) + 1; mSourceMessage.resize(sourceSize); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL } } diff --git a/Utilities/rANS/benchmarks/bench_ransDecodeScaling.cxx b/Utilities/rANS/benchmarks/bench_ransDecodeScaling.cxx index 4b8c1c01f98be..2d882aaecf8b7 100644 --- a/Utilities/rANS/benchmarks/bench_ransDecodeScaling.cxx +++ b/Utilities/rANS/benchmarks/bench_ransDecodeScaling.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief compares performance of different encoders +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -48,11 +49,11 @@ class SourceMessageUniform std::uniform_int_distribution dist(0, max); const size_t sourceSize = messageSize / sizeof(source_T) + 1; mSourceMessage.resize(sourceSize); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL } const auto& get() const { return mSourceMessage; }; diff --git a/Utilities/rANS/benchmarks/bench_ransEncode.cxx b/Utilities/rANS/benchmarks/bench_ransEncode.cxx index 67eb78dfdb127..668697aaad455 100644 --- a/Utilities/rANS/benchmarks/bench_ransEncode.cxx +++ b/Utilities/rANS/benchmarks/bench_ransEncode.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief compares performance of different encoders +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -51,11 +52,11 @@ class SourceMessageProxy std::binomial_distribution dist(draws, probability); const size_t sourceSize = messageSize / sizeof(source_T) + 1; mSourceMessage.resize(sourceSize); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL } } diff --git a/Utilities/rANS/benchmarks/bench_ransEncodeImpl.cxx b/Utilities/rANS/benchmarks/bench_ransEncodeImpl.cxx index 1896d5b4ffe81..b6f260996c9ce 100644 --- a/Utilities/rANS/benchmarks/bench_ransEncodeImpl.cxx +++ b/Utilities/rANS/benchmarks/bench_ransEncodeImpl.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief benchmarks different encoding kernels without the need to renorm and stream data +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -66,11 +67,11 @@ class SymbolTableData std::binomial_distribution dist(draws, probability); const size_t sourceSize = messageSize / sizeof(source_T); mSourceMessage.resize(sourceSize); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL const auto histogram = makeDenseHistogram::fromSamples(gsl::span(mSourceMessage)); Metrics metrics{histogram}; diff --git a/Utilities/rANS/benchmarks/bench_ransHistogram.cxx b/Utilities/rANS/benchmarks/bench_ransHistogram.cxx index ca925ea742bdb..084d4b8fca1bc 100644 --- a/Utilities/rANS/benchmarks/bench_ransHistogram.cxx +++ b/Utilities/rANS/benchmarks/bench_ransHistogram.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief compares performance of different encoders +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -56,11 +57,11 @@ class SourceMessageProxyBinomial std::binomial_distribution dist(draws, probability); const size_t sourceSize = messageSize / sizeof(source_T) + 1; mSourceMessage.resize(sourceSize); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL } return mSourceMessage; @@ -89,7 +90,7 @@ class SourceMessageProxyUniform std::uniform_int_distribution dist(min, max); const size_t sourceSize = messageSize / sizeof(source_T) + 1; mSourceMessage.resize(sourceSize); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); @@ -136,7 +137,7 @@ void ransMakeHistogramBenchmark(benchmark::State& st, Args&&... args) histogram_type hist{}; hist.addSamples(gsl::span(inputData)); - for (std::ptrdiff_t symbol = histogram.getOffset(); symbol != histogram.getOffset() + histogram.size(); ++symbol) { + for (std::ptrdiff_t symbol = histogram.getOffset(); symbol != histogram.getOffset() + static_cast(histogram.size()); ++symbol) { if (histogram[symbol] > 0) { LOG_IF(info, histogram[symbol] != hist[symbol]) << fmt::format("[{}]: {} != {}", symbol, hist[symbol], histogram[symbol]); isSame = isSame && (histogram[symbol] == hist[symbol]); @@ -189,7 +190,7 @@ void ransAccessHistogramBenchmark(benchmark::State& st, Args&&... args) #endif bool isSame = true; - for (std::ptrdiff_t symbol = histogram.getOffset(); symbol != histogram.getOffset() + histogram.size(); ++symbol) { + for (std::ptrdiff_t symbol = histogram.getOffset(); symbol != histogram.getOffset() + static_cast(histogram.size()); ++symbol) { if (histogram[symbol] > 0) { LOG_IF(info, histogram[symbol] != hist[symbol]) << fmt::format("[{}]: {} != {}", symbol, hist[symbol], histogram[symbol]); isSame = isSame && (histogram[symbol] == hist[symbol]); diff --git a/Utilities/rANS/benchmarks/bench_ransPack.cxx b/Utilities/rANS/benchmarks/bench_ransPack.cxx index 188ab481dd4ab..c00a18b323a76 100644 --- a/Utilities/rANS/benchmarks/bench_ransPack.cxx +++ b/Utilities/rANS/benchmarks/bench_ransPack.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief benchmarks packing algorithms and compares it to memcpy +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -46,11 +47,11 @@ std::vector makeRandomUniformVector(size_t nelems, source_T min = std: std::mt19937 mt(0); // same seed we want always the same distrubution of random numbers; std::uniform_int_distribution dist(min, max); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, result.begin(), result.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(result.begin(), result.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL return result; }; diff --git a/Utilities/rANS/benchmarks/bench_ransStreaming.cxx b/Utilities/rANS/benchmarks/bench_ransStreaming.cxx index 04e592aea2403..c079b04910936 100644 --- a/Utilities/rANS/benchmarks/bench_ransStreaming.cxx +++ b/Utilities/rANS/benchmarks/bench_ransStreaming.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief benchmarks streaming out data from rANS state to memory +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -61,11 +62,11 @@ class RenormingData std::binomial_distribution dist(draws, probability); const size_t sourceSize = messageSize / sizeof(source_T); mSourceMessage.resize(sourceSize); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(mSourceMessage.begin(), mSourceMessage.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL const auto histogram = makeDenseHistogram::fromSamples(gsl::span(mSourceMessage)); Metrics metrics{histogram}; diff --git a/Utilities/rANS/benchmarks/bench_ransUnpack.cxx b/Utilities/rANS/benchmarks/bench_ransUnpack.cxx index 9af732f5b58c5..fce8b99cbc910 100644 --- a/Utilities/rANS/benchmarks/bench_ransUnpack.cxx +++ b/Utilities/rANS/benchmarks/bench_ransUnpack.cxx @@ -13,12 +13,13 @@ /// @author Michael Lettrich /// @brief benchmark unpacking of data compared to memcpy +#include "rANS/internal/common/defines.h" + #include #include #include #include -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif #include @@ -46,11 +47,11 @@ std::vector makeRandomUniformVector(size_t nelems, source_T min = std: std::mt19937 mt(0); // same seed we want always the same distrubution of random numbers; std::uniform_int_distribution dist(min, max); -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL std::generate(std::execution::par_unseq, result.begin(), result.end(), [&dist, &mt]() { return dist(mt); }); #else std::generate(result.begin(), result.end(), [&dist, &mt]() { return dist(mt); }); -#endif +#endif // RANS_PARALLEL_STL return result; }; diff --git a/Utilities/rANS/benchmarks/helpers.h b/Utilities/rANS/benchmarks/helpers.h index a06531310b19f..936c478c51054 100644 --- a/Utilities/rANS/benchmarks/helpers.h +++ b/Utilities/rANS/benchmarks/helpers.h @@ -16,25 +16,28 @@ #ifndef RANS_BENCHMARKS_HELPERS_H_ #define RANS_BENCHMARKS_HELPERS_H_ +#include "rANS/internal/common/defines.h" + #ifdef ENABLE_VTUNE_PROFILER #include #endif +#ifdef RANS_ENABLE_JSON #include #include #include #include - +#endif // RANS_ENABLE_JSON #include #include #include "rANS/internal/common/exceptions.h" -#include -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL #include #endif +#ifdef RANS_ENABLE_JSON struct TPCCompressedClusters { TPCCompressedClusters() = default; @@ -276,6 +279,7 @@ TPCCompressedClusters readFile(const std::string& filename) } return compressedClusters; }; +#endif // RANS_ENABLE_JSON template struct EncodeBuffer { @@ -303,11 +307,11 @@ struct DecodeBuffer { template bool operator==(const T& correct) { -#ifdef __cpp_lib_execution +#ifdef RANS_PARALLEL_STL return std::equal(std::execution::par_unseq, buffer.begin(), buffer.end(), std::begin(correct), std::end(correct)); #else return std::equal(buffer.begin(), buffer.end(), std::begin(correct), std::end(correct)); -#endif +#endif // RANS_PARALLEL_STL } std::vector buffer{}; diff --git a/Utilities/rANS/include/rANS/compat.h b/Utilities/rANS/include/rANS/compat.h index f94be92bed472..a4917246eb455 100644 --- a/Utilities/rANS/include/rANS/compat.h +++ b/Utilities/rANS/include/rANS/compat.h @@ -16,6 +16,10 @@ #ifndef RANS_COMPAT_H_ #define RANS_COMPAT_H_ +#ifdef __CLING__ +#error rANS should not be exposed to root +#endif + #include #include "rANS/internal/common/typetraits.h" diff --git a/Utilities/rANS/include/rANS/decode.h b/Utilities/rANS/include/rANS/decode.h index f98d746276ce6..df42560d8e122 100644 --- a/Utilities/rANS/include/rANS/decode.h +++ b/Utilities/rANS/include/rANS/decode.h @@ -16,6 +16,10 @@ #ifndef RANS_DECODE_H_ #define RANS_DECODE_H_ +#ifdef __CLING__ +#error rANS should not be exposed to root +#endif + #include "rANS/internal/containers/DenseSymbolTable.h" #include "rANS/internal/containers/Symbol.h" #include "rANS/internal/decode/Decoder.h" diff --git a/Utilities/rANS/include/rANS/encode.h b/Utilities/rANS/include/rANS/encode.h index a62fe1121db90..42b79b030ce39 100644 --- a/Utilities/rANS/include/rANS/encode.h +++ b/Utilities/rANS/include/rANS/encode.h @@ -16,6 +16,10 @@ #ifndef RANS_ENCODE_H_ #define RANS_ENCODE_H_ +#ifdef __CLING__ +#error rANS should not be exposed to root +#endif + #include "rANS/internal/containers/DenseSymbolTable.h" #include "rANS/internal/containers/Symbol.h" #include "rANS/internal/encode/Encoder.h" diff --git a/Utilities/rANS/include/rANS/factory.h b/Utilities/rANS/include/rANS/factory.h index 549031ae8ecdb..602d3c5879887 100644 --- a/Utilities/rANS/include/rANS/factory.h +++ b/Utilities/rANS/include/rANS/factory.h @@ -16,6 +16,10 @@ #ifndef RANS_FACTORY_H_ #define RANS_FACTORY_H_ +#ifdef __CLING__ +#error rANS should not be exposed to root +#endif + #include "rANS/internal/common/defaults.h" #include "rANS/internal/common/typetraits.h" #include "rANS/internal/common/codertraits.h" diff --git a/Utilities/rANS/include/rANS/histogram.h b/Utilities/rANS/include/rANS/histogram.h index 1c5263495d139..5815fdd45bd1d 100644 --- a/Utilities/rANS/include/rANS/histogram.h +++ b/Utilities/rANS/include/rANS/histogram.h @@ -16,6 +16,10 @@ #ifndef RANS_HISTOGRAM_H_ #define RANS_HISTOGRAM_H_ +#ifdef __CLING__ +#error rANS should not be exposed to root +#endif + #include "rANS/internal/containers/DenseHistogram.h" #include "rANS/internal/containers/AdaptiveHistogram.h" #include "rANS/internal/containers/SparseHistogram.h" diff --git a/Utilities/rANS/include/rANS/internal/common/defines.h b/Utilities/rANS/include/rANS/internal/common/defines.h index 5f1a5731a20bd..21afb4ff01750 100644 --- a/Utilities/rANS/include/rANS/internal/common/defines.h +++ b/Utilities/rANS/include/rANS/internal/common/defines.h @@ -16,6 +16,8 @@ #ifndef RANS_INTERNAL_COMMON_DEFINES_H_ #define RANS_INTERNAL_COMMON_DEFINES_H_ +#include + #ifdef RANS_AVX2 #error RANS_AVX2 cannot be directly set #endif @@ -66,4 +68,8 @@ #define RANS_FMA #endif +#if defined(RANS_ENABLE_PARALLEL_STL) && defined(__cpp_lib_execution) +#define RANS_PARALLEL_STL +#endif + #endif /*RANS_INTERNAL_COMMON_DEFINES_H_*/ \ No newline at end of file diff --git a/Utilities/rANS/include/rANS/internal/containers/DenseHistogram.h b/Utilities/rANS/include/rANS/internal/containers/DenseHistogram.h index 354ee96b84f48..177245ef7bec6 100644 --- a/Utilities/rANS/include/rANS/internal/containers/DenseHistogram.h +++ b/Utilities/rANS/include/rANS/internal/containers/DenseHistogram.h @@ -184,7 +184,7 @@ inline bool DenseHistogram>::i ret = false; } } - if (max - min > this->MaxSize) { + if (max - min > static_cast(this->MaxSize)) { LOGP(warning, "DenseHistogram exceeds {} elements threshold", this->MaxSize); ret = false; } @@ -233,8 +233,6 @@ inline auto DenseHistogram>::a constexpr size_t nUnroll = 4 * ElemsPerQWord; auto iter = begin; - const source_type offset = this->getOffset(); - if (getRangeBits(min, max) <= 17) { container_type histogram{this->mContainer.size(), this->mContainer.getOffset()}; @@ -586,7 +584,16 @@ auto DenseHistogram>::addFrequ addedHistogramView = trim(addedHistogramView); if constexpr (std::is_unsigned_v) { - LOG_IF(warning, addedHistogramView.getMin() < 0) << fmt::format("trying to add frequencies for a signed symbol to a DenseHistogram of an unsiged type."); + LOG_IF(warning, addedHistogramView.getMin() < 0) << fmt::format("trying to add frequencies of a signed symbol type to a DenseHistogram of an unsiged type."); + } + if constexpr (std::is_signed_v) { + const std::ptrdiff_t sourceTypeMax = std::numeric_limits::max(); + const bool isMinOutOfRange = addedHistogramView.getMin() > sourceTypeMax; + const bool isMaxOutOfRange = addedHistogramView.getMax() > sourceTypeMax; + + if (isMaxOutOfRange || isMinOutOfRange) { + LOGP(warning, "trying to add frequencies of an unsigned symbol type to a DenseHistogram of a signed type"); + } } const auto thisHistogramView = makeHistogramView(this->mContainer); diff --git a/Utilities/rANS/include/rANS/internal/containers/ReverseSymbolLookupTable.h b/Utilities/rANS/include/rANS/internal/containers/ReverseSymbolLookupTable.h index 506aaaf53fa40..3a79619b2338d 100644 --- a/Utilities/rANS/include/rANS/internal/containers/ReverseSymbolLookupTable.h +++ b/Utilities/rANS/include/rANS/internal/containers/ReverseSymbolLookupTable.h @@ -71,8 +71,8 @@ class ReverseSymbolLookupTable return mLut[cumul]; }; - inline const iterator_type begin() const noexcept { return mLut.data(); }; - inline const iterator_type end() const noexcept { return mLut.data() + size(); }; + inline iterator_type begin() const noexcept { return mLut.data(); }; + inline iterator_type end() const noexcept { return mLut.data() + size(); }; container_type mLut{}; }; diff --git a/Utilities/rANS/include/rANS/internal/containers/Symbol.h b/Utilities/rANS/include/rANS/internal/containers/Symbol.h index 0c00c46e1a627..5269447e22344 100644 --- a/Utilities/rANS/include/rANS/internal/containers/Symbol.h +++ b/Utilities/rANS/include/rANS/internal/containers/Symbol.h @@ -37,11 +37,8 @@ class Symbol // TODO(milettri): fix once ROOT cling respects the standard http://wg21.link/p1286r2 constexpr Symbol() noexcept {}; // NOLINT - constexpr Symbol(value_type frequency, value_type cumulative, size_t symbolTablePrecision = 0) - : mSymbol{frequency, cumulative} - { - (void)symbolTablePrecision; // silence compiler warnings - }; + constexpr Symbol(value_type frequency, value_type cumulative, [[maybe_unused]] size_t symbolTablePrecision = 0) + : mSymbol{frequency, cumulative} {}; [[nodiscard]] inline constexpr value_type getFrequency() const noexcept { return mSymbol[0]; }; [[nodiscard]] inline constexpr value_type getCumulative() const noexcept { return mSymbol[1]; }; [[nodiscard]] inline constexpr const value_type* data() const noexcept { return mSymbol.data(); }; diff --git a/Utilities/rANS/include/rANS/internal/decode/DecoderConcept.h b/Utilities/rANS/include/rANS/internal/decode/DecoderConcept.h index 759b22aca5d7b..e888ff9b24dcb 100644 --- a/Utilities/rANS/include/rANS/internal/decode/DecoderConcept.h +++ b/Utilities/rANS/include/rANS/internal/decode/DecoderConcept.h @@ -83,7 +83,7 @@ class DecoderConcept auto decode = [&, this](coder_type& decoder) { const auto cumul = decoder.get(); const value_type symbol = lookupSymbol(cumul); -#ifdef O2_RANS_PRINT_PROCESSED_DATA +#ifdef RANS_LOG_PROCESSED_DATA arrayLogger << symbol.first; #endif return std::make_tuple(symbol.first, decoder.advanceSymbol(inputIter, symbol.second)); @@ -110,7 +110,7 @@ class DecoderConcept std::tie(*outputIter++, inputIter) = decode(decoders[i]); } -#ifdef O2_RANS_PRINT_PROCESSED_DATA +#ifdef RANS_LOG_PROCESSED_DATA LOG(info) << "decoderOutput:" << arrayLogger; #endif } diff --git a/Utilities/rANS/include/rANS/internal/encode/Encoder.h b/Utilities/rANS/include/rANS/internal/encode/Encoder.h index ef097992ac9cd..fb6015b7aa021 100644 --- a/Utilities/rANS/include/rANS/internal/encode/Encoder.h +++ b/Utilities/rANS/include/rANS/internal/encode/Encoder.h @@ -143,7 +143,11 @@ decltype(auto) Encoder::process(source_IT EncoderSymbolMapper symbolMapper{this->mSymbolTable, literalsBegin}; typename coder_type::symbol_type encoderSymbols[2]{}; + // one past the end. Will not be dereferenced, thus safe. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" coder_type* codersREnd = advanceIter(coders.data(), -1); +#pragma GCC diagnostic pop coder_type* activeCoder = codersREnd + nPartialCoderIterations; // we are encoding backwards! diff --git a/Utilities/rANS/include/rANS/internal/encode/EncoderSymbolMapper.h b/Utilities/rANS/include/rANS/internal/encode/EncoderSymbolMapper.h index 083dad90f81c6..8925dfb6b34cf 100644 --- a/Utilities/rANS/include/rANS/internal/encode/EncoderSymbolMapper.h +++ b/Utilities/rANS/include/rANS/internal/encode/EncoderSymbolMapper.h @@ -254,8 +254,6 @@ class EncoderSymbolMapper frequencies; @@ -330,8 +328,6 @@ class EncoderSymbolMapper frequencies; diff --git a/Utilities/rANS/include/rANS/internal/encode/SingleStreamEncoderImpl.h b/Utilities/rANS/include/rANS/internal/encode/SingleStreamEncoderImpl.h index 580df53fc64bd..ff7d6535365e0 100644 --- a/Utilities/rANS/include/rANS/internal/encode/SingleStreamEncoderImpl.h +++ b/Utilities/rANS/include/rANS/internal/encode/SingleStreamEncoderImpl.h @@ -145,8 +145,6 @@ class SingleStreamEncoderImpl : public SingleStreamEncoderImplBasegetFrequency() != 0); - const state_type old = this->mState; - const auto [newState, streamPosition] = this->renorm(this->mState, outputIter, symbol->getFrequency()); // coding function state_type quotient = static_cast((static_cast(newState) * symbol->getReciprocalFrequency()) >> 64); diff --git a/Utilities/rANS/include/rANS/internal/pack/DictionaryStreamReader.h b/Utilities/rANS/include/rANS/internal/pack/DictionaryStreamReader.h index f84eaebf25fb9..0109abe102fc3 100644 --- a/Utilities/rANS/include/rANS/internal/pack/DictionaryStreamReader.h +++ b/Utilities/rANS/include/rANS/internal/pack/DictionaryStreamReader.h @@ -84,7 +84,7 @@ class DictionaryStreamParser template template -DictionaryStreamParser::DictionaryStreamParser(buffer_IT begin, buffer_IT end, source_type max) : mEnd{begin}, mPos{end}, mIndex(max) +DictionaryStreamParser::DictionaryStreamParser(buffer_IT begin, buffer_IT end, source_type max) : mPos{end}, mEnd{begin}, mIndex(max) { static_assert(std::is_pointer_v, "can only deserialize from raw pointers"); diff --git a/Utilities/rANS/include/rANS/internal/transform/algorithmImpl.h b/Utilities/rANS/include/rANS/internal/transform/algorithmImpl.h index 84dff9609a574..382f116d5ca04 100644 --- a/Utilities/rANS/include/rANS/internal/transform/algorithmImpl.h +++ b/Utilities/rANS/include/rANS/internal/transform/algorithmImpl.h @@ -161,7 +161,7 @@ inline void forEachIndexValue(container_T&& container, IT begin, IT end, F funct using container_type = removeCVRef_t; typename container_type::source_type index = container.getOffset() + std::distance(container.begin(), begin); - for (size_t i = 0; i < std::distance(begin, end); ++i) { + for (std::ptrdiff_t i = 0; i < std::distance(begin, end); ++i) { functor(index++, begin[i]); } } diff --git a/Utilities/rANS/include/rANS/internal/transform/renorm.h b/Utilities/rANS/include/rANS/internal/transform/renorm.h index 5e3d7d551a665..8d8ab238aaa91 100644 --- a/Utilities/rANS/include/rANS/internal/transform/renorm.h +++ b/Utilities/rANS/include/rANS/internal/transform/renorm.h @@ -74,7 +74,6 @@ decltype(auto) renorm(histogram_T histogram, Metrics #include #include +#include #ifdef RANS_ENABLE_JSON #include diff --git a/Utilities/rANS/include/rANS/utils.h b/Utilities/rANS/include/rANS/utils.h index e4412349a9120..cea058341f921 100644 --- a/Utilities/rANS/include/rANS/utils.h +++ b/Utilities/rANS/include/rANS/utils.h @@ -16,6 +16,10 @@ #ifndef RANS_UTILS_H_ #define RANS_UTILS_H_ +#ifdef __CLING__ +#error rANS should not be exposed to root +#endif + #include #include #include diff --git a/Utilities/rANS/run/bin-encode-decode.cxx b/Utilities/rANS/run/bin-encode-decode.cxx index a6cca6b55c59b..daa991461f5d8 100644 --- a/Utilities/rANS/run/bin-encode-decode.cxx +++ b/Utilities/rANS/run/bin-encode-decode.cxx @@ -109,7 +109,7 @@ int main(int argc, char* argv[]) if (renormedHistogram.hasIncompressibleSymbol()) { LOG(info) << "With incompressible symbols"; - auto [encoderEnd, incompressibleEnd] = encoder.process(tokens.begin(), tokens.end(), std::back_inserter(encoderBuffer), std::back_inserter(incompressibleSymbols)); + [[maybe_unused]] auto res = encoder.process(tokens.begin(), tokens.end(), std::back_inserter(encoderBuffer), std::back_inserter(incompressibleSymbols)); LOGP(info, "nIncompressible {}", incompressibleSymbols.size()); decoder.process(encoderBuffer.end(), decodeBuffer.begin(), tokens.size(), NSTREAMS, incompressibleSymbols.end()); } else { diff --git a/Utilities/rANS/test/test_ransHistograms.cxx b/Utilities/rANS/test/test_ransHistograms.cxx index b08cf18f7b385..362fd107778df 100644 --- a/Utilities/rANS/test/test_ransHistograms.cxx +++ b/Utilities/rANS/test/test_ransHistograms.cxx @@ -359,8 +359,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_addFrequenciesSignChange, histogram_T, histog {static_cast(5), 5}, }; - const size_t fixedSizeOffset = std::numeric_limits::min(); - histogram_T histogram{}; histogram.addFrequencies(frequencies.begin(), frequencies.end(), 0); @@ -389,7 +387,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_addFrequenciesSignChange, histogram_T, histog const std::ptrdiff_t offset = utils::pow2(utils::toBits() - 1); if constexpr (std::is_same_v>) { - const std::ptrdiff_t largeOffset = utils::toBits() - 1; BOOST_CHECK_THROW(histogram.addFrequencies(frequencies2.begin(), frequencies2.end(), offset), HistogramError); BOOST_CHECK_THROW(histogram2.addFrequencies(gsl::make_span(frequencies2), offset), HistogramError); } else { diff --git a/Utilities/rANS/test/test_ransMetrics.cxx b/Utilities/rANS/test/test_ransMetrics.cxx index 42dcf7519f664..4370194306f1d 100644 --- a/Utilities/rANS/test/test_ransMetrics.cxx +++ b/Utilities/rANS/test/test_ransMetrics.cxx @@ -238,7 +238,6 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_singleElementMetrics, histogram_T, histogram_ std::vector frequencies{5}; histogram_T histogram{frequencies.begin(), frequencies.end(), 2}; const auto [min, max] = getMinMax(histogram); - const float eps = 1e-2; const size_t nUsedAlphabetSymbols = countNUsedAlphabetSymbols(histogram); const Metrics metrics{histogram}; diff --git a/Utilities/rANS/test/test_ransPack.cxx b/Utilities/rANS/test/test_ransPack.cxx index bc63ad04cdf96..e6aaaffa79ebf 100644 --- a/Utilities/rANS/test/test_ransPack.cxx +++ b/Utilities/rANS/test/test_ransPack.cxx @@ -56,7 +56,6 @@ using source_types = boost::mp11::mp_list BOOST_AUTO_TEST_CASE_TEMPLATE(test_computePackingBufferSize, buffer_T, buffer_types) { - size_t nElems{}; size_t packingWidth = utils::toBits() - 3; BOOST_CHECK_EQUAL(0, (computePackingBufferSize(0, packingWidth))); @@ -132,7 +131,7 @@ BOOST_AUTO_TEST_CASE(test_packUnpackStream) source_type min = *std::min_element(source.begin(), source.end()); - auto packingBufferEnd = pack(source.data(), source.size(), packingBuffer.data(), packingWidth, min); + [[maybe_unused]] auto packingBufferEnd = pack(source.data(), source.size(), packingBuffer.data(), packingWidth, min); std::vector unpackBuffer(source.size(), 0); unpack(packingBuffer.data(), source.size(), unpackBuffer.data(), packingWidth, min); diff --git a/Utilities/rANS/test/test_ransSIMDEncoderKernels.cxx b/Utilities/rANS/test/test_ransSIMDEncoderKernels.cxx index 12f65a6c610b6..ea099fd2238f7 100644 --- a/Utilities/rANS/test/test_ransSIMDEncoderKernels.cxx +++ b/Utilities/rANS/test/test_ransSIMDEncoderKernels.cxx @@ -120,7 +120,9 @@ struct AosToSoaFixture { uint32_t counter = 0; for (size_t i = 0; i < nElems; ++i) { - Symbol symbol{counter++, counter++, 0}; + const auto freq = counter++; + const auto cumul = counter++; + Symbol symbol{freq, cumul, 0}; mFrequencies(i) = symbol.getFrequency(); mCumulative(i) = symbol.getCumulative(); @@ -228,10 +230,10 @@ struct SSERenormFixture { statesVec[0] = load(states[0]); statesVec[1] = load(states[1]); - stream_iterator newstreamOutIter = ransRenorm(statesVec, - frequenciesVec, - SymbolTablePrecisionBits, - streamOutBuffer.begin(), newStatesVec); + [[maybe_unused]] stream_iterator newstreamOutIter = ransRenorm(statesVec, + frequenciesVec, + SymbolTablePrecisionBits, + streamOutBuffer.begin(), newStatesVec); epi64_t newStates(0); store(newStatesVec[0], newStates[0]); diff --git a/Utilities/rANS/test/test_ransSparseVector.cxx b/Utilities/rANS/test/test_ransSparseVector.cxx index fab69aa5750ad..fc1a2980e3226 100644 --- a/Utilities/rANS/test/test_ransSparseVector.cxx +++ b/Utilities/rANS/test/test_ransSparseVector.cxx @@ -47,8 +47,6 @@ BOOST_AUTO_TEST_CASE(test_write) { sparseVector_type vec{}; - const size_t fixedSizeOffset = std::numeric_limits::min(); - std::vector samples{-5, -2, 1, 3, 5, 8, -5, 5, 1, 1, 1, 14, 8, 8, 8, 8, 8, 8, 8, 8, utils::pow2(18) + 1}; std::unordered_map results{{-5, 2}, {-2, 1}, {-1, 0}, {0, 0}, {1, 4}, {2, 0}, {3, 1}, {4, 0}, {5, 2}, {8, 9}, {14, 1}, {utils::pow2(18) + 1, 1}}; diff --git a/Utilities/rANS/test/test_ransSymbolTables.cxx b/Utilities/rANS/test/test_ransSymbolTables.cxx index 5384607b2b0c4..bbadb8c8c46a0 100644 --- a/Utilities/rANS/test/test_ransSymbolTables.cxx +++ b/Utilities/rANS/test/test_ransSymbolTables.cxx @@ -123,7 +123,7 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(test_symbolTable, histogram_T, histogram_t) BOOST_CHECK_EQUAL(symbolTable.getPrecision(), scaleBits); - for (source_type index = 0; index < rescaledFrequencies.size(); ++index) { + for (source_type index = 0; index < static_cast(rescaledFrequencies.size()); ++index) { auto symbol = symbolTable[index + 6]; BOOST_CHECK_EQUAL(symbol.getFrequency(), rescaledFrequencies[index]); BOOST_CHECK_EQUAL(symbol.getCumulative(), cumulativeFrequencies[index]); diff --git a/cmake/O2AddHipifiedExecutable.cmake b/cmake/O2AddHipifiedExecutable.cmake new file mode 100644 index 0000000000000..6f25e3b061cf5 --- /dev/null +++ b/cmake/O2AddHipifiedExecutable.cmake @@ -0,0 +1,76 @@ +# 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_guard() + +include(O2AddExecutable) + +function(o2_add_hipified_executable baseTargetName) + # Parse arguments in the same way o2_add_executable does + cmake_parse_arguments(PARSE_ARGV + 1 + A + "IS_TEST;NO_INSTALL;IS_BENCHMARK" + "COMPONENT_NAME;TARGETVARNAME" + "SOURCES;PUBLIC_LINK_LIBRARIES;JOB_POOL") + + # Process each .cu file to generate a .hip file + set(HIPIFY_EXECUTABLE "/opt/rocm/bin/hipify-perl") + set(HIP_SOURCES) + + foreach(file ${A_SOURCES}) + get_filename_component(ABS_CUDA_SORUCE ${file} ABSOLUTE) + if(file MATCHES "\\.cu$") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${file}) + get_filename_component(CUDA_SOURCE ${file} NAME) + string(REPLACE ".cu" ".hip" HIP_SOURCE ${CUDA_SOURCE}) + set(OUTPUT_HIP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${HIP_SOURCE}") + list(APPEND HIP_SOURCES ${OUTPUT_HIP_FILE}) + + add_custom_command( + OUTPUT ${OUTPUT_HIP_FILE} + COMMAND ${HIPIFY_EXECUTABLE} --quiet-warnings ${ABS_CUDA_SORUCE} | sed '1{/\#include \"hip\\/hip_runtime.h\"/d}' > ${OUTPUT_HIP_FILE} + DEPENDS ${file} + ) + else() + list(APPEND HIP_SOURCES ${file}) + endif() + endforeach() + + # This is a bit cumbersome, but it seems the only suitable since cmake_parse_arguments is not capable to filter only the SOURCE variadic values + set(FORWARD_ARGS "") + if(A_IS_TEST) + list(APPEND FORWARD_ARGS "IS_TEST") + endif() + if(A_NO_INSTALL) + list(APPEND FORWARD_ARGS "NO_INSTALL") + endif() + if(A_IS_BENCHMARK) + list(APPEND FORWARD_ARGS "IS_BENCHMARK") + endif() + if(A_COMPONENT_NAME) + list(APPEND FORWARD_ARGS "COMPONENT_NAME" ${A_COMPONENT_NAME}) + endif() + if(A_TARGETVARNAME) + list(APPEND FORWARD_ARGS "TARGETVARNAME" ${A_TARGETVARNAME}) + endif() + if(A_PUBLIC_LINK_LIBRARIES) + list(APPEND FORWARD_ARGS "PUBLIC_LINK_LIBRARIES" ${A_PUBLIC_LINK_LIBRARIES}) + endif() + if(A_JOB_POOL) + list(APPEND FORWARD_ARGS "JOB_POOL" ${A_JOB_POOL}) + endif() + + # Call o2_add_executable with new sources + o2_add_executable("${baseTargetName}" + SOURCES ${HIP_SOURCES} + ${FORWARD_ARGS}) +endfunction() \ No newline at end of file diff --git a/cmake/O2AddHipifiedLibrary.cmake b/cmake/O2AddHipifiedLibrary.cmake new file mode 100644 index 0000000000000..0c75f8723a435 --- /dev/null +++ b/cmake/O2AddHipifiedLibrary.cmake @@ -0,0 +1,71 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +include_guard() + +include(O2AddLibrary) + +function(o2_add_hipified_library baseTargetName) + # Parse arguments in the same way o2_add_library does + cmake_parse_arguments(PARSE_ARGV + 1 + A + "" + "TARGETVARNAME" + "SOURCES;PUBLIC_INCLUDE_DIRECTORIES;PUBLIC_LINK_LIBRARIES;PRIVATE_INCLUDE_DIRECTORIES;PRIVATE_LINK_LIBRARIES" + ) + + # Process each .cu file to generate a .hip file + set(HIPIFY_EXECUTABLE "/opt/rocm/bin/hipify-perl") + set(HIP_SOURCES) + + foreach(file ${A_SOURCES}) + get_filename_component(ABS_CUDA_SORUCE ${file} ABSOLUTE) + if(file MATCHES "\\.cu$") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${file}) + get_filename_component(CUDA_SOURCE ${file} NAME) + string(REPLACE ".cu" ".hip" HIP_SOURCE ${CUDA_SOURCE}) + set(OUTPUT_HIP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${HIP_SOURCE}") + list(APPEND HIP_SOURCES ${OUTPUT_HIP_FILE}) + + add_custom_command( + OUTPUT ${OUTPUT_HIP_FILE} + COMMAND ${HIPIFY_EXECUTABLE} --quiet-warnings ${ABS_CUDA_SORUCE} | sed '1{/\#include \"hip\\/hip_runtime.h\"/d}' > ${OUTPUT_HIP_FILE} + DEPENDS ${file} + ) + else() + list(APPEND HIP_SOURCES ${file}) + endif() + endforeach() + + # This is a bit cumbersome, but it seems the only suitable since cmake_parse_arguments is not capable to filter only the SOURCE variadic values + set(FORWARD_ARGS "") + if(A_TARGETVARNAME) + list(APPEND FORWARD_ARGS "TARGETVARNAME" ${A_TARGETVARNAME}) + endif() + if(A_PUBLIC_INCLUDE_DIRECTORIES) + list(APPEND FORWARD_ARGS "PUBLIC_INCLUDE_DIRECTORIES" ${A_PUBLIC_INCLUDE_DIRECTORIES}) + endif() + if(A_PUBLIC_LINK_LIBRARIES) + list(APPEND FORWARD_ARGS "PUBLIC_LINK_LIBRARIES" ${A_PUBLIC_LINK_LIBRARIES}) + endif() + if(A_PRIVATE_INCLUDE_DIRECTORIES) + list(APPEND FORWARD_ARGS "PRIVATE_INCLUDE_DIRECTORIES" ${A_PRIVATE_INCLUDE_DIRECTORIES}) + endif() + if(A_PRIVATE_LINK_LIBRARIES) + list(APPEND FORWARD_ARGS "PRIVATE_LINK_LIBRARIES" ${A_PRIVATE_LINK_LIBRARIES}) + endif() + + # Call o2_add_library with new sources + o2_add_library("${baseTargetName}" + SOURCES ${HIP_SOURCES} + ${FORWARD_ARGS}) +endfunction() \ No newline at end of file diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 924a724aaa82d..01bd08616c9ec 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -26,6 +26,34 @@ string(TOUPPER "${ENABLE_OPENCL1}" ENABLE_OPENCL1) string(TOUPPER "${ENABLE_OPENCL2}" ENABLE_OPENCL2) string(TOUPPER "${ENABLE_HIP}" ENABLE_HIP) +function(set_target_cuda_arch target) + if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "86") + message(STATUS "Using optimized CUDA settings for Ampere GPU") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_AMPERE) + elseif(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "75") + message(STATUS "Using optimized CUDA settings for Turing GPU") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_TURING) + else() + message(STATUS "Defaulting optimized CUDA settings for Ampere GPU") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_AMPERE) + endif() +endfunction() + +function(set_target_hip_arch target) + if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx906") + message(STATUS "Using optimized HIP settings for MI50 GPU") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_VEGA) + elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx908") + message(STATUS "Using optimized HIP settings for MI100 GPU") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI2xx) + elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx90a") + message(STATUS "Using optimized HIP settings for MI210 GPU") + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI2xx) + else() + target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_VEGA) + endif() +endfunction() + # Detect and enable CUDA if(ENABLE_CUDA) set(CMAKE_CUDA_STANDARD 17) @@ -177,10 +205,19 @@ if(ENABLE_HIP) if(HIP_AMDGPUTARGET) set(AMDGPU_TARGETS "${HIP_AMDGPUTARGET}" CACHE STRING "AMD GPU targets to compile for" FORCE) set(GPU_TARGETS "${HIP_AMDGPUTARGET}" CACHE STRING "AMD GPU targets to compile for" FORCE) + set(CMAKE_HIP_ARCHITECTURES "${HIP_AMDGPUTARGET}" CACHE STRING "AMD GPU targets to compile for" FORCE) endif() if(EXISTS "/opt/rocm/lib/cmake" AND (NOT DEFINED CMAKE_PREFIX_PATH OR NOT ${CMAKE_PREFIX_PATH} MATCHES "rocm") AND (NOT ENV{CMAKE_PREFIX_PATH} MATCHES "rocm")) set(CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH};/opt/rocm/lib/cmake") + if (NOT DEFINED CMAKE_HIP_COMPILER) + set(CMAKE_HIP_COMPILER "/opt/rocm/llvm/bin/clang++") + endif() + if (NOT DEFINED HIP_CLANG_PATH) + set(HIP_CLANG_PATH "/opt/rocm/llvm/bin") + endif() endif() + include(CheckLanguage) + check_language(HIP) find_package(hip) find_package(hipcub) find_package(rocprim) @@ -200,17 +237,23 @@ if(ENABLE_HIP) set_package_properties(rocprim PROPERTIES TYPE REQUIRED) set_package_properties(rocthrust PROPERTIES TYPE REQUIRED) endif() + if (CMAKE_HIP_COMPILER) + enable_language(HIP) + message(STATUS "HIP language enabled: ${CMAKE_HIP_COMPILER}") + endif() if(hip_FOUND AND hipcub_FOUND AND rocthrust_FOUND AND rocprim_FOUND AND hip_HIPCC_EXECUTABLE AND hip_HIPIFY_PERL_EXECUTABLE) set(HIP_ENABLED ON) set_target_properties(roc::rocthrust PROPERTIES IMPORTED_GLOBAL TRUE) message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION})") set(O2_HIP_CMAKE_CXX_FLAGS "-fgpu-defer-diag -mllvm -amdgpu-enable-lower-module-lds=false -Wno-invalid-command-line-argument -Wno-unused-command-line-argument -Wno-invalid-constexpr -Wno-ignored-optimization-argument -Wno-unused-private-field -Wno-pass-failed") set(O2_HIP_CMAKE_LINK_FLAGS "-Wno-pass-failed") + string(REGEX REPLACE "(gfx1[0-9]+;?)" "" CMAKE_HIP_ARCHITECTURES "${CMAKE_HIP_ARCHITECTURES}") # ROCm currently doesn’t support integrated graphics if(HIP_AMDGPUTARGET) foreach(HIP_ARCH ${HIP_AMDGPUTARGET}) set(O2_HIP_CMAKE_CXX_FLAGS "${O2_HIP_CMAKE_CXX_FLAGS} --offload-arch=${HIP_ARCH}") set(O2_HIP_CMAKE_LINK_FLAGS "${O2_HIP_CMAKE_LINK_FLAGS} --offload-arch=${HIP_ARCH}") endforeach() + set(CMAKE_HIP_ARCHITECTURES "${HIP_AMDGPUTARGET}") # If GPU build is enforced we override autodetection endif() if(NOT DEFINED GPUCA_NO_FAST_MATH OR NOT ${GPUCA_NO_FAST_MATH}) set(O2_HIP_CMAKE_CXX_FLAGS "${O2_HIP_CMAKE_CXX_FLAGS} -fgpu-flush-denormals-to-zero") # -ffast-math disabled, since apparently it leads to miscompilation and crashes in FollowLooper kernel @@ -219,6 +262,7 @@ if(ENABLE_HIP) string(REGEX REPLACE "(.*)bin/c\\+\\+\$" "\\1" HIP_GCC_TOOLCHAIN_PATH "${CMAKE_CXX_COMPILER}") set(O2_HIP_CMAKE_CXX_FLAGS "${O2_HIP_CMAKE_CXX_FLAGS} --gcc-toolchain=${HIP_GCC_TOOLCHAIN_PATH}") # -ffast-math disabled, since apparently it leads to miscompilation and crashes in FollowLooper kernel endif() + set(CMAKE_HIP_FLAGS "${CMAKE_HIP_FLAGS} ${O2_HIP_CMAKE_CXX_FLAGS}") else() set(HIP_ENABLED OFF) endif() diff --git a/dependencies/O2CompileFlags.cmake b/dependencies/O2CompileFlags.cmake index 4c01d4bd8f5f6..7fe9019701efe 100644 --- a/dependencies/O2CompileFlags.cmake +++ b/dependencies/O2CompileFlags.cmake @@ -80,6 +80,18 @@ MARK_AS_ADVANCED( CMAKE_Fortran_FLAGS_COVERAGE CMAKE_LINK_FLAGS_COVERAGE) +# Options to enable the thread sanitizer +set(CMAKE_CXX_FLAGS_THREADSANITIZER "-g -O2 -fsanitize=thread -fPIC") +set(CMAKE_C_FLAGS_THREADSANITIZER "${CMAKE_CXX_FLAGS_THREADSANITIZER}") +set(CMAKE_Fortran_FLAGS_THREADSANITIZER "-g -O2 -fsanitize=thread -fPIC") +set(CMAKE_LINK_FLAGS_THREADSANITIZER "-fsanitize=thread -fPIC") + +MARK_AS_ADVANCED( + CMAKE_CXX_FLAGS_THREADSANITIZER + CMAKE_C_FLAGS_THREADSANITIZER + CMAKE_Fortran_FLAGS_THREADSANITIZER + CMAKE_LINK_FLAGS_THREADSANITIZER) + #Check the compiler and set the compile and link flags IF (NOT CMAKE_BUILD_TYPE) Message(STATUS "Set BuildType to DEBUG") @@ -114,7 +126,6 @@ set(CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE} "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}} set(CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE} "${CMAKE_C_FLAGS_${CMAKE_BUILD_TYPE}} ${CMAKE_C_WARNINGS}") if(APPLE) - set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-undefined,error") # avoid undefined in our libs elseif(UNIX) set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined") # avoid undefined in our libs endif() diff --git a/macro/build_geometry.C b/macro/build_geometry.C index 3caf94abd5323..85a3ae30481b1 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -54,6 +54,7 @@ #include #include #include +#include #endif void finalize_geometry(FairRunSim* run); @@ -161,6 +162,12 @@ void build_geometry(FairRunSim* run = nullptr) if (isActivated("A3IP")) { run->AddModule(new o2::passive::Alice3Pipe("A3IP", "Alice 3 beam pipe", !isActivated("TRK"), 0.48f, 0.025f, 1000.f, 3.7f, 0.08f, 1000.f)); } + + // the absorber + if (isActivated("A3ABSO")) { + run->AddModule(new o2::passive::Alice3Absorber("A3ABSO", "ALICE3 Absorber")); + } + #endif // the absorber @@ -179,103 +186,120 @@ void build_geometry(FairRunSim* run = nullptr) run->AddModule(new o2::passive::FrameStructure("FRAME", "Frame")); } + std::vector detId2RunningId = std::vector(o2::detectors::DetID::nDetectors, -1); // a mapping of detectorId to a dense runtime index + // used for instance to set bits in the hit structure of MCTracks; -1 means that there is no bit associated + + auto addReadoutDetector = [&detId2RunningId, &run](o2::base::Detector* detector) { + static int runningid = 0; // this is static for constant lambda interfaces --> use fixed type and not auto in the lambda! + run->AddModule(detector); + if (detector->IsActive()) { + auto detID = detector->GetDetId(); + detId2RunningId[detID] = runningid; + LOG(info) << " DETID " << detID << " vs " << detector->GetDetId() << " mapped to hit bit index " << runningid; + runningid++; + } + }; + if (isActivated("TOF")) { // TOF - run->AddModule(new o2::tof::Detector(isReadout("TOF"))); + addReadoutDetector(new o2::tof::Detector(isReadout("TOF"))); } if (isActivated("TRD")) { // TRD - run->AddModule(new o2::trd::Detector(isReadout("TRD"))); + addReadoutDetector(new o2::trd::Detector(isReadout("TRD"))); } if (isActivated("TPC")) { // tpc - run->AddModule(new o2::tpc::Detector(isReadout("TPC"))); + addReadoutDetector(new o2::tpc::Detector(isReadout("TPC"))); } #ifdef ENABLE_UPGRADES if (isActivated("IT3")) { // IT3 - run->AddModule(new o2::its::Detector(isReadout("IT3"), "IT3")); + addReadoutDetector(new o2::its::Detector(isReadout("IT3"), "IT3")); } if (isActivated("TRK")) { // ALICE 3 TRK - run->AddModule(new o2::trk::Detector(isReadout("TRK"))); + addReadoutDetector(new o2::trk::Detector(isReadout("TRK"))); } if (isActivated("FT3")) { // ALICE 3 FT3 - run->AddModule(new o2::ft3::Detector(isReadout("FT3"))); + addReadoutDetector(new o2::ft3::Detector(isReadout("FT3"))); } if (isActivated("FCT")) { // ALICE 3 FCT - run->AddModule(new o2::fct::Detector(isReadout("FCT"))); + addReadoutDetector(new o2::fct::Detector(isReadout("FCT"))); } #endif if (isActivated("ITS")) { // its - run->AddModule(new o2::its::Detector(isReadout("ITS"))); + addReadoutDetector(new o2::its::Detector(isReadout("ITS"))); } if (isActivated("MFT")) { // mft - run->AddModule(new o2::mft::Detector(isReadout("MFT"))); + addReadoutDetector(new o2::mft::Detector(isReadout("MFT"))); } if (isActivated("MCH")) { // mch - run->AddModule(new o2::mch::Detector(isReadout("MCH"))); + addReadoutDetector(new o2::mch::Detector(isReadout("MCH"))); } if (isActivated("MID")) { // mid - run->AddModule(new o2::mid::Detector(isReadout("MID"))); + addReadoutDetector(new o2::mid::Detector(isReadout("MID"))); } if (isActivated("EMC")) { // emcal - run->AddModule(new o2::emcal::Detector(isReadout("EMC"))); + addReadoutDetector(new o2::emcal::Detector(isReadout("EMC"))); } if (isActivated("PHS")) { // phos - run->AddModule(new o2::phos::Detector(isReadout("PHS"))); + addReadoutDetector(new o2::phos::Detector(isReadout("PHS"))); } if (isActivated("CPV")) { // cpv - run->AddModule(new o2::cpv::Detector(isReadout("CPV"))); + addReadoutDetector(new o2::cpv::Detector(isReadout("CPV"))); } if (isActivated("FT0")) { // FIT-T0 - run->AddModule(new o2::ft0::Detector(isReadout("FT0"))); + addReadoutDetector(new o2::ft0::Detector(isReadout("FT0"))); } if (isActivated("FV0")) { // FIT-V0 - run->AddModule(new o2::fv0::Detector(isReadout("FV0"))); + addReadoutDetector(new o2::fv0::Detector(isReadout("FV0"))); } if (isActivated("FDD")) { // FIT-FDD - run->AddModule(new o2::fdd::Detector(isReadout("FDD"))); + addReadoutDetector(new o2::fdd::Detector(isReadout("FDD"))); } if (isActivated("HMP")) { // HMP - run->AddModule(new o2::hmpid::Detector(isReadout("HMP"))); + addReadoutDetector(new o2::hmpid::Detector(isReadout("HMP"))); } if (isActivated("ZDC")) { // ZDC - run->AddModule(new o2::zdc::Detector(isReadout("ZDC"))); + addReadoutDetector(new o2::zdc::Detector(isReadout("ZDC"))); } if (geomonly) { run->Init(); } + + // register the DetId2HitIndex lookup with the detector class by copying the vector + o2::base::Detector::setDetId2HitBitIndex(detId2RunningId); } diff --git a/macro/run_trac_its.C b/macro/run_trac_its.C index 67bc075c5754d..824e4ebcf5d79 100644 --- a/macro/run_trac_its.C +++ b/macro/run_trac_its.C @@ -1,3 +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. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include #include @@ -30,7 +41,6 @@ #include "CCDB/BasicCCDBManager.h" #include "CCDB/CCDBTimeStampUtils.h" #include "DataFormatsITSMFT/TopologyDictionary.h" -#endif #include "ReconstructionDataFormats/PrimaryVertex.h" // hack to silence JIT compiler #include "ITStracking/ROframe.h" @@ -208,3 +218,5 @@ void run_trac_its(std::string path = "./", std::string outputfile = "o2trac_its. timer.Stop(); timer.Print(); } + +#endif diff --git a/prodtests/full-system-test/aggregator-workflow.sh b/prodtests/full-system-test/aggregator-workflow.sh index 55fb15e78b66d..c87485bf4e6e8 100755 --- a/prodtests/full-system-test/aggregator-workflow.sh +++ b/prodtests/full-system-test/aggregator-workflow.sh @@ -81,11 +81,11 @@ fi : ${INTEGRATEDCURR_TF_PER_SLOT:=150000} # setting for FT0, FV0, FDD and TOF if [[ $BEAMTYPE == "PbPb" ]]; then - : ${LHCPHASE_TF_PER_SLOT:=264} - : ${TOF_CHANNELOFFSETS_UPDATE:=3000} - : ${TOF_CHANNELOFFSETS_DELTA_UPDATE:=500} + : ${LHCPHASE_TF_PER_SLOT:=100000} + : ${TOF_CHANNELOFFSETS_UPDATE:=300000} + : ${TOF_CHANNELOFFSETS_DELTA_UPDATE:=50000} else - : ${LHCPHASE_TF_PER_SLOT:=26400} + : ${LHCPHASE_TF_PER_SLOT:=100000} : ${TOF_CHANNELOFFSETS_UPDATE:=300000} : ${TOF_CHANNELOFFSETS_DELTA_UPDATE:=50000} fi @@ -206,7 +206,7 @@ if [[ $AGGREGATOR_TASKS == BARREL_TF ]] || [[ $AGGREGATOR_TASKS == ALL ]]; then fi fi if [[ $CALIB_TOF_DIAGNOSTICS == 1 ]]; then - add_W o2-calibration-tof-diagnostic-workflow "--tf-per-slot 26400 --max-delay 1" "" 0 + add_W o2-calibration-tof-diagnostic-workflow "--tf-per-slot $LHCPHASE_TF_PER_SLOT --max-delay 1" "" 0 fi # TPC if [[ $CALIB_TPC_SCDCALIB == 1 ]]; then @@ -258,10 +258,12 @@ IDC_DELTA="--disable-IDCDelta true" # off by default if [[ "${DISABLE_IDC_DELTA:-}" == "1" ]]; then IDC_DELTA=""; fi if [[ "${ENABLE_IDC_DELTA_FILE:-}" == "1" ]]; then IDC_DELTA+=" --dump-IDCDelta-calib-data true --output-dir $CALIB_DIR --meta-output-dir $EPN2EOS_METAFILES_DIR "; fi +if [[ "${DISABLE_IDC_PAD_MAP_WRITING:-}" == 1 ]]; then TPC_WRITING_PAD_STATUS_MAP=""; else TPC_WRITING_PAD_STATUS_MAP="--enableWritingPadStatusMap true"; fi + if ! workflow_has_parameter CALIB_LOCAL_INTEGRATED_AGGREGATOR; then if [[ $CALIB_TPC_IDC == 1 ]] && [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC || $AGGREGATOR_TASKS == ALL ]]; then add_W o2-tpc-idc-distribute "--crus ${crus} --timeframes ${nTFs} --output-lanes ${lanesFactorize} --send-precise-timestamp true --condition-tf-per-query ${nTFs} --n-TFs-buffer ${nBuffer}" - add_W o2-tpc-idc-factorize "--n-TFs-buffer ${nBuffer} --input-lanes ${lanesFactorize} --crus ${crus} --timeframes ${nTFs} --nthreads-grouping 8 --nthreads-IDC-factorization 8 --sendOutputFFT true --enable-CCDB-output true --enablePadStatusMap true --use-precise-timestamp true $IDC_DELTA" "TPCIDCGroupParam.groupPadsSectorEdges=32211" + add_W o2-tpc-idc-factorize "--n-TFs-buffer ${nBuffer} --input-lanes ${lanesFactorize} --crus ${crus} --timeframes ${nTFs} --nthreads-grouping 8 --nthreads-IDC-factorization 8 --sendOutputFFT true --enable-CCDB-output true --enablePadStatusMap true ${TPC_WRITING_PAD_STATUS_MAP} --use-precise-timestamp true $IDC_DELTA" "TPCIDCGroupParam.groupPadsSectorEdges=32211" add_W o2-tpc-idc-ft-aggregator "--rangeIDC 200 --inputLanes ${lanesFactorize} --nFourierCoeff 40 --nthreads 8" fi if [[ $CALIB_TPC_SAC == 1 ]] && [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC || $AGGREGATOR_TASKS == ALL ]]; then diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index b892d8b7a876f..1add2f9a28d65 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -31,7 +31,8 @@ fi : ${CTF_MAX_FREE_DISK_WAIT:="600"} # if not enough disk space after this time throw error # entropy encoding/decoding mode, default "" is equivalent to '--ans-version compat' (compatible with < 09/2023 data), -# use '--ans-version 1.0 --ccdb-dict none' for the new per-TF dictionary mode +# use '--ans-version 1.0 --ctf-dict none' for the new per-TF dictionary mode +[[ $EPNSYNCMODE == 1 && -z ${RANS_OPT:-} ]] && RANS_OPT="--ans-version 1.0 --ctf-dict none" : ${RANS_OPT:=""} workflow_has_parameter CTF && export SAVECTF=1 @@ -48,7 +49,7 @@ if [[ $EPNSYNCMODE == 1 ]] || type numactl >/dev/null 2>&1 && [[ `numactl -H | g [[ $NUMAGPUIDS != 0 ]] && ARGS_ALL+=" --child-driver 'numactl --membind $NUMAID --cpunodebind $NUMAID'" fi if [[ -z ${TIMEFRAME_RATE_LIMIT:-} ]] && [[ $DIGITINPUT != 1 ]]; then - TIMEFRAME_RATE_LIMIT=$((12 * 230 / $RECO_NUM_NODES_WORKFLOW * ($NUMAGPUIDS != 0 ? 1 : 2) * 128 / $NHBPERTF)) + TIMEFRAME_RATE_LIMIT=$((12 * 230 / ($RECO_NUM_NODES_WORKFLOW < 230 ? $RECO_NUM_NODES_WORKFLOW : 230) * ($NUMAGPUIDS != 0 ? 1 : 2) * 128 / $NHBPERTF)) [[ $BEAMTYPE != "PbPb" && ${HIGH_RATE_PP:-0} == 0 ]] && TIMEFRAME_RATE_LIMIT=$(($TIMEFRAME_RATE_LIMIT * 3)) ! has_detector TPC && TIMEFRAME_RATE_LIMIT=$(($TIMEFRAME_RATE_LIMIT * 4)) [[ ! -z ${EPN_GLOBAL_SCALING:-} ]] && TIMEFRAME_RATE_LIMIT=$(($TIMEFRAME_RATE_LIMIT * $EPN_GLOBAL_SCALING)) @@ -87,16 +88,22 @@ EMCRAW2C_CONFIG= PHS_CONFIG= MCH_CONFIG_KEY= CTP_CONFIG= +INTERACTION_TAG_CONFIG_KEY= +: ${STRTRACKING:=} : ${ITSEXTRAERR:=} : ${TRACKTUNETPCINNER:=} : ${ITSTPC_CONFIG_KEY:=} +: ${AOD_INPUT:=$TRACK_SOURCES} +: ${AODPROD_OPT:=} [[ "0$DISABLE_ROOT_OUTPUT" == "00" ]] && DISABLE_ROOT_OUTPUT= if [[ -z ${ALPIDE_ERR_DUMPS:-} ]]; then [[ $EPNSYNCMODE == 1 ]] && ALPIDE_ERR_DUMPS="1" || ALPIDE_ERR_DUMPS="0" fi - +if [[ $CTFINPUT != 1 ]]; then + GPU_OUTPUT+=",tpc-triggers" +fi if [[ $SYNCMODE == 1 ]]; then if [[ $BEAMTYPE == "PbPb" ]]; then ITS_CONFIG_KEY+="fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:-100};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:-200};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:-20};" @@ -141,12 +148,14 @@ fi if [[ $BEAMTYPE == "PbPb" ]]; then PVERTEXING_CONFIG_KEY+="pvertexer.maxChi2TZDebris=2000;" + INTERACTION_TAG_CONFIG_KEY="ft0tag.minAmplitudeA=${INT_TAG_FT0A:-5};ft0tag.minAmplitudeC=${INT_TAG_FT0C:-5};ft0tag.minAmplitudeAC=${INT_TAG_FT0AC:-20};" elif [[ $BEAMTYPE == "pp" ]]; then PVERTEXING_CONFIG_KEY+="pvertexer.maxChi2TZDebris=10;" fi if [[ $BEAMTYPE == "cosmic" ]]; then [[ -z ${ITS_CONFIG+x} ]] && ITS_CONFIG=" --tracking-mode cosmics" + : ${STRTRACKING:=" --disable-strangeness-tracker "} elif [[ $SYNCMODE == 1 ]]; then [[ -z ${ITS_CONFIG+x} ]] && ITS_CONFIG=" --tracking-mode sync" else @@ -193,7 +202,13 @@ has_detector_flp_processing CPV && CPV_INPUT=digits if [[ $EPNSYNCMODE == 1 ]]; then EVE_CONFIG+=" --eve-dds-collection-index 0" MIDDEC_CONFIG+=" --feeId-config-file \"$MID_FEEID_MAP\"" - if [[ $EXTINPUT == 1 ]] && [[ $GPUTYPE != "CPU" ]] && [[ -z "$GPU_NUM_MEM_REG_CALLBACKS" ]]; then GPU_NUM_MEM_REG_CALLBACKS=4; fi + if [[ $EXTINPUT == 1 ]] && [[ $GPUTYPE != "CPU" ]] && [[ -z "$GPU_NUM_MEM_REG_CALLBACKS" ]]; then + if [[ $NUMAGPUIDS == 1 ]]; then + GPU_NUM_MEM_REG_CALLBACKS=5 + else + GPU_NUM_MEM_REG_CALLBACKS=4 + fi + fi fi if [[ $SYNCRAWMODE == 1 ]]; then GPU_CONFIG_KEY+="GPU_proc.tpcIncreasedMinClustersPerRow=500000;GPU_proc.ignoreNonFatalGPUErrors=1;GPU_proc.throttleAlarms=1;GPU_proc.conservativeMemoryEstimate=1;" @@ -275,6 +290,8 @@ if has_detector_calib PHS && workflow_has_parameter CALIB; then PHS_CONFIG+=" --fullclu-output" fi +[[ ${O2_GPU_DOUBLE_PIPELINE:-$EPNSYNCMODE} == 1 ]] && GPU_CONFIG+=" --enableDoublePipeline" + ( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && has_detector EMC && RAW_EMC_SUBSPEC=" --subspecification 1 " has_detector_reco MID && has_detector_matching MCHMID && MFTMCHConf="FwdMatching.useMIDMatch=true;" || MFTMCHConf="FwdMatching.useMIDMatch=false;" @@ -385,7 +402,13 @@ if [[ ! -z $INPUT_DETECTOR_LIST ]]; then done done [[ ! -z ${TIMEFRAME_RATE_LIMIT:-} ]] && [[ $TIMEFRAME_RATE_LIMIT != 0 ]] && PROXY_CHANNEL+=";name=metric-feedback,type=pull,method=connect,address=ipc://${UDS_PREFIX}metric-feedback-${O2JOBID:-$NUMAID},transport=shmem,rateLogging=0" - add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --readout-proxy \"--channel-config \\\"$PROXY_CHANNEL\\\"\" ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} ${TIMEFRAME_SHM_LIMIT:-}" "" 0 + if [[ $EPNSYNCMODE == 1 ]]; then + RAWPROXY_CONFIG="--print-input-sizes 1000" + else + RAWPROXY_CONFIG="--print-input-sizes 1" + fi + + add_W o2-dpl-raw-proxy "--dataspec \"$PROXY_INSPEC\" --inject-missing-data $RAWPROXY_CONFIG --readout-proxy \"--channel-config \\\"$PROXY_CHANNEL\\\"\" ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} ${TIMEFRAME_SHM_LIMIT:-}" "" 0 elif [[ $DIGITINPUT == 1 ]]; then [[ $NTIMEFRAMES != 1 ]] && { echo "Digit input works only with NTIMEFRAMES=1" 1>&2; exit 1; } DISABLE_DIGIT_ROOT_INPUT= @@ -423,7 +446,7 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then has_detector FV0 && ! has_detector_from_global_reader FV0 && ! has_detector_flp_processing FV0 && add_W o2-fv0-flp-dpl-workflow "$DISABLE_ROOT_OUTPUT --pipeline $(get_N fv0-datareader-dpl FV0 RAW 1)" has_detector MID && ! has_detector_from_global_reader MID && add_W o2-mid-raw-to-digits-workflow "$MIDDEC_CONFIG --pipeline $(get_N MIDRawDecoder MID RAW 1),$(get_N MIDDecodedDataAggregator MID RAW 1)" has_detector MCH && ! has_detector_from_global_reader MCH && add_W o2-mch-raw-to-digits-workflow "--pipeline $(get_N mch-data-decoder MCH RAW 1)" - has_detector TOF && ! has_detector_from_global_reader TOF && ! has_detector_flp_processing TOF && add_W o2-tof-compressor "--pipeline $(get_N tof-compressor-0 TOF RAW 1)" + has_detector TOF && ! has_detector_from_global_reader TOF && ! has_detector_flp_processing TOF && add_W o2-tof-compressor "--tof-compressor-paranoid --pipeline $(get_N tof-compressor-0 TOF RAW 1)" has_detector FDD && ! has_detector_from_global_reader FDD && ! has_detector_flp_processing FDD && add_W o2-fdd-flp-dpl-workflow "$DISABLE_ROOT_OUTPUT --pipeline $(get_N fdd-datareader-dpl FDD RAW 1)" has_detector TRD && ! has_detector_from_global_reader TRD && add_W o2-trd-datareader "$DISABLE_ROOT_OUTPUT --sortDigits --pipeline $(get_N trd-datareader TRD RAW 1 TRDRAWDEC)" "" 0 has_detector ZDC && ! has_detector_from_global_reader ZDC && add_W o2-zdc-raw2digits "$DISABLE_ROOT_OUTPUT --pipeline $(get_N zdc-datareader-dpl ZDC RAW 1)" @@ -436,15 +459,15 @@ fi # --------------------------------------------------------------------------------------------------------------------- # Common reconstruction workflows -(has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" $ASK_CTP_LUMI_GPU --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT --pipeline gpu-reconstruction:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER" +(has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" $ASK_CTP_LUMI_GPU --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT --pipeline gpu-reconstruction:${N_TPCTRK:-1},gpu-reconstruction-prepare:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER" (has_detector_reco TOF || has_detector_ctf TOF) && ! has_detector_from_global_reader TOF && add_W o2-tof-reco-workflow "$TOF_CONFIG --input-type $TOF_INPUT --output-type $TOF_OUTPUT $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N tof-compressed-decoder TOF RAW 1),$(get_N TOFClusterer TOF REST 1)" has_detector_reco ITS && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--trackerCA $ITS_CONFIG $DISABLE_MC $DISABLE_DIGIT_CLUSTER_INPUT $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" has_detector_reco FT0 && ! has_detector_from_global_reader FT0 && add_W o2-ft0-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N ft0-reconstructor FT0 REST 1)" has_detector_reco TRD && ! has_detector_from_global_reader TRD && add_W o2-trd-tracklet-transformer "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TRD_FILTER_CONFIG --pipeline $(get_N TRDTRACKLETTRANSFORMER TRD REST 1 TRDTRKTRANS)" -has_detectors_reco ITS TPC && has_detector_matching ITSTPC && add_W o2-tpcits-match-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $SEND_ITSTPC_DTGL $TPC_CORR_SCALING --nthreads $ITSTPC_THREADS --pipeline $(get_N itstpc-track-matcher MATCH REST $ITSTPC_THREADS TPCITS)" "$ITSTPC_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" -has_detector_reco TRD && [[ ! -z "$TRD_SOURCES" ]] && add_W o2-trd-global-tracking "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TRD_CONFIG $TRD_FILTER_CONFIG $TPC_CORR_SCALING --track-sources $TRD_SOURCES --pipeline $(get_N trd-globaltracking_TPC_ITS-TPC_ TRD REST 1 TRDTRK),$(get_N trd-globaltracking_TPC_FT0_ITS-TPC_ TRD REST 1 TRDTRK)" "$TRD_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" -has_detector_reco TOF && [[ ! -z "$TOF_SOURCES" ]] && add_W o2-tof-matcher-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TPC_CORR_SCALING --track-sources $TOF_SOURCES --pipeline $(get_N tof-matcher TOF REST 1 TOFMATCH)" "$ITSMFT_STROBES;$ITSEXTRAERR" -has_detectors TPC && [[ -z "$DISABLE_ROOT_OUTPUT" && "${SKIP_TPC_CLUSTERSTRACKS_OUTPUT:-}" != 1 ]] && add_W o2-tpc-reco-workflow "--input-type pass-through --output-type clusters,tracks,send-clusters-per-sector $DISABLE_MC" +has_detectors_reco ITS TPC && has_detector_matching ITSTPC && add_W o2-tpcits-match-workflow "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $SEND_ITSTPC_DTGL $TPC_CORR_SCALING --nthreads $ITSTPC_THREADS --pipeline $(get_N itstpc-track-matcher MATCH REST $ITSTPC_THREADS TPCITS)" "$ITSTPC_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +has_detector_reco TRD && [[ ! -z "$TRD_SOURCES" ]] && add_W o2-trd-global-tracking "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TRD_CONFIG $TRD_FILTER_CONFIG $TPC_CORR_SCALING --track-sources $TRD_SOURCES --pipeline $(get_N trd-globaltracking_TPC_ITS-TPC_ TRD REST 1 TRDTRK),$(get_N trd-globaltracking_TPC_FT0_ITS-TPC_ TRD REST 1 TRDTRK)" "$TRD_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +has_detector_reco TOF && [[ ! -z "$TOF_SOURCES" ]] && add_W o2-tof-matcher-workflow "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TPC_CORR_SCALING --track-sources $TOF_SOURCES --pipeline $(get_N tof-matcher TOF REST 1 TOFMATCH)" "$ITSMFT_STROBES;$ITSEXTRAERR" +has_detectors TPC && [[ -z "$DISABLE_ROOT_OUTPUT" && "${SKIP_TPC_CLUSTERSTRACKS_OUTPUT:-}" != 1 ]] && add_W o2-tpc-reco-workflow "--input-type pass-through --output-type clusters,tpc-triggers,tracks,send-clusters-per-sector $DISABLE_MC" # --------------------------------------------------------------------------------------------------------------------- # Reconstruction workflows normally active only in async mode in async mode ($LIST_OF_ASYNC_RECO_STEPS), but can be forced via $WORKFLOW_EXTRA_PROCESSING_STEPS @@ -456,8 +479,8 @@ has_detector FV0 && ! has_detector_from_global_reader FV0 && has_processing_step has_detector ZDC && ! has_detector_from_global_reader ZDC && has_processing_step ZDC_RECO && add_W o2-zdc-digits-reco "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" has_detector HMP && ! has_detector_from_global_reader HMP && has_processing_step HMP_RECO && add_W o2-hmpid-digits-to-clusters-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT --pipeline $(get_N HMP-Clusterization HMP REST 1 HMPCLUS)" has_detector HMP && ! has_detector_from_global_reader HMP && has_processing_step HMP_RECO && add_W o2-hmpid-matcher-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" -has_detectors_reco MCH MID && has_detector_matching MCHMID && add_W o2-muon-tracks-matcher-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N muon-track-matcher MATCH REST 1)" -has_detectors_reco MFT MCH && has_detector_matching MFTMCH && add_W o2-globalfwd-matcher-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N globalfwd-track-matcher MATCH REST 1 FWDMATCH)" "$MFTMCHConf" +has_detectors_reco MCH MID && has_detector_matching MCHMID && add_W o2-muon-tracks-matcher-workflow "$DISABLE_ROOT_INPUT $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N muon-track-matcher MATCH REST 1)" +has_detectors_reco MFT MCH && has_detector_matching MFTMCH && add_W o2-globalfwd-matcher-workflow "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N globalfwd-track-matcher MATCH REST 1 FWDMATCH)" "$MFTMCHConf" # --------------------------------------------------------------------------------------------------------------------- # Reconstruction workflows needed only in case QC or CALIB was requested @@ -472,7 +495,7 @@ has_detectors_reco MFT MCH && has_detector_matching MFTMCH && add_W o2-globalfwd has_detector_reco TRD && ( [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-trd-digittracklet-writer ) && ! has_detector_from_global_reader TRD && add_W o2-trd-digittracklet-writer has_detector_reco TPC && [[ "0${TPC_CONVERT_LINKZS_TO_RAW:-}" == "01" ]] && ( [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-gpu-reco-workflow ) && ! has_detector_from_global_reader TPC && add_W o2-tpc-reco-workflow "--input-type digitizer --output-type digits $DISABLE_MC" has_detector_reco CPV && ( [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-cpv-cluster-writer-workflow ) && ! has_detector_from_global_reader CPV && add_W o2-cpv-cluster-writer-workflow "$DISABLE_MC" -( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && ! has_detector_from_global_reader EMC && has_detector EMC && add_W o2-emcal-cell-writer-workflow "--disable-mc --subspec 10 --cell-writer-name emcal-led-cells-writer --emcal-led-cells-writer \"--outfile emcledcells.root\"" +( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && ! has_detector_from_global_reader EMC && has_detector EMC && add_W o2-emcal-cell-writer-workflow "$DISABLE_MC --subspec 10 --cell-writer-name emcal-led-cells-writer --emcal-led-cells-writer \"--outfile emcledcells.root\"" [[ $CTFINPUT == 1 ]] && has_detector CTP && ! has_detector_from_global_reader CTP && ( [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-ctp-digit-writer ) && add_W o2-ctp-digit-writer "$DISABLE_ROOT_OUTPUT" has_detector_reco EMC && ( [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && ! has_detector_from_global_reader EMC && add_W o2-emcal-cell-writer-workflow "$DISABLE_MC" has_detector_reco PHS && ( [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-phos-cell-writer-workflow ) && ! has_detector_from_global_reader PHS && add_W o2-phos-cell-writer-workflow "$DISABLE_MC" @@ -484,12 +507,13 @@ has_detector_reco MCH && ( [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output # always run vertexing if requested and if there are some sources, but in cosmic mode we work in pass-trough mode (create record for non-associated tracks) ( [[ $BEAMTYPE == "cosmic" ]] || ! has_detector_reco ITS) && PVERTEX_CONFIG+=" --skip" -has_detector_matching PRIMVTX && [[ ! -z "$VERTEXING_SOURCES" ]] && add_W o2-primary-vertexing-workflow "$DISABLE_MC $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $PVERTEX_CONFIG --pipeline $(get_N primary-vertexing MATCH REST 1 PRIMVTX),$(get_N pvertex-track-matching MATCH REST 1 PRIMVTXMATCH)" "${PVERTEXING_CONFIG_KEY}" +has_detector_matching PRIMVTX && [[ ! -z "$VERTEXING_SOURCES" ]] && add_W o2-primary-vertexing-workflow "$DISABLE_MC $DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $PVERTEX_CONFIG --pipeline $(get_N primary-vertexing MATCH REST 1 PRIMVTX),$(get_N pvertex-track-matching MATCH REST 1 PRIMVTXMATCH)" "${PVERTEXING_CONFIG_KEY}" -if [[ $BEAMTYPE != "cosmic" ]]; then - : ${STRTRACKING:="--disable-strangeness-tracker"} - has_detectors_reco ITS && has_detector_matching SECVTX && has_detector_matching && STRTRACKING="" - has_detectors_reco ITS && has_detector_matching SECVTX && [[ ! -z "$SVERTEXING_SOURCES" ]] && add_W o2-secondary-vertexing-workflow "$DISABLE_MC $STRTRACKING $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $TPC_CORR_SCALING --vertexing-sources $SVERTEXING_SOURCES --threads $SVERTEX_THREADS --pipeline $(get_N secondary-vertexing MATCH REST $SVERTEX_THREADS SECVTX)" +if [[ $BEAMTYPE != "cosmic" ]] && has_detectors_reco ITS && has_detector_matching SECVTX && [[ ! -z "$SVERTEXING_SOURCES" ]]; then + add_W o2-secondary-vertexing-workflow "$DISABLE_MC $STRTRACKING $DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $TPC_CORR_SCALING --vertexing-sources $SVERTEXING_SOURCES --threads $SVERTEX_THREADS --pipeline $(get_N secondary-vertexing MATCH REST $SVERTEX_THREADS SECVTX)" + SECTVTX_ON="1" +else + SECTVTX_ON="0" fi # --------------------------------------------------------------------------------------------------------------------- @@ -541,18 +565,15 @@ workflow_has_parameters CALIB CALIB_LOCAL_INTEGRATED_AGGREGATOR && { source ${CA # RS this is a temporary setting : ${ED_TRACKS:=$TRACK_SOURCES} : ${ED_CLUSTERS:=$TRACK_SOURCES} -workflow_has_parameter EVENT_DISPLAY && [[ $NUMAID == 0 ]] && [[ ! -z "$ED_TRACKS" ]] && [[ ! -z "$ED_CLUSTERS" ]] && add_W o2-eve-export-workflow "--display-tracks $ED_TRACKS --display-clusters $ED_CLUSTERS --skipOnEmptyInput $DISABLE_DIGIT_ROOT_INPUT --number-of_tracks 50000 $EVE_CONFIG $DISABLE_MC" "$ITSMFT_STROBES" +workflow_has_parameter EVENT_DISPLAY && [[ $NUMAID == 0 ]] && [[ ! -z "$ED_TRACKS" ]] && [[ ! -z "$ED_CLUSTERS" ]] && [[ $EPNSYNCMODE == 0 || ${EPN_NODE_MI100:-0} == 0 ]] && add_W o2-eve-export-workflow "--display-tracks $ED_TRACKS --display-clusters $ED_CLUSTERS --skipOnEmptyInput $DISABLE_ROOT_INPUT --number-of_tracks 50000 $EVE_CONFIG $DISABLE_MC" "$ITSMFT_STROBES" workflow_has_parameter GPU_DISPLAY && [[ $NUMAID == 0 ]] && add_W o2-gpu-display "${ED_TRACKS+--display-tracks} $ED_TRACKS ${ED_CLUSTERS+--display-clusters} $ED_CLUSTERS" # --------------------------------------------------------------------------------------------------------------------- # AOD -: ${AOD_INPUT:=$TRACK_SOURCES} -: ${AODPROD_OPT:=} -( ! has_detector_matching SECVTX || ! has_detectors_reco ITS || [[ $BEAMTYPE == "cosmic" ]]) && AODPROD_OPT+=" --disable-secondary-vertices" -( ! has_detector_matching STRK || ! has_detector_matching SECVTX || ! has_detectors_reco ITS || [[ $BEAMTYPE == "cosmic" ]]) && AODPROD_OPT+=" --disable-strangeness-tracking" - -workflow_has_parameter AOD && [[ ! -z "$AOD_INPUT" ]] && add_W o2-aod-producer-workflow "$AODPROD_OPT --info-sources $AOD_INPUT $DISABLE_DIGIT_ROOT_INPUT --aod-writer-keep dangling --aod-writer-resfile \"AO2D\" --aod-writer-resmode UPDATE $DISABLE_MC --pipeline $(get_N aod-producer-workflow AOD REST 1 AODPROD)" +[[ ${SECTVTX_ON:-} != "1" ]] && AODPROD_OPT+=" --disable-secondary-vertices " +AODPROD_OPT+=" $STRTRACKING " +workflow_has_parameter AOD && [[ ! -z "$AOD_INPUT" ]] && add_W o2-aod-producer-workflow "$AODPROD_OPT --info-sources $AOD_INPUT $DISABLE_ROOT_INPUT --aod-writer-keep dangling --aod-writer-resfile \"AO2D\" --aod-writer-resmode UPDATE $DISABLE_MC --pipeline $(get_N aod-producer-workflow AOD REST 1 AODPROD)" # --------------------------------------------------------------------------------------------------------------------- # Quality Control diff --git a/prodtests/full-system-test/start_tmux.sh b/prodtests/full-system-test/start_tmux.sh index a84b5a2b043b8..f45a8a83841ec 100755 --- a/prodtests/full-system-test/start_tmux.sh +++ b/prodtests/full-system-test/start_tmux.sh @@ -16,7 +16,7 @@ if [[ -z "${WORKFLOW_PARAMETERS+x}" ]]; then else export WORKFLOW_PARAMETERS="${WORKFLOW_PARAMETERS},CALIB_PROXIES" fi - [[ -z $ARGS_EXTRA_PROCESS_o2_eve_export_workflow ]] && ARGS_EXTRA_PROCESS_o2_eve_export_workflow="--disable-write" + [[ -z $ARGS_EXTRA_PROCESS_o2_eve_export_workflow ]] && export ARGS_EXTRA_PROCESS_o2_eve_export_workflow="--disable-write" if [[ -z "${GEN_TOPO_WORKDIR}" ]]; then mkdir -p gen_topo_tmp export GEN_TOPO_WORKDIR=`pwd`/gen_topo_tmp @@ -36,6 +36,7 @@ if [[ "0$FST_TMUX_NO_EPN" != "01" ]]; then [[ -z $GPUMEMSIZE ]] && export GPUMEMSIZE=$(( 24 << 30 )) [[ -z $NUMAGPUIDS ]] && export NUMAGPUIDS=1 [[ -z $EPNPIPELINES ]] && export EPNPIPELINES=1 + [[ -z $O2_GPU_DOUBLE_PIPELINE ]] && export O2_GPU_DOUBLE_PIPELINE=1 [[ -z $DPL_CONDITION_BACKEND ]] && export DPL_CONDITION_BACKEND="http://localhost:8084" export ALL_EXTRA_CONFIG="$ALL_EXTRA_CONFIG;NameConf.mCCDBServer=${DPL_CONDITION_BACKEND};" export GEN_TOPO_QC_OVERRIDE_CCDB_SERVER="${DPL_CONDITION_BACKEND}" @@ -52,6 +53,7 @@ export SYNCMODE=1 export SHMTHROW=0 export IS_SIMULATED_DATA=1 export DATADIST_NEW_DPL_CHAN=1 +export RANS_OPT="--ans-version 1.0 --ctf-dict none" # Use new RANS coding scheme without dictionary [[ -z $GEN_TOPO_MYDIR ]] && GEN_TOPO_MYDIR="$(dirname $(realpath $0))" source $GEN_TOPO_MYDIR/setenv.sh || { echo "setenv.sh failed" 1>&2 && exit 1; } @@ -108,6 +110,7 @@ else FST_SLEEP1=0 FST_SLEEP2=30 fi +[[ ! -z $FST_TMUX_DD_WAIT ]] && FST_SLEEP2=$FST_TMUX_DD_WAIT if [[ ! -z $FST_TMUX_SINGLENUMA ]]; then eval "FST_SLEEP$((FST_TMUX_SINGLENUMA ^ 1))=\"0; echo SKIPPED; sleep 1000; exit\"" diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt index 07f23328230dd..f9b0f5e220ea6 100644 --- a/run/CMakeLists.txt +++ b/run/CMakeLists.txt @@ -271,7 +271,7 @@ o2_add_test_command(NAME o2sim_hepmc -g hepmc --configKeyValues -"HepMC.fileName=${CMAKE_SOURCE_DIR}/Generators/share/data/pythia.hepmc;HepMC.version=2;align-geom.mDetectors=none" +"GeneratorFileOrCmd.fileNames=${CMAKE_SOURCE_DIR}/Generators/share/data/pythia.hepmc;HepMC.version=2;align-geom.mDetectors=none" -o o2simhepmc LABELS long sim hepmc3 diff --git a/run/O2SimDeviceRunner.cxx b/run/O2SimDeviceRunner.cxx index 512f6059f9569..524e609883926 100644 --- a/run/O2SimDeviceRunner.cxx +++ b/run/O2SimDeviceRunner.cxx @@ -12,6 +12,7 @@ /// @author Sandro Wenzel #include "O2SimDevice.h" +#include "SimSetup/SimSetup.h" #include #include #include @@ -66,7 +67,7 @@ void sigaction_handler(int signal, siginfo_t* signal_info, void*) break; } } - + o2::SimSetup::shutdown(); _exit(0); } @@ -177,6 +178,7 @@ int runSim(KernelSetup setup) while (setup.sim->Kernel(setup.workerID, *setup.primchannel, *setup.datachannel, setup.primstatuschannel)) { } doLogInfo(setup.workerID, "simulation is done"); + o2::SimSetup::shutdown(); return 0; } diff --git a/run/SimExamples/HepMC/README.md b/run/SimExamples/HepMC/README.md new file mode 100644 index 0000000000000..0369ee9d90bdd --- /dev/null +++ b/run/SimExamples/HepMC/README.md @@ -0,0 +1,169 @@ + + +Here are pointers on how to use `GeneratorHepMC` selected by the +option `-g hepmc` for `o2-sim`. + +HepMC event structures can be read from any file format supported by +HepMC it self (see +[here](http://hepmc.web.cern.ch/hepmc/group__IO.html) and +[here](http://hepmc.web.cern.ch/hepmc/group__factory.html). + +## Reading HepMC files + +The generator `GeneratorHepMC` can read events from a +[HepMC(3)](http://hepmc.web.cern.ch/hepmc/) formatted file. These +files can be produced by a standalone event generator program (EG). +Examples of such programs are + +- [Pythia8](https://pythia.org) +- The [CRMC](https://gitlab.iap.kit.edu/AirShowerPhysics/crmc) suite +- [Herwig](https://herwig.hepforge.org/) +- [SMASH](https://smash-transport.github.io/) +- ... and many others + +Please refer to the documentation of these for more on how to make +event files in the HepMC format. + +To make a simulation reading from the file `events.hepmc`, do + + o2-sim -g hepmc --configKeyValues "GeneratorFileOrCmd.fileNames=events.hepmc" ... + +See also [`read.sh`](read.sh). + +## Reading HepMC events from child process + +`GeneratorHepMC` can not only read HepMC events from a file, but can +also spawn an child EG to produce events. Suppose we have a program +named `eg` which is some EG that writes HepMC event records to the +standard output. Then we can execute a simulation using this external +EG by + + o2-sim -g hepmc --configKeyValues "GeneratorFileOrCmd.cmd=eg" + +See also [`child.sh`](child.sh). + +There are some requirements on the program `eg`: + +- The EG program _must_ be able to write the HepMC event structures to + a specified file. The option passed to the program is specified via + the key `GeneratorFileOrCmd.outputSwitch`. This defaults to `>` + which means the EG program is assumed to write the HepMC event + structures to standard output, _and_ that nothing else is printed on + standard output. +- It _must_ accept an option to set the number of events to generate. + This is controlled by the configuration key + `GeneratorFileOrCmd.nEventsSwitch` and defaults to `-n`. Thus, the + EG application should accept `-n 10` to mean that it should generate + `10` events, for example. +- The EG application should accept a command line switch to set the + random number generator seed. This option is specified via the + configuration key `GeneratorFileOrCmd.seedSwitch` and defaults to + `-s`. Thus, the EG application must accept `-s 123456` to mean to + set the random number seed to `123456` for example. +- The EG application should accept a command line switch to set the + maximum impact parameter (in Fermi-metre) sampled. This is set via + the configuration key `GeneratorFileOrCmd.bMaxSwithc` and defaults + to `-b`. Thus, the EG application should take the command line + argument `-b 10` to mean that it should only generate events with an + impact parameter between 0fm and 10fm. + +If a program does not adhere to these requirements, it will often be +simple enough to make a small wrapper script that enforce this. For +example, `crmc` will write a lot of information to standard output. +We can filter that out via a shell script ([`crmc.sh`](crmc.sh)) like + + #!/bin/sh + crmc $@ -o hepmc3 -f /dev/stdout | sed -n 's/^\(HepMC::\|[EAUWVP] \)/\1/p' + +The `sed` command selects lines that begin with `HepMC::`, or one of +single characters `E` (event), `A` (attribute), `U` (units), `W` +(weight), `V` (vertex), or `P` (particle) followed by a space. This +should in most cases be enough to filter out extra stuff written on +standard output. + +The script above also passes any additional command line options on to +`crmc` via `$@`. We can utilise this with `o2-sim` to set options to +the CRMC suite. For example, if we want to simulate p-Pb collisions +using DpmJET, we can do + + o2-sim -g hepmc --configKeyValues "GeneratorFileOrCmd.cmd=crmc.sh -m 12 -i2212 -I 1002080820" + + +### Implementation details + +Internally `GeneratorHepMC` + +1. creates a unique temporary file name in the working directory, +2. then creates a FIFO (or named pipe, see + [Wikipedia](https://en.wikipedia.org/wiki/Named_pipe)), +3. builds a command line, e.g., + + eg options > fifo-name & + +4. and executes that command line + +## The configuration keys + +The `GeneratorHepMC` (and sister generator `GeneratorTParticle`) +allows customisation of the execution via configuration keys passed +via `--configKeyValues` + +- `HepMC.eventsToSkip=number` a number events to skip at the beginning + of each file read. + +- `HepMC.version` - when reading the events from files, this option is + no longer needed. The code itself figures out which format version + the input file is in. If executing a child process through + `GeneratorFileOrCmd.cmd` and the EG writes out HepMC2 format, then + this _must_ be set to `2`. Otherwise, HepMC3 is assumed. + +- `GeneratorFileOrCmd.fileNames=list` a comma separated list of HepMC + files to read. + +- `GeneratorFileOrCmd.cmd=command line` a command line to execute as a + background child process. If this is set (not the empty string), + then `GeneratorFileOrCmd.fileNames` is ignored. + +- A number of keys that specifies the command line option switch that + the child program accepts for certain things. If any of these are + set to the empty string, then that switch and corresponding option + value is not passed to the child program. + + - `GeneratorFileOrCmd.outputSwitch=switch` (default `>`) to specify + output file. The default of `>` assumes that the program write + HepMC events, and _only_ those, to standard output. + + - `GeneratorFileOrCmd.seedSwitch=switch` (default `-s`) to specify + the random number generator seed. The value passed is selected by + the `o2-sim` option `--seed` + + - `GeneratorFileOrCmd.bMaxSwitch=switch` (default `-b`) to specify + the upper limit on the impact parameters sampled. The value + passed is selected by the `o2-sim` option `--bMax` + + - `GeneratorFileOrCmd.nEventsSwitch=switch` (default `-n`) to + specify the number of events to generate. The value passed is + selected by the `o2-sim` option `--nEvents` or (`-n`) + + - `GeneratorFileOrCmd.backgroundSwitch=switch` (default `&`) to + specify how the program is put in the background. Typically this + should be `&`, but a program may itself fork to the background. + +- Some options are deprecated + + - `HepMC.fileName` - use `GeneratorFileOrCmd.fileNames` + +The command line build will now be + +> _commandLine_ _nEventsSwitch_ _nEvents_ _seedSwitch_ _seed_ +> _bMaxSwitch_ _bMax_ _outputSwitch_ _output_ _backgroundSwitch_ + +If any of the `Switch` keys are empty, then the corresponding option +is not propagated to the command line. For example, if _bMaxSwitch_ +is empty, then the build command line will be + +> _commandLine_ _nEventsSwitch_ _nEvents_ _seedSwitch_ _seed_ +> _outputSwitch_ _output_ _backgroundSwitch_ + diff --git a/run/SimExamples/HepMC/child.sh b/run/SimExamples/HepMC/child.sh new file mode 100755 index 0000000000000..89b563ec8d550 --- /dev/null +++ b/run/SimExamples/HepMC/child.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +cmd="./crmc.sh" +more="GeneratorFileOrCmd.bMaxSwitch=none" +seed=$RANDOM +nev=1 +out= + +usage() +{ + cat </dev/stderr + exit 1 + ;; + esac + shift +done + +if test "x$out" = "x" ; then + out=`echo $cmd | sed 's,^\./,,' | tr '[$/. ]' '_'` +fi +out=`echo "$out" | tr ' ' '_'` + +export VMCWORKDIR=${O2_ROOT}/share +o2-sim -g hepmc --configKeyValues "GeneratorFileOrCmd.cmd=$cmd;${more}" \ + --outPrefix "$out" --seed $seed --nEvents $nev $@ diff --git a/run/SimExamples/HepMC/crmc.sh b/run/SimExamples/HepMC/crmc.sh new file mode 100755 index 0000000000000..1c2d2f7827dbc --- /dev/null +++ b/run/SimExamples/HepMC/crmc.sh @@ -0,0 +1,7 @@ +#!/bin/sh +# This script _may not_ write to standard out, except for the HepMC +# event record. + +crmcParam=$(dirname $(dirname `which crmc`))/etc/crmc.param +exec crmc -c $crmcParam $@ -o hepmc -f /dev/stdout | \ + sed -n 's/^\(HepMC::\|[EAUWVP] \)/\1/p' diff --git a/run/SimExamples/HepMC/read.sh b/run/SimExamples/HepMC/read.sh new file mode 100755 index 0000000000000..e64f5561a775c --- /dev/null +++ b/run/SimExamples/HepMC/read.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +inp=events.hepmc +seed=$RANDOM +nev=1 +out= + +usage() +{ + cat </dev/stderr + exit 1 + ;; + esac + shift +done + +if test "x$out" = "x" ; then + out=`basename $inp .hepmc` +fi +out=`echo "$out" | tr ' ' '_'` + +export VMCWORKDIR=${O2_ROOT}/share +o2-sim -g hepmc --configKeyValues "GeneratorFileOrCmd.fileNames=$inp" \ + --outPrefix "$out" --seed $seed --nEvents $nev $@ diff --git a/run/SimExamples/HepMC_STARlight/run.sh b/run/SimExamples/HepMC_STARlight/run.sh index b75cee52b7117..3eb5b91f24a6d 100755 --- a/run/SimExamples/HepMC_STARlight/run.sh +++ b/run/SimExamples/HepMC_STARlight/run.sh @@ -10,9 +10,9 @@ set -x # PART a) env -i HOME="$HOME" USER="$USER" PATH="/bin:/usr/bin:/usr/local/bin" \ - ALIBUILD_WORK_DIR="$ALIBUILD_WORK_DIR" ./run-starlight.sh + ALIBUILD_WORK_DIR="$ALIBUILD_WORK_DIR" ./run-starlight.sh # PART b) NEV=1000 o2-sim -j 20 -n ${NEV} -g hepmc -m PIPE ITS -o sim \ - --configKeyValues "HepMC.fileName=starlight.hepmc;HepMC.version=2;Diamond.position[2]=0.1;Diamond.width[2]=0.05" + --configKeyValues "GeneratorFileOrCmd.fileNames=starlight.hepmc;Diamond.position[2]=0.1;Diamond.width[2]=0.05" diff --git a/run/SimExamples/Inspect_Hits/left_trace.macro b/run/SimExamples/Inspect_Hits/left_trace.macro index 3c3adb6bd033f..88bc4e592edda 100644 --- a/run/SimExamples/Inspect_Hits/left_trace.macro +++ b/run/SimExamples/Inspect_Hits/left_trace.macro @@ -1,54 +1,54 @@ bool -leftTrace_golden(o2::MCTrack& t) +leftTrace_golden(o2::MCTrack& t, std::vector const& bitLUT) { bool trace = false; - trace |= t.leftTrace(o2::detectors::DetID::ITS); - trace |= t.leftTrace(o2::detectors::DetID::TPC); - trace |= t.leftTrace(o2::detectors::DetID::TRD); - trace |= t.leftTrace(o2::detectors::DetID::TOF); + trace |= t.leftTrace(o2::detectors::DetID::ITS, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::TPC, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::TRD, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::TOF, bitLUT); return trace; } bool -leftTrace_barrel(o2::MCTrack& t) +leftTrace_barrel(o2::MCTrack& t, std::vector const& bitLUT) { bool trace = false; - trace |= t.leftTrace(o2::detectors::DetID::ITS); - trace |= t.leftTrace(o2::detectors::DetID::TPC); - trace |= t.leftTrace(o2::detectors::DetID::TRD); - trace |= t.leftTrace(o2::detectors::DetID::TOF); - trace |= t.leftTrace(o2::detectors::DetID::HMP); - trace |= t.leftTrace(o2::detectors::DetID::EMC); - trace |= t.leftTrace(o2::detectors::DetID::PHS); - trace |= t.leftTrace(o2::detectors::DetID::FV0); - trace |= t.leftTrace(o2::detectors::DetID::FT0); - return trace; + trace |= t.leftTrace(o2::detectors::DetID::ITS, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::TPC, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::TRD, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::TOF, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::HMP, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::EMC, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::PHS, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::FV0, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::FT0, bitLUT); + return trace; } bool -leftTrace_muon(o2::MCTrack& t) +leftTrace_muon(o2::MCTrack& t, std::vector const& bitLUT) { bool trace = false; - trace |= t.leftTrace(o2::detectors::DetID::ITS); - trace |= t.leftTrace(o2::detectors::DetID::FV0); - trace |= t.leftTrace(o2::detectors::DetID::FT0); - trace |= t.leftTrace(o2::detectors::DetID::MFT); - trace |= t.leftTrace(o2::detectors::DetID::MCH); - trace |= t.leftTrace(o2::detectors::DetID::MID); - return trace; + trace |= t.leftTrace(o2::detectors::DetID::ITS, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::FV0, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::FT0, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::MFT, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::MCH, bitLUT); + trace |= t.leftTrace(o2::detectors::DetID::MID, bitLUT); + return trace; } bool -leftTrace_any(o2::MCTrack& t) +leftTrace_any(o2::MCTrack& t, std::vector const& bitLUT) { - return leftTrace_barrel(t) || leftTrace_muon(t); + return leftTrace_barrel(t, bitLUT) || leftTrace_muon(t, bitLUT); } -std::function leftTrace_selected = leftTrace_barrel; +std::function const&)> leftTrace_selected = leftTrace_barrel; /* -std::map leftTrace = { +std::map const&)> leftTrace = { {"golden", leftTrace_golden}, {"barrel", leftTrace_barrel}, {"muon" , leftTrace_muon} , @@ -57,8 +57,8 @@ std::map leftTrace = { */ bool -leftTrace(o2::MCTrack& t, vector* tracks) { - if (leftTrace_selected(t)) return true; // this track has left trace in requested detectors +leftTrace(o2::MCTrack& t, vector* tracks, std::vector const& bitLUT) { + if (leftTrace_selected(t, bitLUT)) return true; // this track has left trace in requested detectors // if (leftTrace_barrel(t)) return true; // this track has left trace in requested detectors // check if any of the daughters left trace auto id1 = t.getFirstDaughterTrackId(); @@ -67,7 +67,7 @@ leftTrace(o2::MCTrack& t, vector* tracks) { bool trace = false; for (int id = id1; id <= id2; ++id) { auto d = tracks->at(id); - trace |= leftTrace(d, tracks); // at least one daughter has left trace + trace |= leftTrace(d, tracks, bitLUT); // at least one daughter has left trace } return trace; } diff --git a/run/SimExamples/Inspect_Hits/primary_and_hits.macro b/run/SimExamples/Inspect_Hits/primary_and_hits.macro index dd8ce743d58c0..94f941b31bfd1 100644 --- a/run/SimExamples/Inspect_Hits/primary_and_hits.macro +++ b/run/SimExamples/Inspect_Hits/primary_and_hits.macro @@ -15,6 +15,10 @@ primary_and_hits(const char *fname, std::string where = "barrel") tin->SetBranchAddress("MCTrack", &tracks); auto nev = tin->GetEntries(); + // get the LUT for detector ID to bit index for hits + o2::dataformats::MCEventHeader *m = nullptr; + tin->SetBranchAddress("MCEventHeader.", &m); + std::map hEtaPtGen, hEtaPtHit; for (int iev = 0; iev < nev; ++iev) { @@ -31,7 +35,7 @@ primary_and_hits(const char *fname, std::string where = "barrel") hEtaPtGen[pdg]->Fill(t.GetEta(), t.GetPt()); - if (!leftTrace(t, tracks)) continue; + if (!leftTrace(t, tracks, m->getDetId2HitBitLUT())) continue; hEtaPtHit[pdg]->Fill(t.GetEta(), t.GetPt()); diff --git a/run/SimExamples/Inspect_Hits/secondary_and_hits.macro b/run/SimExamples/Inspect_Hits/secondary_and_hits.macro index 9a21924e62b2d..8e34faaecb21a 100644 --- a/run/SimExamples/Inspect_Hits/secondary_and_hits.macro +++ b/run/SimExamples/Inspect_Hits/secondary_and_hits.macro @@ -15,6 +15,10 @@ secondary_and_hits(const char *fname, std::string where = "barrel") tin->SetBranchAddress("MCTrack", &tracks); auto nev = tin->GetEntries(); + // get the LUT for detector ID to bit index for hits + o2::dataformats::MCEventHeader *m = nullptr; + tin->SetBranchAddress("MCEventHeader.", &m); + std::map hZRGen, hZRHit; for (int iev = 0; iev < nev; ++iev) { @@ -32,7 +36,9 @@ secondary_and_hits(const char *fname, std::string where = "barrel") auto R = std::hypot(t.GetStartVertexCoordinatesX(), t.GetStartVertexCoordinatesY()); hZRGen[pdg]->Fill(z, R); - if (!leftTrace(t, tracks)) continue; + if (!leftTrace(t, tracks, m->getDetId2HitBitLUT())) { + continue; + } hZRHit[pdg]->Fill(z, R); diff --git a/run/SimExamples/McTracksToAOD/mctracks_to_aod_simple_task.cxx b/run/SimExamples/McTracksToAOD/mctracks_to_aod_simple_task.cxx index 8c897712d50bf..e670b25befb40 100644 --- a/run/SimExamples/McTracksToAOD/mctracks_to_aod_simple_task.cxx +++ b/run/SimExamples/McTracksToAOD/mctracks_to_aod_simple_task.cxx @@ -16,6 +16,7 @@ using namespace o2; using namespace o2::framework; +using namespace o2::constants::math; struct AodConsumerTestTask { diff --git a/run/SimExamples/README.md b/run/SimExamples/README.md index 2613f6bb2773c..ae2d15d47316c 100644 --- a/run/SimExamples/README.md +++ b/run/SimExamples/README.md @@ -11,6 +11,7 @@ * \subpage refrunSimExamplesAdaptive_Pythia8 * \subpage refrunSimExamplesAliRoot_Hijing * \subpage refrunSimExamplesAliRoot_AMPT +* \subpage refrunSimExamplesHepMC * \subpage refrunSimExamplesHepMC_STARlight * \subpage refrunSimExamplesJet_Embedding_Pythia8 * \subpage refrunSimExamplesStepMonitoringSimple1 @@ -20,4 +21,5 @@ * \subpage refrunSimExamplesSelective_Transport_pi0 * \subpage refrunSimExamplesCustom_EventInfo * \subpage refrunSimExamplesMCTrackToDPL +* \subpage refrunSimExamplesTParticle /doxy --> diff --git a/run/SimExamples/TParticle/MyEG.macro b/run/SimExamples/TParticle/MyEG.macro new file mode 100644 index 0000000000000..a067b4c1051b7 --- /dev/null +++ b/run/SimExamples/TParticle/MyEG.macro @@ -0,0 +1,128 @@ +#include +#include +#include +#include +#include +#include + +//-------------------------------------------------------------------- +// Our generator class. Really simple. +class MyGenerator : public TGenerator +{ + public: + Long_t projectilePDG; + Long_t targetPDG; + Double_t sqrts; + MyGenerator() {} + void Initialize(Long_t projectile, + Long_t target, + Double_t sqrts) + { + this->projectilePDG = projectile; + this->targetPDG = target; + this->sqrts = sqrts; + } + void GenerateEvent() + { /* Do something */ + } + TObjArray* ImportParticles(Option_t* option = "") { return 0; } + Int_t ImportParticles(TClonesArray* particles, Option_t* option = "") + { + Int_t nParticles = 10; + Int_t iParticle = 0; + // Make beam particles + new ((*particles)[iParticle++]) TParticle(projectilePDG, 4, -1, -1, + 2, nParticles - 1, + 0, 0, sqrts / 2, + TMath::Sqrt(1 + sqrts * sqrts), + 0, 0, 0, 0); + new ((*particles)[iParticle++]) TParticle(projectilePDG, 4, -1, -1, + 2, nParticles - 1, + 0, 0, -sqrts / 2, + TMath::Sqrt(1 + sqrts * sqrts), + 0, 0, 0, 0); + for (; iParticle < nParticles; iParticle++) + new ((*particles)[iParticle]) + TParticle(211, 1, 0, 1, + -1, -1, + 0.1 * iParticle, + 0.1 * iParticle, + 0.1 * iParticle, + TMath::Sqrt(0.03 * iParticle * iParticle + 0.14 * 0.14), + 0, 0, 0, 0); + + return nParticles; + } +}; +//-------------------------------------------------------------------- +// Our steering class +struct MySteer { + TGenerator* generator; + TFile* file; + TTree* tree; + TClonesArray* particles; + Int_t every; + MySteer(TGenerator* generator, const TString& output, Int_t every) + : generator(generator), + file(TFile::Open(output, "RECREATE")), + tree(new TTree("T", "T")), + particles(new TClonesArray("TParticle")), + every(every) + { + tree->SetDirectory(file); + tree->Branch("Particles", &particles); + } + ~MySteer() + { + close(); + } + void event() + { + particles->Clear(); + generator->GenerateEvent(); + generator->ImportParticles(particles); + tree->Fill(); + } + void sync() + { + // Important so that GeneratorTParticle picks up the events as + // they come. + tree->AutoSave("SaveSelf FlushBaskets Overwrite"); + } + void run(Int_t nev) + { + for (Int_t iev = 0; iev < nev; iev++) { + event(); + + if (every > 0 and (iev % every == 0) and iev != 0) + sync(); + } + } + void close() + { + if (not file) + return; + file->Write(); + file->Close(); + file = nullptr; + } +}; + +//-------------------------------------------------------------------- +// Our steering function +void MyEG(Int_t nev, const TString& out, Int_t seed, Int_t every = 1) +{ + gRandom->SetSeed(seed); + + MyGenerator* eg = new MyGenerator(); + eg->Initialize(2212, 2212, 5200); + + MySteer steer(eg, out, every); + steer.run(nev); +} +// Local Variables: +// mode: C++ +// End: +// +// EOF +// diff --git a/run/SimExamples/TParticle/README.md b/run/SimExamples/TParticle/README.md new file mode 100644 index 0000000000000..c9df40665d829 --- /dev/null +++ b/run/SimExamples/TParticle/README.md @@ -0,0 +1,265 @@ + + +Here are pointers on how to use `GeneratorTParticle` selected by the +option `-g tparticle` for `o2-sim`. + +## Reading TParticle files + +The generator `GeneratorTParticle` can read events from a ROOT file +containing a `TTree` with a branch holding a `TClonesArray` of +`TParticle` objects. These files can be produced by a standalone event +generator program (EG). + +To make a simulation reading from the file `particles.root`, do + + o2-sim -g tparticle --configKeyValues "GeneratorFileOrCmd.fileNames=particles.root" ... + +See also [`read.sh`](read.sh). Do + + ./read.sh --help + +for a list of options. This expects an input file with a `TTree` with +a single `TBranch` holding a `TClonesArray` of `TParticle` objects. +One such example file can be made with [`myeg.sh`]. Do + + ./myeg.sh --help + +for a list of options. + +### Configurations + +- The name of the `TTree` to read can be set by the configuration key + `GeneratorTParticle.treeName`. The default is `T` +- The name of the `TBranch` holding the `TClonesArray` of `TParticle` + objects can be set by the configuration key + `GeneratorTParticle.branchName`. The default is `Particles` + +For example + + o2-sim -g tparticle --configKeyValues "GeneratorFileOrCmd.fileNames=particles.root;GeneratorTParticle.treeName=Events;GeneratorTParticle.branchName=Tracks" ... + +## Reading TParticle events from child process + +`GeneratorTParticle` can not only read events from a file, but can +also spawn an child EG to produce events. Suppose we have a program +named `eg` which is some EG that writes `TParticle` event records to a +file . Then we can execute a simulation using this external EG by + + o2-sim -g tgenerator --configKeyValues "GeneratorFileOrCmd.cmd=eg" + +See also [`child.sh`](child.sh). Do + + ./child.sh --help + +for a list of options. + +There are some requirements on the program `eg`: + +- The EG program _must_ be able to write the HepMC event structures to + a specified file. The option passed to the program is specified via + the key `GeneratorFileOrCmd.outputSwitch`. This defaults to `-o`. +- It _must_ accept an option to set the number of events to generate. + This is controlled by the configuration key + `GeneratorFileOrCmd.nEventsSwitch` and defaults to `-n`. Thus, the + EG application should accept `-n 10` to mean that it should generate + `10` events, for example. +- The EG application should accept a command line switch to set the + random number generator seed. This option is specified via the + configuration key `GeneratorFileOrCmd.seedSwitch` and defaults to + `-s`. Thus, the EG application must accept `-s 123456` to mean to + set the random number seed to `123456` for example. +- The EG application should accept a command line switch to set the + maximum impact parameter (in Fermi-metre) sampled. This is set via + the configuration key `GeneratorFileOrCmd.bMaxSwithc` and defaults + to `-b`. Thus, the EG application should take the command line + argument `-b 10` to mean that it should only generate events with an + impact parameter between 0fm and 10fm. + +If a program does not adhere to these requirements, it will often be +simple enough to make a small wrapper script that enforce this. + +### Configurations + +Same as above. + +### Example EG + +The child-process feature allows us to use almost any EG for which we +have a `TGenerator` interface without compiling it in to O2. Suppose +we have defined the class `MyGenerator` to produce events. + + class MyGenerator : public TGenerator { + public: + MyGenerator(); + void Initialize(Long_t projectile, + Long_t target, + Double_t sqrts); + void GenerateEvent(); + Int_t ImportParticles(TClonesArray* particles,Option_t*option=""); + }; + +and a steering class + + struct MySteer { + TGenerator* generator; + TFile* file; + TTree* tree; + TClonesArray* particle; + Int_t flushEvery; + MySteer(TGenerator* generator, + const TString& output, + Int_t flushEvery) + : generator(generator) + file(TFile::Open(output,"RECREATE")), + tree("T","Particle tree"), + particles(new TClonesArray("TParticle")), + flushEvery(flushEvery) + { + tree->SetDirectory(file); + tree->Branch("Particles",&particles); + } + ~MySteer() { close(); } + void event() { + particles->Clear(); + generator->GenerateEvent(); + generator->ImportParticles(particles); + tree->Fill(); + } + void sync() { + tree->AutoSave("SaveSelf FlushBaskets Overwrite"); + } + void run(Int_t nev) { + for (Int_t iev = 0; iev < nev; iev++) { + event(); + if (flushEvery > 0 and (iev % flushEvery == 0) and iev != 0) + sync(); + } + } + void close() { + if (not file) return; + file->Write(); + file->Close(); + file = nullptr; + } + }; + +Then we could make the script [`MyEG.macro` (complete code)](MyEG.macro) like + + void MyEG(Int_t nev,const TString& out,Int_t every=1) + { + MyGenerator* eg = new MyGenerator(); + eg->Initialize(2212, 2212, 5200); + + MySteer steer(eg, out, every); + steer.run(nev); + } + +and a simple shell-script [`myeg.sh`](myeg.sh) to pass arguments to +the `MyEG.macro` script + + #!/bin/sh + + nev=1 + out=particles.root + + while test $# -gt 0 ; do + case $1 in + -n) nev=$2 ; shift ;; + -o) out=$2 ; shift ;; + *) ;; + esac + shift + done + + root -l MyEG.macro -- $nev \"$out\" + +We can then do + + o2-sim -g tgenerator --configKeyValues "GeneratorFileOrCmd.cmd=./myeg.sh" + +to produce events with our generator `MyGenerator`. + + +### Implementation details + +Internally `GeneratorTParticle` + +1. creates a unique temporary file name in the working directory, +2. builds a command line, e.g., + + eg options -o temporary-name & + +3. and executes that command line + +## The future + +The `GeneratorTParticle` (and sister generator `GeneratorHepMC`) is +configured through configuration keys set via `--configKeyValues` + +- `GeneratorTParticle.treeName=name` the name of the `TTree` in the + input files. + +- `GeneratorTParticle.branchName=name` the name of the `TBranch` in + the `TTree` that holds the `TClonesArray` of `TParticle` objects. + +- `GeneratorFileOrCmd.fileNames=list` a comma separated list of HepMC + files to read + +- `GeneratorFileOrCmd.cmd=command line` a command line to execute as a + background child process. If this is set (not the empty string), + then `GeneratorFileOrCmd.fileNames` is ignored. + +- A number of keys that specifies the command line option switch that + the child program accepts for certain things. If any of these are + set to the empty string or special value `none`, then that switch + and corresponding option value is not passed to the child program. + + - `GeneratorFileOrCmd.outputSwitch=switch` (default `>`) to specify + output file. The default of `>` assumes that the program write + events, and _only_ those, to standard output. + + - `GeneratorFileOrCmd.seedSwitch=switch` (default `-s`) to specify + the random number generator seed. The value passed is selected by + the `o2-sim` option `--seed`. + + - `GeneratorFileOrCmd.bMaxSwitch=switch` (default `-b`) to specify + the upper limit on the impact parameters sampled. The value + passed is selected by the `o2-sim` option `--bMax`. + + - `GeneratorFileOrCmd.nEventsSwitch=switch` (default `-n`) to + specify the number of events to generate. The value passed is + selected by the `o2-sim` option `--nEvents` or (`-n`). + + - `GeneratorFileOrCmd.backgroundSwitch=switch` (default `&`) to + specify how the program is put in the background. Typically this + should be `&`, but a program may itself fork to the background. + +The command line build will now be + +> _commandLine_ _nEventsSwitch_ _nEvents_ _seedSwitch_ _seed_ +> _bMaxSwitch_ _bMax_ _outputSwitch_ _output_ _backgroundSwitch_ + +If any of the `Switch` keys are empty or set to `none`, then the +corresponding option is not propagated to the command line. For +example, if _bMaxSwitch_ is empty, then the build command line will be + +> _commandLine_ _nEventsSwitch_ _nEvents_ _seedSwitch_ _seed_ +> _outputSwitch_ _output_ _backgroundSwitch_ + + +## TODO + +### Header information + +The class `GeneratorTParticle` will take a key parameter, say +`headerName` which will indicate a branch that contains header +information. Under that branch, the class will then search for leaves +(`TLeaf`) that correspond to standard header information keys (see +`o2::dataformats::MCInfoKeys`). If any of those leaves are present, +then the corresponding keys will be set on the generated event header. + +Thus, as long as the generator observes the convention used, we can +also import auxiliary information (impact parameter, Npart, ...) from +the input files in addition to the particle information. diff --git a/run/SimExamples/TParticle/child.sh b/run/SimExamples/TParticle/child.sh new file mode 100755 index 0000000000000..ae188024808d0 --- /dev/null +++ b/run/SimExamples/TParticle/child.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash + +cmd="./myeg.sh" +seed=$RANDOM +nev=1 +out= +opt= + +usage() +{ + cat </dev/stderr + exit 1 + ;; + esac + shift +done + +if test "x$out" = "x" ; then + out=`echo $cmd | sed 's,^\./,,' | tr '[$/. ]' '_'` +fi +out=`echo "$out" | tr ' ' '_'` + +set -x +export VMCWORKDIR=${O2_ROOT}/share +o2-sim -g tparticle \ + --configKeyValues "GeneratorFileOrCmd.cmd=$cmd $opt;GeneratorFileOrCmd.outputSwitch=-o" \ + --outPrefix "$out" --seed $seed --nEvents $nev $@ + diff --git a/run/SimExamples/TParticle/myeg.sh b/run/SimExamples/TParticle/myeg.sh new file mode 100755 index 0000000000000..4e76c8a8a02d8 --- /dev/null +++ b/run/SimExamples/TParticle/myeg.sh @@ -0,0 +1,37 @@ +#!/bin/sh + + +nev=1 +out=particles.root +seed=0 +opt= + +usage() +{ + cat </dev/stderr + exit 1 + ;; + esac + shift +done + +if test "x$out" = "x" ; then + out=`basename $inp .root` +fi +out=`echo "$out" | tr ' ' '_'` + +set -e +export VMCWORKDIR=${O2_ROOT}/share +o2-sim -g tparticle --configKeyValues "GeneratorFileOrCmd.fileNames=${inp}" \ + --outPrefix "$out" --seed $seed --nEvents $nev $@ + diff --git a/run/checkStack.cxx b/run/checkStack.cxx index 4c7aa3516c402..4470ea463fd98 100644 --- a/run/checkStack.cxx +++ b/run/checkStack.cxx @@ -89,6 +89,9 @@ int main(int argc, char** argv) std::vector trackidsinTPC; std::vector trackidsinITS; + // fetch the encoding of DetIDs to bits for the hit properties (can be fetched from any MCEventHeader) + auto& mcEventHeader = mcreader.getMCEventHeader(0, 0); + int primaries = 0; int physicalprimaries = 0; int secondaries = 0; @@ -103,12 +106,14 @@ int main(int argc, char** argv) // for primaries, this may be different (for instance with Pythia8) assert(ti > t.getMotherTrackId()); } - if (t.leftTrace(o2::detectors::DetID::TPC)) { + + if (t.leftTrace(o2::detectors::DetID::TPC, mcEventHeader.getDetId2HitBitLUT())) { trackidsinTPC.emplace_back(ti); } - if (t.leftTrace(o2::detectors::DetID::ITS)) { + if (t.leftTrace(o2::detectors::DetID::ITS, mcEventHeader.getDetId2HitBitLUT())) { trackidsinITS.emplace_back(ti); } + bool physicalPrim = o2::mcutils::MCTrackNavigator::isPhysicalPrimary(t, *mctracks); LOG(debug) << " track " << ti << "\t" << t.getMotherTrackId() << " hits " << t.hasHits() << " isPhysicalPrimary " << physicalPrim; if (t.isPrimary()) { diff --git a/run/o2sim_hepmc_publisher.cxx b/run/o2sim_hepmc_publisher.cxx index 22616b09dc45a..76fbddc1993f3 100644 --- a/run/o2sim_hepmc_publisher.cxx +++ b/run/o2sim_hepmc_publisher.cxx @@ -68,6 +68,55 @@ struct O2simHepmcPublisher { o2::dataformats::MCEventHeader mcHeader; mcHeader.SetEventID(event.event_number()); mcHeader.SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); + auto xsecInfo = event.cross_section(); + if (xsecInfo != nullptr) { + mcHeader.putInfo("Accepted", (uint64_t)xsecInfo->get_accepted_events()); + mcHeader.putInfo("Attempted", (uint64_t)xsecInfo->get_attempted_events()); + mcHeader.putInfo("XsectGen", (float)xsecInfo->xsec()); + mcHeader.putInfo("XsectErr", (float)xsecInfo->xsec_err()); + } + auto scale = event.attribute("event_scale"); + if (scale != nullptr) { + mcHeader.putInfo("PtHard", (float)scale->value()); + } + auto nMPI = event.attribute("mpi"); + if (nMPI != nullptr) { + mcHeader.putInfo("MPI", nMPI->value()); + } + auto sid = event.attribute("signal_process_id"); + if (sid != nullptr) { + mcHeader.putInfo("ProcessId", sid->value()); + } + auto pdfInfo = event.pdf_info(); + if (pdfInfo != nullptr) { + mcHeader.putInfo("Id1", pdfInfo->parton_id[0]); + mcHeader.putInfo("Id2", pdfInfo->parton_id[1]); + mcHeader.putInfo("PdfId1", pdfInfo->pdf_id[0]); + mcHeader.putInfo("PdfId2", pdfInfo->pdf_id[1]); + mcHeader.putInfo("X1", (float)pdfInfo->x[0]); + mcHeader.putInfo("X2", (float)pdfInfo->x[1]); + mcHeader.putInfo("scale", (float)pdfInfo->scale); + mcHeader.putInfo("Pdf1", (float)pdfInfo->xf[0]); + mcHeader.putInfo("Pdf2", (float)pdfInfo->xf[1]); + } + auto heavyIon = event.heavy_ion(); + if (heavyIon != nullptr) { + mcHeader.putInfo("NcollHard", heavyIon->Ncoll_hard); + mcHeader.putInfo("NpartProj", heavyIon->Npart_proj); + mcHeader.putInfo("NpartTarg", heavyIon->Npart_targ); + mcHeader.putInfo("Ncoll", heavyIon->Ncoll); + mcHeader.putInfo("NNwoundedCollisions", heavyIon->N_Nwounded_collisions); + mcHeader.putInfo("NwoundedNCollisions", heavyIon->Nwounded_N_collisions); + mcHeader.putInfo("NwoundedNwoundedCollisions", heavyIon->Nwounded_Nwounded_collisions); + mcHeader.putInfo("SpectatorNeutrons", heavyIon->spectator_neutrons); + mcHeader.putInfo("SpectatorProtons", heavyIon->spectator_protons); + mcHeader.putInfo("ImpactParameter", (float)heavyIon->impact_parameter); + mcHeader.putInfo("EventPlaneAngle", (float)heavyIon->event_plane_angle); + mcHeader.putInfo("Eccentricity", (float)heavyIon->eccentricity); + mcHeader.putInfo("SigmaInelNN", (float)heavyIon->sigma_inel_NN); + mcHeader.putInfo("Centrality", (float)heavyIon->centrality); + } + auto particles = event.particles(); for (auto const& particle : particles) { auto parents = particle->parents(); diff --git a/run/o2sim_mctracks_proxy.cxx b/run/o2sim_mctracks_proxy.cxx index 8bd37a11bb44d..8b40c6a647cca 100644 --- a/run/o2sim_mctracks_proxy.cxx +++ b/run/o2sim_mctracks_proxy.cxx @@ -15,6 +15,7 @@ #include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamSpec.h" #include "Framework/ExternalFairMQDeviceProxy.h" +#include "Framework/RawDeviceService.h" #include "Framework/Task.h" #include "Framework/DataRef.h" #include "Framework/InputRecordWalker.h" @@ -92,7 +93,9 @@ InjectorFunction o2simKinematicsConverter(std::vector const& specs, auto MCTracksMessageCache = std::make_shared(); auto Nparts = std::make_shared(nPerTF); - return [timesliceId, specs, step, nevents, nPerTF, totalEventCounter, eventCounter, TFcounter, Nparts, MCHeadersMessageCache = MCHeadersMessageCache, MCTracksMessageCache = MCTracksMessageCache](TimingInfo& ti, fair::mq::Device& device, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) mutable { + return [timesliceId, specs, step, nevents, nPerTF, totalEventCounter, eventCounter, TFcounter, Nparts, MCHeadersMessageCache = MCHeadersMessageCache, MCTracksMessageCache = MCTracksMessageCache](TimingInfo& ti, ServiceRegistryRef const& services, fair::mq::Parts& parts, ChannelRetriever channelRetriever, size_t newTimesliceId, bool& stop) mutable -> bool { + auto*device = services.get().device(); + bool didSendData = false; if (nPerTF < 0) { // if no aggregation requested, forward each message with the DPL header if (*timesliceId != newTimesliceId) { @@ -105,7 +108,8 @@ InjectorFunction o2simKinematicsConverter(std::vector const& specs, DataProcessingHeader dph{newTimesliceId, 0}; // we have to move the incoming data o2::header::Stack headerStack{dh, dph}; - sendOnChannel(device, std::move(headerStack), std::move(parts.At(i)), specs[i], channelRetriever); + sendOnChannel(*device, std::move(headerStack), std::move(parts.At(i)), specs[i], channelRetriever); + didSendData |= parts.At(i)->GetSize() > 0; } *timesliceId += step; } else { @@ -127,8 +131,8 @@ InjectorFunction o2simKinematicsConverter(std::vector const& specs, DataHeader tracksDH = headerFromSpec(specs[1], tracksSize, gSerializationMethodROOT, *Nparts, *eventCounter); o2::header::Stack ths{tracksDH, tdph}; - appendForSending(device, std::move(hhs), *TFcounter, std::move(parts.At(0)), specs[0], *MCHeadersMessageCache.get(), channelRetriever); - appendForSending(device, std::move(ths), *TFcounter, std::move(parts.At(1)), specs[1], *MCTracksMessageCache.get(), channelRetriever); + appendForSending(*device, std::move(hhs), *TFcounter, std::move(parts.At(0)), specs[0], *MCHeadersMessageCache.get(), channelRetriever); + appendForSending(*device, std::move(ths), *TFcounter, std::move(parts.At(1)), specs[1], *MCTracksMessageCache.get(), channelRetriever); ++(*eventCounter); } @@ -137,8 +141,10 @@ InjectorFunction o2simKinematicsConverter(std::vector const& specs, // send the events when the timeframe is accumulated LOGP(info, ">> Events: {}; TF counter: {}", *eventCounter, *TFcounter); *eventCounter = 0; - sendOnChannel(device, *MCHeadersMessageCache.get(), channelRetriever(specs[0], *TFcounter), *TFcounter); - sendOnChannel(device, *MCTracksMessageCache.get(), channelRetriever(specs[1], *TFcounter), *TFcounter); + sendOnChannel(*device, *MCHeadersMessageCache.get(), channelRetriever(specs[0], *TFcounter), *TFcounter); + sendOnChannel(*device, *MCTracksMessageCache.get(), channelRetriever(specs[1], *TFcounter), *TFcounter); + didSendData |= MCHeadersMessageCache->Size() > 0; + didSendData |= MCTracksMessageCache->Size() > 0; ++(*TFcounter); MCHeadersMessageCache->Clear(); MCTracksMessageCache->Clear(); @@ -148,7 +154,7 @@ InjectorFunction o2simKinematicsConverter(std::vector const& specs, // I am done (I don't expect more events to convert); so tell the proxy device to shut-down stop = true; } - return; + return didSendData; }; } diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index 092572e630ad3..e5460e346c3f9 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -22,6 +22,12 @@ using namespace o2::framework; struct MctracksToAod { Produces mcollisions; Produces mcparticles; + + // optional extra info from HepMC (if found in header) + Produces hepmcxsections; + Produces hepmcpdfinfos; + Produces hepmcheavyions; + Configurable IR{"interaction-rate", 100.f, "Interaction rate to simulate"}; uint64_t timeframe = 0; @@ -57,6 +63,56 @@ struct MctracksToAod { record.timeInBCNS * 1.e-3, // ns to ms 1., // weight mcheader->GetB()); + bool valid; + if (mcheader->hasInfo("Accepted")) { + auto ptHard = mcheader->hasInfo("PtHard") ? mcheader->getInfo("PtHard", valid) : 0.f; + auto nMPI = mcheader->hasInfo("MPI") ? mcheader->getInfo("MPI", valid) : -1; + auto processId = mcheader->hasInfo("ProcessId") ? mcheader->getInfo("ProcessId", valid) : -1; + hepmcxsections( + mcollisions.lastIndex(), + 0, + mcheader->getInfo("Accepted", valid), + mcheader->getInfo("Attempted", valid), + mcheader->getInfo("XsectGen", valid), + mcheader->getInfo("XsectErr", valid), + ptHard, + nMPI, + processId); + } + if (mcheader->hasInfo("Id1")) { + hepmcpdfinfos( + mcollisions.lastIndex(), + 0, + mcheader->getInfo("Id1", valid), + mcheader->getInfo("Id2", valid), + mcheader->getInfo("PdfId1", valid), + mcheader->getInfo("PdfId2", valid), + mcheader->getInfo("X1", valid), + mcheader->getInfo("X2", valid), + mcheader->getInfo("scale", valid), + mcheader->getInfo("Pdf1", valid), + mcheader->getInfo("Pdf2", valid)); + } + if (mcheader->hasInfo("NcollHard")) { + hepmcheavyions( + mcollisions.lastIndex(), + 0, + mcheader->getInfo("NcollHard", valid), + mcheader->getInfo("NpartProj", valid), + mcheader->getInfo("NpartTarg", valid), + mcheader->getInfo("Ncoll", valid), + mcheader->getInfo("NNwoundedCollisions", valid), + mcheader->getInfo("NwoundedNCollisions", valid), + mcheader->getInfo("NwoundedNwoundedCollisions", valid), + mcheader->getInfo("SpectatorNeutrons", valid), + mcheader->getInfo("SpectatorProtons", valid), + mcheader->getInfo("ImpactParameter", valid), + mcheader->getInfo("EventPlaneAngle", valid), + mcheader->getInfo("Eccentricity", valid), + mcheader->getInfo("SigmaInelNN", valid), + mcheader->getInfo("Centrality", valid)); + } + for (auto& mctrack : mctracks) { std::vector mothers; int daughters[2];