diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index 71287c2f07d76..fd0fe7aa6d05b 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -20,6 +20,8 @@ #include "CommonUtils/NameConf.h" #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include @@ -340,6 +342,13 @@ T* CCDBManagerInstance::getForTimeStamp(std::string const& path, long timestamp, } auto end = std::chrono::system_clock::now(); mTimerMS += std::chrono::duration_cast(end - start).count(); + auto *ref = o2::framework::ServiceRegistryRef::globalDeviceRef(); + if (ref && ref->active()) { + auto& stats = ref->get(); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_HIT, o2::framework::DataProcessingStats::Op::Set, (int64_t)mQueries - mFailures - mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_MISS, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFetches}); + stats.updateStats({(int)o2::framework::ProcessingStatsId::CCDB_CACHE_FAILURE, o2::framework::DataProcessingStats::Op::Set, (int64_t)mFailures}); + } return ptr; } @@ -391,4 +400,4 @@ class BasicCCDBManager : public CCDBManagerInstance } // namespace o2::ccdb -#endif //O2_BASICCCDBMANAGER_H +#endif // O2_BASICCCDBMANAGER_H diff --git a/CCDB/src/BasicCCDBManager.cxx b/CCDB/src/BasicCCDBManager.cxx index bcf88554578c1..d55fdad960d3a 100644 --- a/CCDB/src/BasicCCDBManager.cxx +++ b/CCDB/src/BasicCCDBManager.cxx @@ -13,6 +13,8 @@ // Created by Sandro Wenzel on 2019-08-14. // #include "CCDB/BasicCCDBManager.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include diff --git a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index 1adf7a9ae7329..2641dec84aed9 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -818,6 +818,7 @@ GPUd() o2::math_utils::SMatrix { EG4Physics physicsmode = EG4Physics::kFTFP_BERT_EMV_optical; // default physics mode with which to configure G4 @@ -40,6 +47,8 @@ struct G4Params : public o2::conf::ConfigurableParamHelper { std::string configMacroFile = ""; // a user provided g4Config.in file (otherwise standard one fill be taken) std::string userPhysicsList = ""; // possibility to directly give physics list as string + EG4Nav navmode = EG4Nav::kTGeo; // geometry navigation mode (default TGeo) + std::string const& getPhysicsConfigString() const; O2ParamDef(G4Params, "G4"); diff --git a/Common/SimConfig/src/SimConfigLinkDef.h b/Common/SimConfig/src/SimConfigLinkDef.h index 9c27536be5eb8..a1315e24ffedd 100644 --- a/Common/SimConfig/src/SimConfigLinkDef.h +++ b/Common/SimConfig/src/SimConfigLinkDef.h @@ -29,6 +29,7 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::DigiParams> + ; #pragma link C++ enum o2::conf::EG4Physics; +#pragma link C++ enum o2::conf::EG4Nav; #pragma link C++ enum o2::conf::SimFieldMode; #pragma link C++ struct o2::conf::G4Params + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::conf::G4Params> + ; diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h index e9464089d71fc..ff1462084d53d 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/Configuration.h @@ -214,7 +214,8 @@ struct CtpCfg { uint32_t orbitShift = 0; uint32_t irInputs_1_24 = 0; uint32_t irInputs_25_48 = 0; - ClassDefNV(CtpCfg, 1) + std::vector listOfUsedInputs(); + ClassDefNV(CtpCfg, 2) }; } // namespace ctp } // namespace o2 diff --git a/DataFormats/Detectors/CTP/src/Configuration.cxx b/DataFormats/Detectors/CTP/src/Configuration.cxx index 61e51bcb20d91..98458ef06d1d3 100644 --- a/DataFormats/Detectors/CTP/src/Configuration.cxx +++ b/DataFormats/Detectors/CTP/src/Configuration.cxx @@ -1227,9 +1227,24 @@ int CtpCfg::readAndSave(std::string& path) } return 0; } - +std::vector CtpCfg::listOfUsedInputs() +{ + std::cout << std::hex << "0x" << irInputs_1_24 << " " << irInputs_25_48 << std::dec << std::endl; + std::vector inputList; + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_1_24) { + inputList.push_back(i); + } + } + for (int i = 0; i < 24; i++) { + if ((1ul << i) & irInputs_25_48) { + inputList.push_back(i + 24); + } + } + return inputList; +} std::ostream& o2::ctp::operator<<(std::ostream& in, const o2::ctp::CTPConfiguration& conf) { conf.printStream(in); return in; -} +} \ No newline at end of file diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h deleted file mode 100644 index 3c014d37e6f9e..0000000000000 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h +++ /dev/null @@ -1,55 +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 EMCALChannelData.h -/// \brief - -/// \class EMCALChannelCalibrator -/// \brief Class to store the data format for calibraton of the EMCal -/// \author Hannah Bossi, Yale University -/// \ingroup DetectorEMCAL -/// \since Feb 11, 2021 - -#ifndef ALICEO2_EMCALCHANNELDATA_H -#define ALICEO2_EMCALCHANNELDATA_H - -#include "Rtypes.h" - -namespace o2 -{ -namespace dataformats -{ -class EMCALChannelData -{ - public: - EMCALChannelData(int cellID, int timestamp, int flags = 0, int events) : mEMCALCellID(cellID), mTimestamp(timestamp), mFlags(flags){}; - EMCALChannelData() = default; - ~EMCALChannelData() = default; - - void setEMCALCellID(int index) { mEMCALCellID = index; } - int getEMCALCellID() const { return mEMCALCellID; } - - void setTimestamp(int ts) { mTimestamp = ts; } - int getTimestamp() const { return mTimestamp; } - - void setFlags(int flags) { mFlags = flags; } - float getFlags() const { return mFlags; } - - private: - int mEMCALCellID; ///< EMCal Cell ID - int mTimestamp; ///< timestamp in seconds - unsigned char mFlags; ///< bit mask with quality flags (to be defined) - - ClassDefNV(EMCALChannelData, 1); -}; -} // namespace dataformats -} // namespace o2 -#endif diff --git a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index e5331b7b739b2..f7d6a111f4348 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -47,4 +47,5 @@ o2_target_root_dictionary(DataFormatsFT0 include/DataFormatsFT0/GlobalOffsetsCalibrationObject.h include/DataFormatsFT0/SpectraInfoObject.h include/DataFormatsFT0/SlewingCoef.h + include/DataFormatsFT0/EventsPerBc.h ) diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h similarity index 63% rename from Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx rename to DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h index caa642794b561..9fcd1318914bd 100644 --- a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h @@ -9,16 +9,17 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// @file FT0DataReaderDPLSpec.cxx +#ifndef _FT0_EVENTS_PER_BC_CALIB_OBJECT +#define _FT0_EVENTS_PER_BC_CALIB_OBJECT -#include "FT0Workflow/FT0DataReaderDPLSpec.h" +#include "CommonConstants/LHCConstants.h" +#include -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 +namespace o2::ft0 { - -} // namespace ft0 -} // namespace o2 +struct EventsPerBc { + std::array histogram; + ClassDefNV(EventsPerBc, 1); +}; +} // namespace o2::ft0 +#endif \ No newline at end of file diff --git a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 0d3491224180c..7f8c17a0cd191 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -56,4 +56,6 @@ #pragma link C++ class std::pair < std::vector < double>, std::vector < double>> + ; #pragma link C++ class o2::ft0::SlewingCoef + ; +#pragma link C++ class o2::ft0::EventsPerBc + ; + #endif diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h index 1d7b10dc965f7..024d6189593e9 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CalibdEdxCorrection.h @@ -115,6 +115,9 @@ class CalibdEdxCorrection /// Single fit parameters averaged over all sectors for a stack type float getMeanEntries(const GEMstack stack, ChargeType charge) const; + /// set all corrections to 1, used for default initialization and to reset corrections + void setUnity(); + #endif private: diff --git a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx index 0991c8693d8e8..152feacb41937 100644 --- a/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx +++ b/DataFormats/Detectors/TPC/src/CalibdEdxCorrection.cxx @@ -168,3 +168,16 @@ float CalibdEdxCorrection::getMeanEntries(const GEMstack stack, ChargeType charg return mean / (SECTORSPERSIDE * SIDES); } + +void CalibdEdxCorrection::setUnity() +{ + for (int i = 0; i < FitSize; ++i) { + for (int j = 0; j < ParamSize; ++j) { + mParams[i][j] = 0.f; + } + mParams[i][0] = 1.f; // constant term = 1 + mChi2[i] = 0.f; + mEntries[i] = 0; + } + mDims = 0; +} diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h index f90101e7a4f21..b4e64db094a5c 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h @@ -33,12 +33,52 @@ class CalGain void setMPVdEdx(int iDet, float mpv) { mMPVdEdx[iDet] = mpv; } - float getMPVdEdx(int iDet) const { return mMPVdEdx[iDet]; } + float getMPVdEdx(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || isGoodGain(iDet)) + return mMPVdEdx[iDet]; + else { + if (TMath::Abs(mMeanGain + 999.) < 1e-6) + mMeanGain = getAverageGain(); + return mMeanGain; + } + } + + float getAverageGain() const + { + float averageGain = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodGain(iDet)) { + // The chamber has correct calibration + ngood++; + averageGain += mMPVdEdx[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::MPVDEDXDEFAULT; + } + averageGain /= ngood; + return averageGain; + } + + bool isGoodGain(int iDet) const + { + if (TMath::Abs(mMPVdEdx[iDet] - constants::MPVDEDXDEFAULT) > 1e-6) + return true; + else + return false; + } private: std::array mMPVdEdx{}; ///< Most probable value of dEdx distribution per TRD chamber + mutable float mMeanGain{-999.}; ///! average gain, calculated only once - ClassDefNV(CalGain, 1); + ClassDefNV(CalGain, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h index bad9dcfef4e37..65981e928fb39 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h @@ -34,14 +34,102 @@ class CalVdriftExB void setVdrift(int iDet, float vd) { mVdrift[iDet] = vd; } void setExB(int iDet, float exb) { mExB[iDet] = exb; } - float getVdrift(int iDet) const { return mVdrift[iDet]; } - float getExB(int iDet) const { return mExB[iDet]; } + float getVdrift(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mVdrift[iDet]; + else { + if (TMath::Abs(mMeanVdrift + 999.) < 1e-6) + mMeanVdrift = getAverageVdrift(); + return mMeanVdrift; + } + } + float getExB(int iDet, bool defaultAvg = true) const + { + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mExB[iDet]; + else { + if (TMath::Abs(mMeanExB + 999.) < 1e-6) + mMeanExB = getAverageExB(); + return mMeanExB; + } + } + + float getAverageVdrift() const + { + float averageVdrift = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageVdrift += mVdrift[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::VDRIFTDEFAULT; + } + averageVdrift /= ngood; + return averageVdrift; + } + + float getAverageExB() const + { + float averageExB = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageExB += mExB[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::EXBDEFAULT; + } + averageExB /= ngood; + return averageExB; + } + + bool isGoodExB(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (TMath::Abs(mExB[iDet] - constants::EXBDEFAULT) > 1e-6 && + TMath::Abs(mExB[iDet] - constants::EXBMIN) > 0.01 && + TMath::Abs(mExB[iDet] - constants::EXBMAX) > 0.01) + return true; + else + return false; + } + + bool isGoodVdrift(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (TMath::Abs(mVdrift[iDet] - constants::VDRIFTDEFAULT) > 1e-6 && + TMath::Abs(mVdrift[iDet] - constants::VDRIFTMIN) > 0.1 && + TMath::Abs(mVdrift[iDet] - constants::VDRIFTMAX) > 0.1) + return true; + else + return false; + } private: std::array mVdrift{}; ///< calibrated drift velocity per TRD chamber std::array mExB{}; ///< calibrated Lorentz angle per TRD chamber + mutable float mMeanVdrift{-999.}; ///! average drift velocity, calculated only once + mutable float mMeanExB{-999.}; ///! average lorentz angle, calculated only once - ClassDefNV(CalVdriftExB, 1); + ClassDefNV(CalVdriftExB, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h index 7a650cf3699cf..9a4da1024e251 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h @@ -75,7 +75,11 @@ constexpr int TIMEBINS = 30; ///< the number of time bins constexpr float MAXIMPACTANGLE = 25.f; ///< the maximum impact angle for tracks relative to the TRD detector plane to be considered for vDrift and ExB calibration constexpr int NBINSANGLEDIFF = 25; ///< the number of bins for the track angle used for the vDrift and ExB calibration based on the tracking constexpr double VDRIFTDEFAULT = 1.546; ///< default value for vDrift +constexpr double VDRIFTMIN = 0.4; ///< min value for vDrift +constexpr double VDRIFTMAX = 2.0; ///< max value for vDrift constexpr double EXBDEFAULT = 0.0; ///< default value for LorentzAngle +constexpr double EXBMIN = -0.4; ///< min value for LorentzAngle +constexpr double EXBMAX = 0.4; ///< max value for LorentzAngle constexpr int NBINSGAINCALIB = 320; ///< number of bins in the charge (Q0+Q1+Q2) histogram for gain calibration constexpr float MPVDEDXDEFAULT = 42.; ///< default Most Probable Value of TRD dEdx constexpr float T0DEFAULT = 1.2; ///< default value for t0 diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 2d16f343dc1eb..2c58db42ed856 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -238,6 +238,7 @@ class AODProducerWorkflowDPL : public Task bool mPropTracks{false}; bool mPropMuons{false}; float mTrackQCKeepGlobalTracks{false}; + float mTrackQCRetainOnlydEdx{false}; float mTrackQCFraction{0.00}; int64_t mTrackQCNTrCut{4}; float mTrackQCDCAxy{3.}; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index be169ad4be19d..852419a9895eb 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -363,10 +363,10 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs { tracksQACursor( trackQAInfoHolder.trackID, - truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + mTrackQCRetainOnlydEdx ? 0.0f : truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), truncateFloatFraction(trackQAInfoHolder.tpcdEdxNorm, mTrackSignal), - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaR, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.tpcdcaZ, trackQAInfoHolder.tpcClusterByteMask, trackQAInfoHolder.tpcdEdxMax0R, trackQAInfoHolder.tpcdEdxMax1R, @@ -376,18 +376,18 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs trackQAInfoHolder.tpcdEdxTot1R, trackQAInfoHolder.tpcdEdxTot2R, trackQAInfoHolder.tpcdEdxTot3R, - trackQAInfoHolder.dRefContY, - trackQAInfoHolder.dRefContZ, - trackQAInfoHolder.dRefContSnp, - trackQAInfoHolder.dRefContTgl, - trackQAInfoHolder.dRefContQ2Pt, - trackQAInfoHolder.dRefGloY, - trackQAInfoHolder.dRefGloZ, - trackQAInfoHolder.dRefGloSnp, - trackQAInfoHolder.dRefGloTgl, - trackQAInfoHolder.dRefGloQ2Pt, - trackQAInfoHolder.dTofdX, - trackQAInfoHolder.dTofdZ); + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefContQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloY, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloZ, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloSnp, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloTgl, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dRefGloQ2Pt, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdX, + mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdZ); } template @@ -499,7 +499,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, float weight = 0; static std::uniform_real_distribution<> distr(0., 1.); - bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)) || (src != GIndex::TPC && mTrackQCKeepGlobalTracks); + bool writeQAData = o2::math_utils::Tsallis::downsampleTsallisCharged(data.getTrackParam(trackIndex).getPt(), mTrackQCFraction, mSqrtS, weight, distr(mGenerator)) || ((src != GIndex::TPC || mGIDUsedBySVtx.find(trackIndex) != mGIDUsedBySVtx.end() || mGIDUsedByStr.find(trackIndex) != mGIDUsedByStr.end()) && mTrackQCKeepGlobalTracks); auto extraInfoHolder = processBarrelTrack(collisionID, collisionBC, trackIndex, data, bcsMap); if (writeQAData) { @@ -1720,6 +1720,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) } } mTrackQCKeepGlobalTracks = ic.options().get("trackqc-keepglobaltracks"); + mTrackQCRetainOnlydEdx = ic.options().get("trackqc-retainonlydedx"); mTrackQCFraction = ic.options().get("trackqc-fraction"); mTrackQCNTrCut = ic.options().get("trackqc-NTrCut"); mTrackQCDCAxy = ic.options().get("trackqc-tpc-dca"); @@ -3356,6 +3357,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"propagate-muons", VariantType::Bool, false, {"Propagate muons to IP"}}, ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}}, ConfigParamSpec{"trackqc-keepglobaltracks", VariantType::Bool, false, {"Always keep TrackQA for global tracks"}}, + ConfigParamSpec{"trackqc-retainonlydedx", VariantType::Bool, false, {"Keep only dEdx information, zero out everything else"}}, ConfigParamSpec{"trackqc-fraction", VariantType::Float, float(0.1), {"Fraction of tracks to QC"}}, ConfigParamSpec{"trackqc-NTrCut", VariantType::Int64, 4L, {"Minimal length of the track - in amount of tracklets"}}, ConfigParamSpec{"trackqc-tpc-dca", VariantType::Float, 3.f, {"Keep TPC standalone track with this DCAxy to the PV"}}, diff --git a/Detectors/Base/CMakeLists.txt b/Detectors/Base/CMakeLists.txt index 30ab4c4fe8a40..83a9193274e4f 100644 --- a/Detectors/Base/CMakeLists.txt +++ b/Detectors/Base/CMakeLists.txt @@ -29,6 +29,8 @@ o2_add_library(DetectorsBase src/Stack.cxx src/VMCSeederService.cxx src/GlobalParams.cxx + src/O2Tessellated.cxx + src/TGeoGeometryUtils.cxx PUBLIC_LINK_LIBRARIES FairRoot::Base O2::CommonUtils O2::DetectorsCommonDataFormats @@ -46,6 +48,7 @@ o2_add_library(DetectorsBase O2::GPUDataTypes MC::VMC TBB::tbb + ROOT::Gdml ) o2_target_root_dictionary(DetectorsBase @@ -62,7 +65,9 @@ o2_target_root_dictionary(DetectorsBase include/DetectorsBase/Aligner.h include/DetectorsBase/Stack.h include/DetectorsBase/SimFieldUtils.h - include/DetectorsBase/GlobalParams.h) + include/DetectorsBase/GlobalParams.h + include/DetectorsBase/O2Tessellated.h + ) if(BUILD_SIMULATION) if (NOT APPLE) diff --git a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h index 593bf37df5879..e94123bb2b7ff 100644 --- a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h +++ b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h @@ -314,6 +314,7 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (ic.options().hasOption("irframe-shift")) { mIRFrameSelShift = (long)ic.options().get("irframe-shift"); } + bool ansVersionProvided = false; if (ic.options().hasOption("ans-version")) { if (ic.options().isSet("ans-version")) { const std::string ansVersionString = ic.options().get("ans-version"); @@ -323,6 +324,7 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (mANSVersion == ANSVersionUnspecified) { throw std::invalid_argument(fmt::format("Invalid ANS Version {}", ansVersionString)); } + ansVersionProvided = true; } } } @@ -331,9 +333,12 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) } else { if (mDictOpt != "none") { // none means per-CTF dictionary will created on the fly createCodersFromFile(mDictOpt, mOpType); - LOGP(info, "Loaded {} from {}", mExtHeader.asString(), mDictOpt); + LOGP(info, "Loaded {} from {}, ANS Version {}", mExtHeader.asString(), mDictOpt, std::string(mANSVersion)); } else { - LOGP(info, "Internal per-TF CTF Dict will be created"); + if (!ansVersionProvided) { + mANSVersion = ANSVersion1; + } + LOGP(info, "Internal per-TF CTF Dict will be created, ANS Version {}", std::string(mANSVersion)); } mLoadDictFromCCDB = false; // don't try to load from CCDB } @@ -368,7 +373,7 @@ bool CTFCoderBase::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, voi } createCoders(*dict, mOpType); mExtHeader = static_cast(CTF::get(dict->data())->getHeader()); - LOGP(info, "Loaded {} from CCDB", mExtHeader.asString()); + LOGP(info, "Loaded {} from CCDB, ANS Version {}", mExtHeader.asString(), std::string(mANSVersion)); } mLoadDictFromCCDB = false; // we read the dictionary at most once! } else if ((match = (matcher == o2::framework::ConcreteDataMatcher("CTP", "Trig_Offset", 0)))) { diff --git a/Detectors/Base/include/DetectorsBase/O2Tessellated.h b/Detectors/Base/include/DetectorsBase/O2Tessellated.h new file mode 100644 index 0000000000000..0a1cee8b3e01f --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/O2Tessellated.h @@ -0,0 +1,142 @@ +// 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_BASE_O2TESSELLATED_ +#define ALICEO2_BASE_O2TESSELLATED_ + +#include "TGeoShape.h" +#include "TGeoBBox.h" +#include "TGeoVector3.h" +#include "TGeoTypedefs.h" +#include "TGeoTessellated.h" + +namespace o2 +{ +namespace base +{ + +class O2Tessellated : public TGeoBBox +{ + + public: + using Vertex_t = Tessellated::Vertex_t; + + private: + int fNfacets = 0; // Number of facets + int fNvert = 0; // Number of vertices + int fNseg = 0; // Number of segments + bool fDefined = false; //! Shape fully defined + bool fClosedBody = false; // The faces are making a closed body + + // for now separate vectors but might be better to group per face + std::vector fVertices; // List of vertices + std::vector fFacets; // List of facets + std::vector fOutwardNormals; // Vector of outward-facing normals (to be streamed !) + + std::multimap fVerticesMap; //! Temporary map used to deduplicate vertices + bool fIsClosed = false; //! to know if shape still needs closure/initialization + void* fBVH = nullptr; //! BVH acceleration structure for safety and navigation + + O2Tessellated(const O2Tessellated&) = delete; + O2Tessellated& operator=(const O2Tessellated&) = delete; + + // bvh helper functions + void BuildBVH(); + void CalculateNormals(); + + public: + // constructors + O2Tessellated() {} + O2Tessellated(const char* name, int nfacets = 0); + O2Tessellated(const char* name, const std::vector& vertices); + // from a TGeoTessellated + O2Tessellated(TGeoTessellated const&, bool check = false); + + // destructor + ~O2Tessellated() override {} + + void ComputeBBox() override; + void CloseShape(bool check = true, bool fixFlipped = true, bool verbose = true); + + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2); + bool AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3); + bool AddFacet(int i1, int i2, int i3); + bool AddFacet(int i1, int i2, int i3, int i4); + int AddVertex(const Vertex_t& vert); + + bool FacetCheck(int ifacet) const; + Vertex_t FacetComputeNormal(int ifacet, bool& degenerated) const; + + int GetNfacets() const { return fFacets.size(); } + int GetNsegments() const { return fNseg; } + int GetNvertices() const { return fNvert; } + bool IsClosedBody() const { return fClosedBody; } + bool IsDefined() const { return fDefined; } + + const TGeoFacet& GetFacet(int i) const { return fFacets[i]; } + const Vertex_t& GetVertex(int i) const { return fVertices[i]; } + + int DistancetoPrimitive(int, int) override { return 99999; } + const TBuffer3D& GetBuffer3D(int reqSections, Bool_t localFrame) const override; + void GetMeshNumbers(int& nvert, int& nsegs, int& npols) const override; + int GetNmeshVertices() const override { return fNvert; } + void InspectShape() const override {} + TBuffer3D* MakeBuffer3D() const override; + void Print(Option_t* option = "") const override; + void SavePrimitive(std::ostream&, Option_t*) override {} + void SetPoints(double* points) const override; + void SetPoints(Float_t* points) const override; + void SetSegsAndPols(TBuffer3D& buff) const override; + void Sizeof3D() const override {} + + /// Resize and center the shape in a box of size maxsize + void ResizeCenter(double maxsize); + + /// Flip all facets + void FlipFacets() + { + for (auto facet : fFacets) + facet.Flip(); + } + + bool CheckClosure(bool fixFlipped = true, bool verbose = true); + + /// Reader from .obj format + static O2Tessellated* ImportFromObjFormat(const char* objfile, bool check = false, bool verbose = false); + + // navigation functions used by TGeoNavigator (attention: only the iact == 3 cases implemented for now) + Double_t DistFromOutside(const Double_t* point, const Double_t* dir, Int_t iact = 1, + Double_t step = TGeoShape::Big(), Double_t* safe = nullptr) const override; + Double_t DistFromInside(const Double_t* point, const Double_t* dir, Int_t iact = 1, Double_t step = TGeoShape::Big(), + Double_t* safe = nullptr) const override; + bool Contains(const Double_t* point) const override; + Double_t Safety(const Double_t* point, Bool_t in = kTRUE) const override; + void ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const override; + + // these are trivial implementations, just for debugging + Double_t DistFromInside_Loop(const Double_t* point, const Double_t* dir) const; + Double_t DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const; + bool Contains_Loop(const Double_t* point) const; + + Double_t Capacity() const override; + + private: + // a safety kernel used in multiple implementations + template + Double_t SafetyKernel(const Double_t* point, bool in, int* closest_facet_id = nullptr) const; + + ClassDefOverride(O2Tessellated, 1) // tessellated shape class +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h similarity index 51% rename from Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h rename to Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h index a4988b2c18fc7..5ec85f1c14702 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h +++ b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h @@ -9,20 +9,30 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef O2_FIT_FT0WORKFLOW_H -#define O2_FIT_FT0WORKFLOW_H +/// \file TGeoGeometryUtils.h +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo -/// @file FT0Workflow.h +#ifndef ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ +#define ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ -#include "Framework/WorkflowSpec.h" +class TGeoShape; +class TGeoTessellated; namespace o2 { -namespace ft0 +namespace base { -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist); -} // namespace ft0 + +/// A few utility functions to operate on TGeo geometries (transformations, printing, ...) +class TGeoGeometryUtils +{ + public: + ///< Transform any (primitive) TGeoShape to a tessellated representation + static TGeoTessellated* TGeoShapeToTGeoTessellated(TGeoShape const*); +}; + +} // namespace base } // namespace o2 + #endif diff --git a/Detectors/Base/src/DetectorsBaseLinkDef.h b/Detectors/Base/src/DetectorsBaseLinkDef.h index bd76e9bfbe2e4..8255c143ebb4a 100644 --- a/Detectors/Base/src/DetectorsBaseLinkDef.h +++ b/Detectors/Base/src/DetectorsBaseLinkDef.h @@ -42,4 +42,6 @@ #pragma link C++ class o2::data::Stack + ; +#pragma link C++ class o2::base::O2Tessellated - ; + #endif diff --git a/Detectors/Base/src/O2Tessellated.cxx b/Detectors/Base/src/O2Tessellated.cxx new file mode 100644 index 0000000000000..256a70e5a697a --- /dev/null +++ b/Detectors/Base/src/O2Tessellated.cxx @@ -0,0 +1,1509 @@ +// 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. + +// Sandro Wenzel 2026 + +// An implementation of TGeoTessellated augmented with efficient navigation functions. +// Asked for integration into ROOT here https://github.com/root-project/root/pull/21045 +// Will be deleted once we get this from ROOT. + +#include +#include + +#include "TGeoManager.h" +#include "TGeoMatrix.h" +#include "TGeoVolume.h" +#include "TVirtualGeoPainter.h" +#include "DetectorsBase/O2Tessellated.h" +#include "TBuffer3D.h" +#include "TBuffer3DTypes.h" +#include "TMath.h" +#include "TBuffer.h" + +#include +#include + +// THIS IS THIRD PARTY CODE (TO BE PUT IN ROOT) WHICH DOES NOT NEED TO ADHERE TO OUR LINTING +// NOLINTBEGIN + +// include the Third-party BVH headers +#include "bvh2_third_party.h" +// some kernels on top of BVH +#include "bvh2_extra_kernels.h" + +#include +#include + +using namespace o2::base; +ClassImp(O2Tessellated); + +using Vertex_t = Tessellated::Vertex_t; + +//////////////////////////////////////////////////////////////////////////////// +/// Compact consecutive equal vertices + +int TGeoFacet::CompactFacet(Vertex_t* vert, int nvertices) +{ + // Compact the common vertices and return new facet + if (nvertices < 2) + return nvertices; + int nvert = nvertices; + int i = 0; + while (i < nvert) { + if (vert[(i + 1) % nvert] == vert[i]) { + // shift last vertices left by one element + for (int j = i + 2; j < nvert; ++j) + vert[j - 1] = vert[j]; + nvert--; + } + i++; + } + return nvert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check if a connected neighbour facet has compatible normal + +bool TGeoFacet::IsNeighbour(const TGeoFacet& other, bool& flip) const +{ + + // Find a connecting segment + bool neighbour = false; + int line1[2], line2[2]; + int npoints = 0; + for (int i = 0; i < fNvert; ++i) { + auto ivert = fIvert[i]; + // Check if the other facet has the same vertex + for (int j = 0; j < other.GetNvert(); ++j) { + if (ivert == other[j]) { + line1[npoints] = i; + line2[npoints] = j; + if (++npoints == 2) { + neighbour = true; + bool order1 = line1[1] == line1[0] + 1; + bool order2 = line2[1] == (line2[0] + 1) % other.GetNvert(); + flip = (order1 == order2); + return neighbour; + } + } + } + } + return neighbour; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor. In case nfacets is zero, it is user's responsibility to +/// call CloseShape once all faces are defined. + +O2Tessellated::O2Tessellated(const char* name, int nfacets) : TGeoBBox(name, 0, 0, 0) +{ + fNfacets = nfacets; + if (nfacets) + fFacets.reserve(nfacets); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor providing directly the array of vertices. Facets have to be added +/// providing vertex indices rather than coordinates. + +O2Tessellated::O2Tessellated(const char* name, const std::vector& vertices) : TGeoBBox(name, 0, 0, 0) +{ + fVertices = vertices; + fNvert = fVertices.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Construct from TGeoTessellated + +O2Tessellated::O2Tessellated(TGeoTessellated const& tsl, bool check) : TGeoBBox(tsl.GetName(), 0, 0, 0) +{ + fNfacets = tsl.GetNfacets(); + fNvert = tsl.GetNvertices(); + fNseg = tsl.GetNsegments(); + + // copy facet and vertex done + fVertices.reserve(fNvert); + fFacets.reserve(fNfacets); + for (int i = 0; i < fNfacets; ++i) { + fFacets.push_back(tsl.GetFacet(i)); + } + for (int i = 0; i < fNvert; ++i) { + fVertices.push_back(tsl.GetVertex(i)); + } + // finish remaining structures + CloseShape(check); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a vertex checking for duplicates, returning the vertex index + +int O2Tessellated::AddVertex(Vertex_t const& vert) +{ + constexpr double tolerance = 1.e-10; + auto vertexHash = [&](Vertex_t const& vertex) { + // Compute hash for the vertex + long hash = 0; + // helper function to generate hash from integer numbers + auto hash_combine = [](long seed, const long value) { + return seed ^ (std::hash{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + }; + for (int i = 0; i < 3; i++) { + // use tolerance to generate int with the desired precision from a real number for hashing + hash = hash_combine(hash, std::roundl(vertex[i] / tolerance)); + } + return hash; + }; + + auto hash = vertexHash(vert); + bool isAdded = false; + int ivert = -1; + // Get the compatible vertices + auto range = fVerticesMap.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + ivert = it->second; + if (fVertices[ivert] == vert) { + isAdded = true; + break; + } + } + if (!isAdded) { + ivert = fVertices.size(); + fVertices.push_back(vert); + fVerticesMap.insert(std::make_pair(hash, ivert)); + } + return ivert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + + Vertex_t vert[3]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + int nvert = TGeoFacet::CompactFacet(vert, 3); + if (nvert < 3) { + Error("AddFacet", "Triangular facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + int ind[3]; + for (auto i = 0; i < 3; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += 3; + fFacets.emplace_back(ind[0], ind[1], ind[2]); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 3; + fFacets.emplace_back(i0, i1, i2); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + Vertex_t vert[4]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + vert[3] = pt3; + int nvert = TGeoFacet::CompactFacet(vert, 4); + if (nvert < 3) { + Error("AddFacet", "Quadrilateral facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + + int ind[4]; + for (auto i = 0; i < nvert; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += nvert; + if (nvert == 3) + fFacets.emplace_back(ind[0], ind[1], ind[2]); + else + fFacets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + + if (fNfacets > 0 && GetNfacets() == fNfacets) + CloseShape(false); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2, int i3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 4; + fFacets.emplace_back(i0, i1, i2, i3); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute normal for a given facet + +Vertex_t O2Tessellated::FacetComputeNormal(int ifacet, bool& degenerated) const +{ + // Compute normal using non-zero segments + constexpr double kTolerance = 1.e-20; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + degenerated = true; + Vertex_t normal; + for (int i = 0; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i + 1]] - fVertices[facet[i]]; + if (e1.Mag2() < kTolerance) + continue; + for (int j = i + 1; j < nvert; ++j) { + Vertex_t e2 = fVertices[facet[(j + 1) % nvert]] - fVertices[facet[j]]; + if (e2.Mag2() < kTolerance) + continue; + normal = Vertex_t::Cross(e1, e2); + // e1 and e2 may be colinear + if (normal.Mag2() < kTolerance) + continue; + normal.Normalize(); + degenerated = false; + break; + } + if (!degenerated) + break; + } + return normal; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check validity of facet + +bool O2Tessellated::FacetCheck(int ifacet) const +{ + constexpr double kTolerance = 1.e-10; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + bool degenerated = true; + FacetComputeNormal(ifacet, degenerated); + if (degenerated) { + std::cout << "Facet: " << ifacet << " is degenerated\n"; + return false; + } + + // Compute surface area + double surfaceArea = 0.; + for (int i = 1; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i]] - fVertices[facet[0]]; + Vertex_t e2 = fVertices[facet[i + 1]] - fVertices[facet[0]]; + surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag(); + } + if (surfaceArea < kTolerance) { + std::cout << "Facet: " << ifacet << " has zero surface area\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Close the shape: calculate bounding box and compact vertices + +void O2Tessellated::CloseShape(bool check, bool fixFlipped, bool verbose) +{ + if (fIsClosed && fBVH) { + return; + } + // Compute bounding box + fDefined = true; + fNvert = fVertices.size(); + fNfacets = fFacets.size(); + ComputeBBox(); + + BuildBVH(); + if (fOutwardNormals.size() == 0) { + CalculateNormals(); + } else { + // short check if the normal container is of correct size + if (fOutwardNormals.size() != fFacets.size()) { + std::cerr << "Inconsistency in normal container"; + } + } + fIsClosed = true; + + // Cleanup the vertex map + std::multimap().swap(fVerticesMap); + + if (fVertices.size() > 0) { + if (!check) + return; + + // Check facets + for (auto i = 0; i < fNfacets; ++i) + FacetCheck(i); + + fClosedBody = CheckClosure(fixFlipped, verbose); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check closure of the solid and check/fix flipped normals + +bool O2Tessellated::CheckClosure(bool fixFlipped, bool verbose) +{ + int* nn = new int[fNfacets]; + bool* flipped = new bool[fNfacets]; + bool hasorphans = false; + bool hasflipped = false; + for (int i = 0; i < fNfacets; ++i) { + nn[i] = 0; + flipped[i] = false; + } + + for (int icrt = 0; icrt < fNfacets; ++icrt) { + // all neighbours checked? + if (nn[icrt] >= fFacets[icrt].GetNvert()) + continue; + for (int i = icrt + 1; i < fNfacets; ++i) { + bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[i], flipped[i]); + if (isneighbour) { + if (flipped[icrt]) + flipped[i] = !flipped[i]; + if (flipped[i]) + hasflipped = true; + nn[icrt]++; + nn[i]++; + if (nn[icrt] == fFacets[icrt].GetNvert()) + break; + } + } + if (nn[icrt] < fFacets[icrt].GetNvert()) + hasorphans = true; + } + + if (hasorphans && verbose) { + Error("Check", "Tessellated solid %s has following not fully connected facets:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (nn[icrt] < fFacets[icrt].GetNvert()) + std::cout << icrt << " (" << fFacets[icrt].GetNvert() << " edges, " << nn[icrt] << " neighbours)\n"; + } + } + fClosedBody = !hasorphans; + int nfixed = 0; + if (hasflipped) { + if (verbose) + Warning("Check", "Tessellated solid %s has following facets with flipped normals:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (flipped[icrt]) { + if (verbose) + std::cout << icrt << "\n"; + if (fixFlipped) { + fFacets[icrt].Flip(); + nfixed++; + } + } + } + if (nfixed && verbose) + Info("Check", "Automatically flipped %d facets to match first defined facet", nfixed); + } + delete[] nn; + delete[] flipped; + + return !hasorphans; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute bounding box + +void O2Tessellated::ComputeBBox() +{ + const double kBig = TGeoShape::Big(); + double vmin[3] = {kBig, kBig, kBig}; + double vmax[3] = {-kBig, -kBig, -kBig}; + for (const auto& facet : fFacets) { + for (int i = 0; i < facet.GetNvert(); ++i) { + for (int j = 0; j < 3; ++j) { + vmin[j] = TMath::Min(vmin[j], fVertices[facet[i]].operator[](j)); + vmax[j] = TMath::Max(vmax[j], fVertices[facet[i]].operator[](j)); + } + } + } + fDX = 0.5 * (vmax[0] - vmin[0]); + fDY = 0.5 * (vmax[1] - vmin[1]); + fDZ = 0.5 * (vmax[2] - vmin[2]); + for (int i = 0; i < 3; ++i) + fOrigin[i] = 0.5 * (vmax[i] + vmin[i]); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns numbers of vertices, segments and polygons composing the shape mesh. + +void O2Tessellated::GetMeshNumbers(int& nvert, int& nsegs, int& npols) const +{ + nvert = fNvert; + nsegs = fNseg; + npols = GetNfacets(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Creates a TBuffer3D describing *this* shape. +/// Coordinates are in local reference frame. + +TBuffer3D* O2Tessellated::MakeBuffer3D() const +{ + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + auto buff = new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols); + if (buff) { + SetPoints(buff->fPnts); + SetSegsAndPols(*buff); + } + return buff; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Prints basic info + +void O2Tessellated::Print(Option_t*) const +{ + std::cout << "=== Tessellated shape " << GetName() << " having " << GetNvertices() << " vertices and " + << GetNfacets() << " facets\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills TBuffer3D structure for segments and polygons. + +void O2Tessellated::SetSegsAndPols(TBuffer3D& buff) const +{ + const int c = GetBasicColor(); + int* segs = buff.fSegs; + int* pols = buff.fPols; + + int indseg = 0; // segment internal data index + int indpol = 0; // polygon internal data index + int sind = 0; // segment index + for (const auto& facet : fFacets) { + auto nvert = facet.GetNvert(); + pols[indpol++] = c; + pols[indpol++] = nvert; + for (auto j = 0; j < nvert; ++j) { + int k = (j + 1) % nvert; + // segment made by next consecutive points + segs[indseg++] = c; + segs[indseg++] = facet[j]; + segs[indseg++] = facet[k]; + // add segment to current polygon and increment segment index + pols[indpol + nvert - j - 1] = sind++; + } + indpol += nvert; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points to an array. + +void O2Tessellated::SetPoints(double* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + vertex.CopyTo(&points[ind]); + ind += 3; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points in float. + +void O2Tessellated::SetPoints(Float_t* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + points[ind++] = vertex.x(); + points[ind++] = vertex.y(); + points[ind++] = vertex.z(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Resize the shape by scaling vertices within maxsize and center to origin + +void O2Tessellated::ResizeCenter(double maxsize) +{ + using Vector3_t = Vertex_t; + + if (!fDefined) { + Error("ResizeCenter", "Not all faces are defined"); + return; + } + Vector3_t origin(fOrigin[0], fOrigin[1], fOrigin[2]); + double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ); + double scale = maxsize / maxedge; + for (size_t i = 0; i < fVertices.size(); ++i) { + fVertices[i] = scale * (fVertices[i] - origin); + } + fOrigin[0] = fOrigin[1] = fOrigin[2] = 0; + fDX *= scale; + fDY *= scale; + fDZ *= scale; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills a static 3D buffer and returns a reference. + +const TBuffer3D& O2Tessellated::GetBuffer3D(int reqSections, Bool_t localFrame) const +{ + static TBuffer3D buffer(TBuffer3DTypes::kGeneric); + + FillBuffer3D(buffer, reqSections, localFrame); + + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + + if (reqSections & TBuffer3D::kRawSizes) { + if (buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) { + buffer.SetSectionsValid(TBuffer3D::kRawSizes); + } + } + if ((reqSections & TBuffer3D::kRaw) && buffer.SectionsValid(TBuffer3D::kRawSizes)) { + SetPoints(buffer.fPnts); + if (!buffer.fLocalFrame) { + TransformPoints(buffer.fPnts, buffer.NbPnts()); + } + + SetSegsAndPols(buffer); + buffer.SetSectionsValid(TBuffer3D::kRaw); + } + + return buffer; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Reads a single tessellated solid from an .obj file. + +O2Tessellated* O2Tessellated::ImportFromObjFormat(const char* objfile, bool check, bool verbose) +{ + using std::vector, std::string, std::ifstream, std::stringstream, std::endl; + + vector vertices; + vector sfacets; + + struct FacetInd_t { + int i0 = -1; + int i1 = -1; + int i2 = -1; + int i3 = -1; + int nvert = 0; + FacetInd_t(int a, int b, int c) + { + i0 = a; + i1 = b; + i2 = c; + nvert = 3; + }; + FacetInd_t(int a, int b, int c, int d) + { + i0 = a; + i1 = b; + i2 = c; + i3 = d; + nvert = 4; + }; + }; + + vector facets; + // List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0. + // struct vtx_t { double x = 0; double y = 0; double z = 0; double w = 1; }; + + // Texture coordinates in u, [,v ,w]) coordinates, these will vary between 0 and 1. v, w are optional and default to + // 0. + // struct tex_t { double u; double v; double w; }; + + // List of vertex normals in (x,y,z) form; normals might not be unit vectors. + // struct vn_t { double x; double y; double z; }; + + // Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement + // struct vp_t { double u; double v; double w; }; + + // Faces are defined using lists of vertex, texture and normal indices which start at 1. + // Polygons such as quadrilaterals can be defined by using more than three vertex/texture/normal indices. + // f v1//vn1 v2//vn2 v3//vn3 ... + + // Records starting with the letter "l" specify the order of the vertices which build a polyline. + // l v1 v2 v3 v4 v5 v6 ... + + string line; + int ind[4] = {0}; + ifstream file(objfile); + if (!file.is_open()) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unable to open %s", objfile); + return nullptr; + } + + while (getline(file, line)) { + stringstream ss(line); + string tag; + + // We ignore everything which is not a vertex or a face + if (line.rfind('v', 0) == 0 && line.rfind("vt", 0) != 0 && line.rfind("vn", 0) != 0 && line.rfind("vn", 0) != 0) { + // Decode the vertex + double pos[4] = {0, 0, 0, 1}; + ss >> tag >> pos[0] >> pos[1] >> pos[2] >> pos[3]; + vertices.emplace_back(pos[0] * pos[3], pos[1] * pos[3], pos[2] * pos[3]); + } + + else if (line.rfind('f', 0) == 0) { + // Decode the face + ss >> tag; + string word; + sfacets.clear(); + while (ss >> word) + sfacets.push_back(word); + if (sfacets.size() > 4 || sfacets.size() < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Detected face having unsupported %zu vertices", + sfacets.size()); + return nullptr; + } + int nvert = 0; + for (auto& sword : sfacets) { + stringstream ssword(sword); + string token; + getline(ssword, token, '/'); // just need the vertex index, which is the first token + // Convert string token to integer + + ind[nvert++] = stoi(token) - 1; + if (ind[nvert - 1] < 0) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unsupported relative vertex index definition in %s", + objfile); + return nullptr; + } + } + if (nvert == 3) + facets.emplace_back(ind[0], ind[1], ind[2]); + else + facets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + } + } + + int nvertices = (int)vertices.size(); + int nfacets = (int)facets.size(); + if (nfacets < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Not enough faces detected in %s", objfile); + return nullptr; + } + + string sobjfile(objfile); + if (verbose) + std::cout << "Read " << nvertices << " vertices and " << nfacets << " facets from " << sobjfile << endl; + + auto tsl = new O2Tessellated(sobjfile.erase(sobjfile.find_last_of('.')).c_str(), vertices); + + for (int i = 0; i < nfacets; ++i) { + auto facet = facets[i]; + if (facet.nvert == 3) + tsl->AddFacet(facet.i0, facet.i1, facet.i2); + else + tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3); + } + tsl->CloseShape(check, true, verbose); + tsl->Print(); + return tsl; +} + +// implementation of some geometry helper functions in anonymous namespace +namespace +{ + +using Vertex_t = Tessellated::Vertex_t; +// The classic Moeller-Trumbore ray triangle-intersection kernel: +// - Compute triangle edges e1, e2 +// - Compute determinant det +// - Reject parallel rays +// - Compute barycentric coordinates u, v +// - Compute ray parameter t +double rayTriangle(const Vertex_t& orig, const Vertex_t& dir, const Vertex_t& v0, const Vertex_t& v1, + const Vertex_t& v2, double rayEPS = 1e-8) +{ + constexpr double EPS = 1e-8; + const double INF = std::numeric_limits::infinity(); + Vertex_t e1{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; + Vertex_t e2{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; + auto p = Vertex_t::Cross(dir, e2); + auto det = e1.Dot(p); + if (std::abs(det) <= EPS) { + return INF; + } + + Vertex_t tvec{orig[0] - v0[0], orig[1] - v0[1], orig[2] - v0[2]}; + auto invDet = 1.0 / det; + auto u = tvec.Dot(p) * invDet; + if (u < 0.0 || u > 1.0) { + return INF; + } + auto q = Vertex_t::Cross(tvec, e1); + auto v = dir.Dot(q) * invDet; + if (v < 0.0 || u + v > 1.0) { + return INF; + } + auto t = e2.Dot(q) * invDet; + return (t > rayEPS) ? t : INF; +} + +template +struct Vec3f { + T x, y, z; +}; + +template +inline Vec3f operator-(const Vec3f& a, const Vec3f& b) +{ + return {a.x - b.x, a.y - b.y, a.z - b.z}; +} + +template +inline Vec3f cross(const Vec3f& a, const Vec3f& b) +{ + return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; +} + +template +inline T dot(const Vec3f& a, const Vec3f& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +// Kernel to get closest/shortest distance between a point and a triangl (a,b,c). +// Performed by default in float since Safety can be approximate. +// Project point onto triangle plane +// If projection lies inside → distance to plane +// Otherwise compute min distance to the three edges +// Return squared distance +template +T pointTriangleDistSq(const Vec3f& p, const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + // Edges + Vec3f ab = b - a; + Vec3f ac = c - a; + Vec3f ap = p - a; + + auto d1 = dot(ab, ap); + auto d2 = dot(ac, ap); + if (d1 <= T(0.0) && d2 <= T(0.0)) { + return dot(ap, ap); // barycentric (1,0,0) + } + + Vec3f bp = p - b; + auto d3 = dot(ab, bp); + auto d4 = dot(ac, bp); + if (d3 >= T(0.0) && d4 <= d3) { + return dot(bp, bp); // (0,1,0) + } + + T vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + T v = d1 / (d1 - d3); + Vec3f proj = {a.x + v * ab.x, a.y + v * ab.y, a.z + v * ab.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AB + } + + Vec3f cp = p - c; + T d5 = dot(ab, cp); + T d6 = dot(ac, cp); + if (d6 >= T(0.0f) && d5 <= d6) { + return dot(cp, cp); // (0,0,1) + } + + T vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + T w = d2 / (d2 - d6); + Vec3f proj = {a.x + w * ac.x, a.y + w * ac.y, a.z + w * ac.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AC + } + + T va = d3 * d6 - d5 * d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) { + T w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + Vec3f proj = {b.x + w * (c.x - b.x), b.y + w * (c.y - b.y), b.z + w * (c.z - b.z)}; + Vec3f d = p - proj; + return dot(d, d); // edge BC + } + + // Inside face region + T denom = T(1.0f) / (va + vb + vc); + T v = vb * denom; + T w = vc * denom; + + Vec3f proj = {a.x + ab.x * v + ac.x * w, a.y + ab.y * v + ac.y * w, a.z + ab.z * v + ac.z * w}; + + Vec3f d = p - proj; + return dot(d, d); +} + +template +inline Vec3f normalize(const Vec3f& v) +{ + T len2 = dot(v, v); + if (len2 == T(0.0f)) { + std::cerr << "Degnerate triangle. Cannot determine normal"; + return {0, 0, 0}; + } + T invLen = T(1.0f) / std::sqrt(len2); + return {v.x * invLen, v.y * invLen, v.z * invLen}; +} + +template +inline Vec3f triangleNormal(const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + const Vec3f e1 = b - a; + const Vec3f e2 = c - a; + return normalize(cross(e1, e2)); +} + +} // end anonymous namespace + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromOutside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t stepmax, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + const auto topnode_bbox = mybvh->get_root().get_bbox(); + if ((-point[0] + topnode_bbox.min[0]) > stepmax) { + return Big(); + } + if ((-point[1] + topnode_bbox.min[1]) > stepmax) { + return Big(); + } + if ((-point[2] + topnode_bbox.min[2]) > stepmax) { + return Big(); + } + if ((point[0] - topnode_bbox.max[0]) > stepmax) { + return Big(); + } + if ((point[1] - topnode_bbox.max[1]) > stepmax) { + return Big(); + } + if ((point[2] - topnode_bbox.max[2]) > stepmax) { + return Big(); + } + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + const auto& facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // quick normal test. Coming from outside, the dot product must be negative + if (n.Dot(dir_v) > 0.) { + continue; + } + + auto thisdist = rayTriangle(Vertex_t(point[0], point[1], point[2]), dir_v, + fVertices[facet[0]], fVertices[facet[1]], fVertices[facet[2]], 0.); + + if (thisdist < local_step) { + local_step = thisdist; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromInside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t /*stepmax*/, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0., // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(dir_v) <= 0.) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = + rayTriangle(Vertex_t{point[0], point[1], point[2]}, dir_v, v0, v1, v2, 0.); + if (t < local_step) { + local_step = t; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Capacity + +Double_t O2Tessellated::Capacity() const +{ + // For explanation of the following algorithm see: + // https://en.wikipedia.org/wiki/Polyhedron#Volume + // http://wwwf.imperial.ac.uk/~rn/centroid.pdf + + double vol = 0.0; + for (size_t i = 0; i < fFacets.size(); ++i) { + auto& facet = fFacets[i]; + auto a = fVertices[facet[0]]; + auto b = fVertices[facet[1]]; + auto c = fVertices[facet[2]]; + vol += + a[0] * (b[1] * c[2] - b[2] * c[1]) + b[0] * (c[1] * a[2] - c[2] * a[1]) + c[0] * (a[1] * b[2] - a[2] * b[1]); + } + return vol / 6.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// BuildBVH + +void O2Tessellated::BuildBVH() +{ + using Scalar = float; + using BBox = bvh::v2::BBox; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // helper determining axis aligned bounding box from a facet; + auto GetBoundingBox = [this](TGeoFacet const& facet) { +#ifndef NDEBUG + const auto nvertices = facet.GetNvert(); + assert(nvertices == 3); // for now only triangles +#endif + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + BBox bbox; + bbox.min[0] = std::min(std::min(v1[0], v2[0]), v3[0]) - 0.001f; + bbox.min[1] = std::min(std::min(v1[1], v2[1]), v3[1]) - 0.001f; + bbox.min[2] = std::min(std::min(v1[2], v2[2]), v3[2]) - 0.001f; + bbox.max[0] = std::max(std::max(v1[0], v2[0]), v3[0]) + 0.001f; + bbox.max[1] = std::max(std::max(v1[1], v2[1]), v3[1]) + 0.001f; + bbox.max[2] = std::max(std::max(v1[2], v2[2]), v3[2]) + 0.001f; + return bbox; + }; + + // we need bounding boxes enclosing the primitives and centers of primitives + // (replaced here by centers of bounding boxes) to build the bvh + std::vector bboxes; + std::vector centers; + + // loop over all the triangles/Facets; + int nd = fFacets.size(); + for (int i = 0; i < nd; ++i) { + auto& facet = fFacets[i]; + + // fetch the bounding box of this node and add to the vector of bounding boxes + (bboxes).push_back(GetBoundingBox(facet)); + centers.emplace_back((bboxes).back().get_center()); + } + + // check if some previous object is registered and delete if necessary + if (fBVH) { + delete (Bvh*)fBVH; + fBVH = nullptr; + } + + // create the bvh + typename bvh::v2::DefaultBuilder::Config config; + config.quality = bvh::v2::DefaultBuilder::Quality::High; + auto bvh = bvh::v2::DefaultBuilder::build(bboxes, centers, config); + auto bvhptr = new Bvh; + *bvhptr = std::move(bvh); // copy structure + fBVH = (void*)(bvhptr); + + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Contains + +bool O2Tessellated::Contains(Double_t const* point) const +{ + // we do the parity test + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return false; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + if (!TGeoBBox::Contains(point)) { + return false; + } + + // An arbitrary test direction. + // Doesn't need to be normalized and probes all normals. Also ensuring to be skewed somewhat + // without evident symmetries. + Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + double local_step = Big(); + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(test_dir[0], test_dir[1], test_dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + size_t crossings = 0; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto& facet = fFacets[objectid]; + + // for the parity test, we probe all crossing surfaces + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(Vertex_t(point[0], point[1], point[2]), + test_dir, v0, v1, v2, 0.); + + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return false; + }); + + return crossings & 1; +} + +namespace +{ + +// Helper classes/structs used for priority queue - BVH traversal +// structure keeping cost (value) for a BVH index +struct BVHPrioElement { + size_t bvh_node_id; + float value; +}; + +// A priority queue for BVHPrioElement with an additional clear method +// for quick reset. We intentionally derive from std::priority_queue here to expose a +// clear() convenience method via access to the protected container `c`. +// This is internal, non-polymorphic code and relies on standard-library +// implementation details that are stable across supported platforms. +template +class BVHPrioQueue : public std::priority_queue, Comparator> +{ + public: + using std::priority_queue, + Comparator>::priority_queue; // constructor inclusion + + // convenience method to quickly clear/reset the queue (instead of having to pop one by one) + void clear() { this->c.clear(); } +}; + +} // namespace + +/// a reusable safety kernel, which optionally returns the closest face +template +inline Double_t O2Tessellated::SafetyKernel(const Double_t* point, bool in, int* closest_facet_id) const +{ + // This is the classic traversal/pruning of a BVH based on priority queue search + + float smallest_safety_sq = TGeoShape::Big(); + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + + // testpoint object in float for quick BVH interaction + Vec3 testpoint(point[0], point[1], point[2]); + + auto currnode = mybvh->nodes[0]; // we start from the top BVH node + // we do a quick check on the top node (in case we are outside shape) + bool outside_top = false; + if (!in) { + outside_top = !bvh::v2::extra::contains(currnode.get_bbox(), testpoint); + if (outside_top) { + const auto safety_sq_to_top = bvh::v2::extra::SafetySqToNode(currnode.get_bbox(), testpoint); + // we simply return safety to the outer bounding box as an estimate + return std::sqrt(safety_sq_to_top); + } + } + + // comparator bringing out "smallest" value on top + auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; }; + static thread_local BVHPrioQueue queue(cmp); + queue.clear(); + + // algorithm is based on standard iterative tree traversal with priority queues + float current_safety_to_node_sq = 0.f; + + if (returnFace) { + *closest_facet_id = -1; + } + + do { + if (currnode.is_leaf()) { + // we are in a leaf node and actually talk to a face/triangular primitive + const auto begin_prim_id = currnode.index.first_id(); + const auto end_prim_id = begin_prim_id + currnode.index.prim_count(); + + for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) { + const auto object_id = mybvh->prim_ids[p_id]; + + const auto& facet = fFacets[object_id]; + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + + auto thissafetySQ = pointTriangleDistSq(Vec3f{point[0], point[1], point[2]}, Vec3f{v1[0], v1[1], v1[2]}, + Vec3f{v2[0], v2[1], v2[2]}, Vec3f{v3[0], v3[1], v3[2]}); + + if (thissafetySQ < smallest_safety_sq) { + smallest_safety_sq = thissafetySQ; + if (returnFace) { + *closest_facet_id = object_id; + } + } + } + } else { + // not a leave node ... for further traversal, + // we inject the children into priority queue based on distance to it's bounding box + const auto leftchild_id = currnode.index.first_id(); + const auto rightchild_id = leftchild_id + 1; + + for (size_t childid : {leftchild_id, rightchild_id}) { + if (childid >= mybvh->nodes.size()) { + continue; + } + + const auto& node = mybvh->nodes[childid]; + const auto inside = bvh::v2::extra::contains(node.get_bbox(), testpoint); + + if (inside) { + // this must be further considered because we are inside the bounding box + queue.push(BVHPrioElement{childid, -1.}); + } else { + auto safety_to_node_square = bvh::v2::extra::SafetySqToNode(node.get_bbox(), testpoint); + if (safety_to_node_square <= smallest_safety_sq) { + // this should be further considered + queue.push(BVHPrioElement{childid, safety_to_node_square}); + } + } + } + } + + if (queue.size() > 0) { + auto currElement = queue.top(); + currnode = mybvh->nodes[currElement.bvh_node_id]; + current_safety_to_node_sq = currElement.value; + queue.pop(); + } else { + break; + } + } while (current_safety_to_node_sq <= smallest_safety_sq); + + return std::nextafter(std::sqrt(smallest_safety_sq), 0.0f); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Safety + +Double_t O2Tessellated::Safety(const Double_t* point, Bool_t in) const +{ + // we could use some caching here (in future) since queries to the solid will likely + // be made with some locality + + // fall-back to precise safety kernel + return SafetyKernel(point, in); +} + +//////////////////////////////////////////////////////////////////////////////// +/// ComputeNormal interface + +void O2Tessellated::ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const +{ + // We take the approach to identify closest facet to the point via safety + // and returning the normal from this face. + + // TODO: Before doing that we could check for cached points from other queries + + // use safety kernel + int closest_face_id = -1; + SafetyKernel(point, true, &closest_face_id); + + if (closest_face_id < 0) { + norm[0] = 1.; + norm[1] = 0.; + norm[2] = 0.; + return; + } + + const auto& n = fOutwardNormals[closest_face_id]; + norm[0] = n[0]; + norm[1] = n[1]; + norm[2] = n[2]; + + // change sign depending on dir + if (norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2] < 0) { + norm[0] = -norm[0]; + norm[1] = -norm[1]; + norm[2] = -norm[2]; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromInside function + +Double_t O2Tessellated::DistFromInside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(d) <= 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromOutside function + +Double_t O2Tessellated::DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from outside, the dot product must be negative) + if (n.Dot(d) > 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) Contains + +bool O2Tessellated::Contains_Loop(const Double_t* point) const +{ + // Fixed ray direction + const Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + Vertex_t p(point[0], point[1], point[2]); + + int crossings = 0; + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, test_dir, v0, v1, v2, 0.); + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return (crossings & 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Custom streamer which performs Closing on read. +/// Recalculation of BVH and normals is fast + +void O2Tessellated::Streamer(TBuffer& b) +{ + if (b.IsReading()) { + b.ReadClassBuffer(O2Tessellated::Class(), this); + CloseShape(false); // close shape but do not re-perform checks + } else { + b.WriteClassBuffer(O2Tessellated::Class(), this); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate the normals + +void O2Tessellated::CalculateNormals() +{ + fOutwardNormals.clear(); + for (auto& facet : fFacets) { + auto& v1 = fVertices[facet[0]]; + auto& v2 = fVertices[facet[1]]; + auto& v3 = fVertices[facet[2]]; + using Vec3d = Vec3f; + auto norm = triangleNormal(Vec3d{v1[0], v1[1], v1[2]}, Vec3d{v2[0], v2[1], v2[2]}, Vec3d{v3[0], v3[1], v3[2]}); + fOutwardNormals.emplace_back(Vertex_t{norm.x, norm.y, norm.z}); + } +} + +// NOLINTEND \ No newline at end of file diff --git a/Detectors/Base/src/TGeoGeometryUtils.cxx b/Detectors/Base/src/TGeoGeometryUtils.cxx new file mode 100644 index 0000000000000..6f06eff17a6d7 --- /dev/null +++ b/Detectors/Base/src/TGeoGeometryUtils.cxx @@ -0,0 +1,144 @@ +// 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 TGeoGeometryUtils.cxx +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#include +#include +#include +#include +#include + +namespace o2 +{ +namespace base +{ + +namespace +{ +// some helpers to interpret TGeo TBuffer3D output +// and convert it to surface triangles (reengineered from TGeo code) + +std::vector BuildVertexLoop(const TBuffer3D& buf, + const std::vector& segs) +{ + // adjacency list + std::unordered_map> adj; + + for (int s : segs) { + int a = buf.fSegs[3 * s + 1]; + int b = buf.fSegs[3 * s + 2]; + adj[a].push_back(b); + adj[b].push_back(a); + } + + // start from any vertex + int start = adj.begin()->first; + int prev = -1; + int curr = start; + + std::vector loop; + + while (true) { + loop.push_back(curr); + + const auto& nbrs = adj[curr]; + int next = -1; + + for (int n : nbrs) { + if (n != prev) { + next = n; + break; + } + } + + if (next == -1 || next == start) { + break; + } + + prev = curr; + curr = next; + } + return loop; +} + +std::vector> ExtractPolygons(const TBuffer3D& buf) +{ + std::vector> polys; + Int_t idx = 0; + + for (Int_t ip = 0; ip < buf.NbPols(); ++ip) { + + idx++; // color + Int_t nseg = buf.fPols[idx++]; + + std::vector segs(nseg); + for (Int_t i = 0; i < nseg; ++i) { + segs[i] = buf.fPols[idx++]; + } + + auto verts = BuildVertexLoop(buf, segs); + if (verts.size() >= 3) { + polys.push_back(std::move(verts)); + } + } + + return polys; +} + +std::vector> + Triangulate(const std::vector>& polys) +{ + std::vector> tris; + for (const auto& poly : polys) { + int nv = poly.size(); + if (nv < 3) { + continue; + } + + int v0 = poly[0]; + for (int i = 1; i < nv - 1; ++i) { + tris.push_back({{v0, poly[i], poly[i + 1]}}); + } + } + return tris; +} + +TGeoTessellated* MakeTessellated(const TBuffer3D& buf) +{ + auto polys = ExtractPolygons(buf); + auto tris = Triangulate(polys); + int i = 0; + auto* tess = new TGeoTessellated("tess"); + const Double_t* p = buf.fPnts; + for (auto& t : tris) { + tess->AddFacet( + TGeoTessellated::Vertex_t{p[3 * t[0]], p[3 * t[0] + 1], p[3 * t[0] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[1]], p[3 * t[1] + 1], p[3 * t[1] + 2]}, + TGeoTessellated::Vertex_t{p[3 * t[2]], p[3 * t[2] + 1], p[3 * t[2] + 2]}); + } + tess->CloseShape(); + return tess; +} +} // end anonymous namespace + +///< Transform any (primitive) TGeoShape to a TGeoTessellated +TGeoTessellated* TGeoGeometryUtils::TGeoShapeToTGeoTessellated(TGeoShape const* shape) +{ + auto& buf = shape->GetBuffer3D(TBuffer3D::kRawSizes | TBuffer3D::kRaw | TBuffer3D::kCore, false); + auto tes = MakeTessellated(buf); + return tes; +} + +} // namespace base +} // namespace o2 diff --git a/Detectors/Base/src/bvh2_extra_kernels.h b/Detectors/Base/src/bvh2_extra_kernels.h new file mode 100644 index 0000000000000..70e43202a53c4 --- /dev/null +++ b/Detectors/Base/src/bvh2_extra_kernels.h @@ -0,0 +1,79 @@ +// 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. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_EXTRA + +namespace bvh::v2::extra +{ + +// reusable geometry kernels used in multiple places +// for interaction with BVH2 structures + +// determines if a point is inside the bounding box +template +bool contains(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + auto min = box.min; + auto max = box.max; + return (p[0] >= min[0] && p[0] <= max[0]) && (p[1] >= min[1] && p[1] <= max[1]) && + (p[2] >= min[2] && p[2] <= max[2]); +} + +// determines the largest squared distance of point to any of the bounding box corners +template +auto RmaxSqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + // construct the 8 corners to get the maximal distance + const auto minCorner = box.min; + const auto maxCorner = box.max; + using Vec3 = bvh::v2::Vec; + // these are the corners of the bounding box + const std::array, 8> corners{ + Vec3{minCorner[0], minCorner[1], minCorner[2]}, Vec3{minCorner[0], minCorner[1], maxCorner[2]}, + Vec3{minCorner[0], maxCorner[1], minCorner[2]}, Vec3{minCorner[0], maxCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], minCorner[1], minCorner[2]}, Vec3{maxCorner[0], minCorner[1], maxCorner[2]}, + Vec3{maxCorner[0], maxCorner[1], minCorner[2]}, Vec3{maxCorner[0], maxCorner[1], maxCorner[2]}}; + + T Rmax_sq{0}; + for (const auto& corner : corners) { + float R_sq = 0.; + const auto dx = corner[0] - p[0]; + R_sq += dx * dx; + const auto dy = corner[1] - p[1]; + R_sq += dy * dy; + const auto dz = corner[2] - p[2]; + R_sq += dz * dz; + Rmax_sq = std::max(Rmax_sq, R_sq); + } + return Rmax_sq; +}; + +// determines the minimum squared distance of point to a bounding box ("safey square") +template +auto SafetySqToNode(bvh::v2::BBox const& box, bvh::v2::Vec const& p) +{ + T sqDist{0.0}; + for (int i = 0; i < 3; i++) { + T v = p[i]; + if (v < box.min[i]) { + sqDist += (box.min[i] - v) * (box.min[i] - v); + } else if (v > box.max[i]) { + sqDist += (v - box.max[i]) * (v - box.max[i]); + } + } + return sqDist; +}; + +} // namespace bvh::v2::extra + +#endif \ No newline at end of file diff --git a/Detectors/Base/src/bvh2_third_party.h b/Detectors/Base/src/bvh2_third_party.h new file mode 100644 index 0000000000000..5cf7772269642 --- /dev/null +++ b/Detectors/Base/src/bvh2_third_party.h @@ -0,0 +1,49 @@ +// 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. + +// Sandro Wenzel 2026 + +#ifndef ROOT_GEOM_BVH2_THIRD_PARTY + +// A single entry header into third-party BVH2 installed in ROOT +// Good place to manage compiler warnings etc. + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wshadow" +#pragma clang diagnostic ignored "-Wpsabi" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wpsabi" +#pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wattributes" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 5051) +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#endif + +#endif \ No newline at end of file diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index a5f9d0eac90e8..4e259c24f44a6 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace cpv class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::CPV, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CPV, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx index 6f9445d9ddd16..90fedb1d5eb83 100644 --- a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index b17db0e77be28..8dbc5adadbfc5 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ctp class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::CTP, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CTP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx index 9057d16df4384..c2b324a4b3bfa 100644 --- a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"no-lumi-input", VariantType::Bool, false, {"Lumi info not available"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; diff --git a/Detectors/DCS/src/DataPointCreator.cxx b/Detectors/DCS/src/DataPointCreator.cxx index 06b0321ad2c94..0bfb5bcd7d387 100644 --- a/Detectors/DCS/src/DataPointCreator.cxx +++ b/Detectors/DCS/src/DataPointCreator.cxx @@ -37,10 +37,9 @@ DataPointCompositeObject createDataPointCompositeObject(const std::string& alias template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, float val, uint32_t seconds, uint16_t msec, uint16_t flags) { - float tmp[2]; - tmp[0] = val; - tmp[1] = 0; - return createDPCOM(alias, reinterpret_cast(&tmp[0]), seconds, msec, flags, DeliveryType::DPVAL_FLOAT); + uint64_t tmp = 0; + memcpy(&tmp, &val, sizeof(val)); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_FLOAT); } template <> @@ -54,36 +53,38 @@ template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, uint32_t val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_UINT); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, long long val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{static_cast(val)}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_UINT); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_UINT); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, char val, uint32_t seconds, uint16_t msec, uint16_t flags) { - return createDPCOM(alias, reinterpret_cast(&val), seconds, msec, flags, DeliveryType::DPVAL_CHAR); + uint64_t tmp = 0; + memcpy(&tmp, &val, 1); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_CHAR); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, bool val, uint32_t seconds, uint16_t msec, uint16_t flags) { uint64_t tmp{val}; - return createDPCOM(alias, reinterpret_cast(&tmp), seconds, msec, flags, DeliveryType::DPVAL_BOOL); + return createDPCOM(alias, &tmp, seconds, msec, flags, DeliveryType::DPVAL_BOOL); } template <> DataPointCompositeObject createDataPointCompositeObject(const std::string& alias, std::string val, uint32_t seconds, uint16_t msec, uint16_t flags) { constexpr int N{56}; - char str[N]; - strncpy(str, val.c_str(), N); - return createDPCOM(alias, reinterpret_cast(&str[0]), seconds, msec, flags, DeliveryType::DPVAL_STRING); + uint64_t tmp[N / sizeof(uint64_t)]; + strncpy((char*)tmp, val.c_str(), N); + return createDPCOM(alias, tmp, seconds, msec, flags, DeliveryType::DPVAL_STRING); } } // namespace o2::dcs diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index 6584775057d9f..706b9bf8138a6 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace emcal class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::EMC, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::EMC, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx b/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx deleted file mode 100644 index 6fc119dc69521..0000000000000 --- a/Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx +++ /dev/null @@ -1,171 +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 rawReaderFileNew.cxx -/// \author Markus Fasel , Oak Ridge National Laboratory - -#include -#include - -#include - -#include "DetectorsRaw/RawFileReader.h" -#include "DetectorsRaw/RDHUtils.h" -#include "EMCALBase/Mapper.h" -#include "EMCALBase/TriggerMappingV2.h" -#include "EMCALReconstruction/AltroDecoder.h" -#include "EMCALReconstruction/RawReaderMemory.h" -#include - -namespace bpo = boost::program_options; -// using namespace o2::emcal; - -int main(int argc, char** argv) -{ - bpo::variables_map vm; - bpo::options_description opt_general("Usage:\n " + std::string(argv[0]) + - " \n" - " Tool will decode the DDLx data for EMCAL 0\n" - "Commands / Options"); - bpo::options_description opt_hidden(""); - bpo::options_description opt_all; - bpo::positional_options_description opt_pos; - - try { - auto add_option = opt_general.add_options(); - add_option("help,h", "Print this help message"); - add_option("verbose,v", bpo::value()->default_value(0), "Select verbosity level [0 = no output]"); - add_option("version", "Print version information"); - add_option("input-file,i", bpo::value()->required(), "Specifies input file."); - add_option("debug,d", bpo::value()->default_value(0), "Select debug output level [0 = no debug output]"); - - opt_all.add(opt_general).add(opt_hidden); - bpo::store(bpo::command_line_parser(argc, argv).options(opt_all).positional(opt_pos).run(), vm); - - if (vm.count("help") || argc == 1) { - std::cout << opt_general << std::endl; - exit(0); - } - - if (vm.count("version")) { - // std::cout << GitInfo(); - exit(0); - } - - bpo::notify(vm); - } catch (bpo::error& e) { - std::cerr << "ERROR: " << e.what() << std::endl - << std::endl; - std::cerr << opt_general << std::endl; - exit(1); - } catch (std::exception& e) { - std::cerr << e.what() << ", application will now exit" << std::endl; - exit(2); - } - - auto rawfilename = vm["input-file"].as(); - - o2::raw::RawFileReader reader; - reader.setDefaultDataOrigin(o2::header::gDataOriginEMC); - reader.setDefaultDataDescription(o2::header::gDataDescriptionRawData); - reader.setDefaultReadoutCardType(o2::raw::RawFileReader::RORC); - reader.addFile(rawfilename); - reader.init(); - - o2::emcal::MappingHandler mapper; - o2::emcal::TriggerMappingV2 triggermapping; - - std::unique_ptr treefile(TFile::Open("trudata.root", "RECREATE")); - TTree trudata("trudata", "Tree with TRU data"); - // branches in tree - struct collisiontrigger { - unsigned long bc; - unsigned long orbit; - } mycollision; - int absFastOR; - int starttime; - std::vector timesamples; - tree->Branch(&mycollision, "collisiontrigger", "bc,orbit/l"); - tree->Branch(&starttime, "starttime", "starttime/i"); - tree->Branch(×amples, "timesamples", ""); // @todo check how to write std::vector to tree; - - while (1) { - int tfID = reader.getNextTFToRead(); - if (tfID >= reader.getNTimeFrames()) { - LOG(info) << "nothing left to read after " << tfID << " TFs read"; - break; - } - std::vector dataBuffer; // where to put extracted data - for (int il = 0; il < reader.getNLinks(); il++) { - auto& link = reader.getLink(il); - std::cout << "Decoding link " << il << std::endl; - - auto sz = link.getNextTFSize(); // size in bytes needed for the next TF of this link - dataBuffer.resize(sz); - link.readNextTF(dataBuffer.data()); - - // Parse - o2::emcal::RawReaderMemory parser(dataBuffer); - while (parser.hasNext()) { - parser.next(); - auto rdh = parser.getRawHeader(); - auto ddl = o2::raw::RDHUtils::getFEEID(parser.getRawHeader()); - // Exclude STU DDLs - if (ddl >= 40) { - continue; - } - - mycollision.bc = o2::raw::RDHUtils::getTriggerBC(rdh); - mycollision.orbit = o2::raw::RDHUtils::getTriggerOrbit(rdh); - - o2::emcal::AltroDecoder decoder(parser); - decoder.decode(); - auto& ddlmapping = mapper.getMappingForDDL(ddl); - - std::cout << decoder.getRCUTrailer() << std::endl; - for (auto& chan : decoder.getChannels()) { - if (ddlmapping.getChannelType(chan.getHardwareAddress) != o2::emcal::ChannelType_t::TRU) { - continue; - } - std::cout << "Hw address: " << chan.getHardwareAddress() << std::endl; - // Get absolute FastOR index - this will tell us where on the EMCAL surface the FastOR is - // TRU index is encoded in column, needs to be converted to an absoluted FastOR ID via the - // trigger mapping. The absoluted FastOR ID can be connected via the geometry to tower IDs - // from the FEC data. - // we are only interested in the FastORs for now, skip patches starting from 96 - auto fastorInTRU = ddlmapping.getColumn(chan.getHardwareAddress()); - if (fastorInTRU >= 96) { - // indices starting from 96 encode patches, not FastORs - continue; - } - auto truindex = triggermapping.getTRUIndexFromOnlineHardareAddree(chan.getHardwareAddress(), ddl, ddl / 2); - auto absFastOrID = triggermapping.getAbsFastORIndexFromIndexInTRU(truindex, fastorInTRU); - - for (auto& bunch : chan.getBunches()) { - std::cout << "BunchLength: " << int(bunch.getBunchLength()) << std::endl; - auto adcs = bunch.getADC(); - int time = bunch.getStartTime(); - starttime = time; - timesamples.clear(); - timesamples.resize(adcs.size()); - std::copy(adcs.begin(), adcs.end(), timesamples.begin()); - trudata.Fill(); - for (int i = adcs.size() - 1; i >= 0; i--) { - std::cout << "Timebin " << time << ", ADC " << adcs[i] << std::endl; - time--; - } - } - } - } - } - reader.setNextTFToRead(++tfID); - } -} \ No newline at end of file diff --git a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx index 953b726fcb971..d5264aabc0566 100644 --- a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index cb3b13aa9b8e4..c62e013447416 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace fdd class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::FDD, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FDD, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h deleted file mode 100644 index 54c8b7b203edb..0000000000000 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h +++ /dev/null @@ -1,68 +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 ReadRaw.h -/// \brief Reads raw data and converts to digits -/// \author Maciej.Slupecki@cern.ch, arvind.khuntia@cern.ch, based on the FT0 code -// RAW data format description: DataFormat/Detectors/FIT/FDD/RawEventData - -#ifndef ALICEO2_FDD_READRAW_H_ -#define ALICEO2_FDD_READRAW_H_ - -#include -#include -#include -#include -#include -#include -#include -#include "TBranch.h" -#include "TTree.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "DataFormatsFDD/Digit.h" -#include "DataFormatsFDD/ChannelData.h" -#include "DataFormatsFDD/LookUpTable.h" -#include "DataFormatsFDD/RawEventData.h" - -namespace o2 -{ -namespace fdd -{ -class ReadRaw -{ - public: - ReadRaw() = default; - ReadRaw(bool doConversionToDigits, const std::string inputRawFilePath = "fdd.raw", const std::string outputRawFilePath = "fdddigitsFromRaw.root"); - void readRawData(const LookUpTable& lut); - void writeDigits(const std::string& outputDigitsFilePath); - void close(); - - private: - std::ifstream mRawFileIn; - std::map> mDigitAccum; // digit accumulator - - template - TBranch* getOrMakeBranch(TTree& tree, std::string brname, T* ptr) - { - if (auto br = tree.GetBranch(brname.c_str())) { - br->SetAddress(static_cast(ptr)); - return br; - } - // otherwise make it - return tree.Branch(brname.c_str(), ptr); - } - - ClassDefNV(ReadRaw, 1); -}; - -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h index 161b800a2c3ca..8881605b652ac 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/Reconstructor.h @@ -17,6 +17,7 @@ #include #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" namespace o2 { namespace fdd @@ -30,10 +31,16 @@ class Reconstructor gsl::span inChData, std::vector& RecPoints, std::vector& outChData); - void finish(); + void setDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) + { + LOG(info) << "Updated dead channel map"; + mDeadChannelMap = deadChannelMap; + } + private: + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(Reconstructor, 3); }; } // namespace fdd diff --git a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx index 3a87a11046a77..7d133e30df08e 100644 --- a/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx +++ b/Detectors/FIT/FDD/reconstruction/src/Reconstructor.cxx @@ -33,7 +33,12 @@ void Reconstructor::process(o2::fdd::Digit const& digitBC, gsl::spanisChannelAlive(inChData[ich].mPMNumber)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } bool inTime = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsEventInTVDC); bool inAdcGate = inChData[ich].getFlag(ChannelData::EEventDataBit::kIsCFDinADCgate); if (inAdcGate) { diff --git a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h b/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h deleted file mode 100644 index 4afcf5da37ae8..0000000000000 --- a/Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h +++ /dev/null @@ -1,74 +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 Digits2Raw.h -/// \brief converts digits to raw format -/// \author Maciej.Slupecki@cern.ch -// based on FV0 - -#ifndef ALICEO2_FDD_DIGITS2RAW_H_ -#define ALICEO2_FDD_DIGITS2RAW_H_ - -#include "Headers/RAWDataHeader.h" -#include "CommonDataFormat/InteractionRecord.h" -#include "DataFormatsFDD/RawEventData.h" -#include "DataFormatsFDD/LookUpTable.h" -#include "DataFormatsFDD/ChannelData.h" -#include "DataFormatsFDD/Digit.h" -#include "DetectorsRaw/HBFUtils.h" -#include "DetectorsRaw/RawFileWriter.h" -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace fdd -{ -class Digits2Raw -{ - public: - Digits2Raw() = default; - void readDigits(const std::string& outDir, const std::string& fileDigitsName); - void convertDigits(o2::fdd::Digit bcdigits, - gsl::span pmchannels, - const o2::fdd::LookUpTable& lut); - - o2::raw::RawFileWriter& getWriter() { return mWriter; } - void setFilePerLink(bool v) { mOutputPerLink = v; } - bool getFilePerLink() const { return mOutputPerLink; } - - int carryOverMethod(const header::RDHAny* rdh, const gsl::span data, - const char* ptr, int maxSize, int splitID, - std::vector& trailer, std::vector& header) const; - - private: - static constexpr uint32_t sTcmLink = 2; - static constexpr uint16_t sCruId = 0; - static constexpr uint32_t sEndPointId = sCruId; - - void makeGBTHeader(EventHeader& eventHeader, int link, o2::InteractionRecord const& mIntRecord); - void fillSecondHalfWordAndAddData(int iChannelPerLink, int prevPmLink, const o2::InteractionRecord& ir); - RawEventData mRawEventData; - o2::fdd::Triggers mTriggers; - o2::raw::RawFileWriter mWriter{"FDD"}; - bool mOutputPerLink = false; - ///////////////////////////////////////////////// - - ClassDefNV(Digits2Raw, 1); -}; - -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h deleted file mode 100644 index 6ed465b6181dd..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h +++ /dev/null @@ -1,61 +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 RawDataProcessSpec.h - -#ifndef O2_FDD_RAWDATAPROCESSSPEC_H -#define O2_FDD_RAWDATAPROCESSSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FDDRaw/DigitBlockFDD.h" -#include "DataFormatsFDD/Digit.h" -#include "DataFormatsFDD/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -class RawDataProcessSpec : public Task -{ - public: - RawDataProcessSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~RawDataProcessSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; -}; - -framework::DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor); - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h deleted file mode 100644 index c3b0349826e98..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h +++ /dev/null @@ -1,84 +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 RawDataReaderSpec.h - -#ifndef O2_FDD_RAWDATAREADERSPEC_H -#define O2_FDD_RAWDATAREADERSPEC_H - -#include "DataFormatsFDD/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "DetectorsRaw/RDHUtils.h" - -#include -#include -#include -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -template -class RawDataReaderSpec : public Task -{ - public: - RawDataReaderSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - RawDataReaderSpec() = default; - ~RawDataReaderSpec() override = default; - void init(InitContext& ic) final { o2::fdd::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - DPLRawParser parser(pc.inputs()); - mRawReader.clear(); - LOG(info) << "FDD RawDataReaderSpec"; - uint64_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), int(0)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - } - RawReader mRawReader; -}; - -template -framework::DataProcessorSpec getFDDRawDataReaderSpec(const RawReader& rawReader) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFDD"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - return DataProcessorSpec{ - "fdd-datareader-dpl", - o2::framework::select("TF:FDD/RAWDATA"), - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace fdd -} // namespace o2 - -#endif /* O2_FDDDATAREADERDPL_H */ diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h deleted file mode 100644 index 3bbab66d16497..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h +++ /dev/null @@ -1,28 +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_FDD_RAWWORKFLOW_H -#define O2_FDD_RAWWORKFLOW_H - -/// @file RawWorkflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace fdd -{ -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut); -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h index 2dbd854e34eee..0d5d308216bb0 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fdd } // namespace o2 #endif diff --git a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h index 7dcb5d9aaba40..8f20ff1513ab4 100644 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h +++ b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/ReconstructorSpec.h @@ -18,6 +18,8 @@ #include "Framework/Task.h" #include "FDDReconstruction/Reconstructor.h" #include "DataFormatsFDD/RecPoint.h" +#include "DataFormatsFIT/DeadChannelMap.h" +#include "Framework/ConcreteDataMatcher.h" using namespace o2::framework; @@ -29,21 +31,25 @@ namespace fdd class FDDReconstructorDPL : public Task { public: - FDDReconstructorDPL(bool useMC) : mUseMC(useMC) {} + FDDReconstructorDPL(bool useMC, bool useDeadChannelMap) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap) {} ~FDDReconstructorDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: bool mUseMC = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; std::vector mRecPoints; std::vector mRecChData; + o2::fit::DeadChannelMap const* mDeadChannelMap; o2::fdd::Reconstructor mReco; o2::header::DataOrigin mOrigin = o2::header::gDataOriginFDD; }; /// create a processor spec -framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true); +framework::DataProcessorSpec getFDDReconstructorSpec(bool useMC = true, bool useDeadChannelMap = true); } // namespace fdd } // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx deleted file mode 100644 index bf18db67672c2..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx +++ /dev/null @@ -1,52 +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 RawDataProcessSpec.cxx - -#include "FDDWorkflow/RawDataProcessSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -using namespace std; -void RawDataProcessSpec::init(InitContext& ic) -{ -} - -void RawDataProcessSpec::run(ProcessingContext& pc) -{ - LOG(info) << "RawDataProcessSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFDD::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getRawDataProcessSpec"; - return DataProcessorSpec{ - "fdd-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx deleted file mode 100644 index 631655d3038ec..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx +++ /dev/null @@ -1,24 +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 RawDataReaderSpec.cxx - -#include "FDDWorkflow/RawDataReaderSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx deleted file mode 100644 index c9e5c5be0c81d..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx +++ /dev/null @@ -1,42 +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 RawWorkflow.cxx - -#include "FDDWorkflow/RawWorkflow.h" -#include "FDDWorkflow/RawDataProcessSpec.h" -#include "FDDWorkflow/RawDataReaderSpec.h" -#include "FDDWorkflow/DigitWriterSpec.h" -#include "FDDWorkflow/RawReaderFDD.h" -namespace o2 -{ -namespace fdd -{ - -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut) -{ - LOG(info) << "framework::WorkflowSpec getFDDWorkflow"; - framework::WorkflowSpec specs; - specs.emplace_back(o2::fdd::getFDDRawDataReaderSpec(RawReaderFDD{dumpReader})); - - if (useProcess) { - specs.emplace_back(o2::fdd::getFDDRawDataProcessSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::fdd::getFDDDigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx index a7d4c15af81bb..b464e689f7a75 100644 --- a/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,14 @@ namespace o2 namespace fdd { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fdd::getFDDDigitReaderSpec(useMC)); } - specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC)); + specs.emplace_back(o2::fdd::getFDDReconstructorSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fdd::getFDDRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx index b7a0b9876a2ee..1d5d599b5ee31 100644 --- a/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/ReconstructorSpec.cxx @@ -18,6 +18,7 @@ #include "FDDWorkflow/ReconstructorSpec.h" #include "DataFormatsFDD/Digit.h" #include "DataFormatsFDD/MCLabel.h" +#include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -44,6 +45,11 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) // lblPtr = labels.get(); LOG(info) << "Ignoring MC info"; } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(info) << "Populating reconsturctor object with Dead Channel Map object"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.setDeadChannelMap(deadChannelMap.get()); + } int nDig = digitsBC.size(); mRecPoints.reserve(nDig); mRecChData.reserve(digitsCh.size()); @@ -58,16 +64,29 @@ void FDDReconstructorDPL::run(ProcessingContext& pc) pc.outputs().snapshot(Output{mOrigin, "RECCHDATA", 0}, mRecChData); } -DataProcessorSpec getFDDReconstructorSpec(bool useMC) +void FDDReconstructorDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("FDD", "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + return; + } +} + +DataProcessorSpec getFDDReconstructorSpec(bool useMC, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digitsBC", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digitsCh", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently FDDReconstructor does not consume and provide MC truth"; // inputSpec.emplace_back("labels", o2::header::gDataOriginFDD, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFDD, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FDD/Calib/DeadChannelMap")); + } outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFDD, "RECCHDATA", 0, Lifetime::Timeframe); @@ -75,7 +94,7 @@ DataProcessorSpec getFDDReconstructorSpec(bool useMC) "fdd-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx index 0e43c6e3c4ba0..b83e25557e760 100644 --- a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx index 652ddb8bd2a29..888792425909b 100644 --- a/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/fdd-reco-workflow.cxx @@ -38,6 +38,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -57,8 +58,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); - auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fdd::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FT0/calibration/CMakeLists.txt b/Detectors/FIT/FT0/calibration/CMakeLists.txt index d103b4a9a18b6..bee0493d300c1 100644 --- a/Detectors/FIT/FT0/calibration/CMakeLists.txt +++ b/Detectors/FIT/FT0/calibration/CMakeLists.txt @@ -10,26 +10,50 @@ # or submit itself to any jurisdiction. o2_add_library(FT0Calibration - SOURCES - src/FT0TimeOffsetSlotContainer.cxx - PUBLIC_LINK_LIBRARIES - O2::DataFormatsFT0 - O2::CommonDataFormat - O2::DetectorsCalibration - ) - o2_target_root_dictionary(FT0Calibration - HEADERS - include/FT0Calibration/FT0TimeOffsetSlotContainer.h - ) - o2_add_executable(ft0-time-offset-calib - COMPONENT_NAME calibration - SOURCES workflow/FT0TimeOffsetCalibration-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration O2::FITCalibration - ) - o2_add_executable(ft0-time-spectra-processor - COMPONENT_NAME calibration - SOURCES workflow/FT0TimeSpectraProcessor-Workflow.cxx - PUBLIC_LINK_LIBRARIES - O2::FT0Calibration - ) + SOURCES + src/FT0TimeOffsetSlotContainer.cxx + src/EventsPerBcCalibrator.cxx + PUBLIC_LINK_LIBRARIES + O2::DetectorsCalibration + O2::Framework + O2::CommonUtils + Microsoft.GSL::GSL + O2::DataFormatsFT0 + O2::CommonDataFormat + O2::Steer + O2::CCDB + ROOT::Minuit + ROOT::Hist + ) + +o2_target_root_dictionary(FT0Calibration + HEADERS + include/FT0Calibration/FT0TimeOffsetSlotContainer.h + include/FT0Calibration/EventsPerBcCalibrator.h + ) + +o2_add_executable(ft0-time-offset-calib + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeOffsetCalibration-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration O2::FITCalibration + ) + +o2_add_executable(ft0-time-spectra-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0TimeSpectraProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + ) + +o2_add_executable(ft0-events-per-bc-processor + COMPONENT_NAME calibration + SOURCES + workflow/FT0EventsPerBcProcessor-Workflow.cxx + PUBLIC_LINK_LIBRARIES + O2::FT0Calibration + O2::Framework + O2::CCDB +) \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/README.md b/Detectors/FIT/FT0/calibration/README.md new file mode 100644 index 0000000000000..78b0f980400d2 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/README.md @@ -0,0 +1,62 @@ +# Calibrations + +## Events per BC Calibration +### Description +Generates histograms of **Events per Bunch Crossing (BC)**. Events can be filtered by applying amplitude thresholds to the **A-side** and **C-side**. + +### Command-Line Options +| Option | Default | Description | +| :--- | :--- | :--- | +| `--slot-len-sec` | `3600` | Duration of each slot in seconds. | +| `--slot-len-tf` | `0` | Slot length in Time Frames (TFs). | +| `--one-object-per-run` | — | If set, the workflow creates only one calibration object per run. | +| `--min-entries-number` | `0` | Minimum number of entries required for a slot to be valid. | +| `--min-ampl-side-a` | `-2147483648` | Amplitude threshold for Side A events. | +| `--min-ampl-side-c` | `-2147483648` | Amplitude threshold for Side C events. | + +--- + +## How to Run + +### Simulation Data +First, it is important to digitize data with a non-zero run number, orbit, and timestamp. To set these parameters, one can use the `--configKeyValues` option, as shown in the example below. +``` +o2-sim-digitizer-workflow \ +--onlyDet FT0 \ +--configKeyValues="HBFUtils.nHBFPerTF=128;HBFUtils.orbitFirst=128;HBFUtils.orbitFirstSampled=256;HBFUtils.runNumber=560560;HBFUtils.startTime=1768464099000" +``` + +To process simulation data, digits must first be converted to RAW format. The `o2-ft0-digi2raw` tool performs this conversion and generates the required configuration file. + +Once converted, you can run the calibration either as a single integrated workflow or by spawning as the sender and receiver components separately. + +#### Single Workflow Example +Execute the following command within the simulation directory: +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080 +``` + +Sender example (in simulation directory): +``` +o2-raw-file-reader-workflow --input-conf FT0raw.cfg --loop -1 \ +| o2-ft0-flp-dpl-workflow --condition-backend=http://localhost:8080 \ +| o2-dpl-output-proxy --channel-config "name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq" --dataspec "downstream:FT0/DIGITSBC" +``` + +Receiver example: +``` +o2-dpl-raw-proxy --channel-config "name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq" --dataspec "A:FT0/DIGITSBC/0" \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10 --min-ampl-side-a=0" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` + +### CTF Data +Example: +``` +o2-ctf-reader-workflow --ctf-input ctf.root --onlyDet FT0 \ +| o2-calibration-ft0-events-per-bc-processor --FT0EventsPerBcProcessor "--slot-len-sec=10" \ +| o2-calibration-ccdb-populator-workflow --ccdb-path=http://localhost:8080/ +``` \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h new file mode 100644 index 0000000000000..d831cc36201ab --- /dev/null +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -0,0 +1,83 @@ +// 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_FT0TVXPERBCID +#define O2_FT0TVXPERBCID + +#include +#include +#include +#include + +#include "CommonDataFormat/FlatHisto2D.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsFT0/SpectraInfoObject.h" +#include "DataFormatsFT0/Digit.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "DetectorsCalibration/TimeSlotCalibration.h" +#include "DetectorsCalibration/TimeSlot.h" +#include "CommonDataFormat/TFIDInfo.h" +#include "TH1F.h" +#include "Rtypes.h" + +namespace o2::ft0 +{ +struct EventsPerBcContainer { + EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) {} + + size_t getEntries() const { return entries; } + void print() const; + void fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data); + void merge(const EventsPerBcContainer* prev); + + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::array mTvx{0.0}; + size_t entries{0}; + long startTimeStamp{0}; + long stopTimeStamp{0}; + + ClassDefNV(EventsPerBcContainer, 1); +}; + +class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration +{ + using Slot = o2::calibration::TimeSlot; + using TFType = o2::calibration::TFType; + using EventsHistogram = std::array; + + public: + EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude); + + bool hasEnoughData(const Slot& slot) const override; + void initOutput() override; + void finalizeSlot(Slot& slot) override; + Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) override; + + const std::vector& getTvxPerBc() { return mTvxPerBcs; } + std::vector>& getTvxPerBcCcdbInfo() { return mTvxPerBcInfos; } + + private: + const uint32_t mMinNumberOfEntries; + const int32_t mMinAmplitudeSideA; + const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; + + std::vector mTvxPerBcs; + std::vector> mTvxPerBcInfos; + + ClassDefOverride(EventsPerBcCalibrator, 1); +}; +} // namespace o2::ft0 + +#endif diff --git a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx new file mode 100644 index 0000000000000..b17c81213cd08 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx @@ -0,0 +1,81 @@ +// 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 "FT0Calibration/EventsPerBcCalibrator.h" +#include "CommonUtils/MemFileHelper.h" + +namespace o2::ft0 +{ +void EventsPerBcContainer::print() const +{ + LOG(info) << entries << " entries"; +} + +void EventsPerBcContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl::span data) +{ + size_t oldEntries = entries; + for (const auto& digit : data) { + if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC && (digit.mTriggers.getAmplA() + digit.mTriggers.getAmplC()) >= mMinSumOfAmplitude) { + mTvx[digit.mIntRecord.bc]++; + entries++; + } + } + LOG(debug) << "Container is filled with " << entries - oldEntries << " new events"; +} + +void EventsPerBcContainer::merge(const EventsPerBcContainer* prev) +{ + for (int bc = 0; bc < o2::constants::lhc::LHCMaxBunches; bc++) { + mTvx[bc] += prev->mTvx[bc]; + } + entries += prev->entries; +} + +void EventsPerBcCalibrator::initOutput() +{ + mTvxPerBcs.clear(); + mTvxPerBcInfos.clear(); +} + +EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC), mMinSumOfAmplitude(minSumOfAmplitude) +{ + LOG(info) << "Defined threshold for number of entires per slot: " << mMinNumberOfEntries; + LOG(info) << "Defined threshold for side A amplitude for event: " << mMinAmplitudeSideA; + LOG(info) << "Defined threshold for side C amplitude for event: " << mMinAmplitudeSideC; +} + +bool EventsPerBcCalibrator::hasEnoughData(const EventsPerBcCalibrator::Slot& slot) const +{ + return slot.getContainer()->entries > mMinNumberOfEntries; +} + +void EventsPerBcCalibrator::finalizeSlot(EventsPerBcCalibrator::Slot& slot) +{ + LOG(info) << "Finalizing slot from " << slot.getStartTimeMS() << " to " << slot.getEndTimeMS(); + o2::ft0::EventsPerBcContainer* data = slot.getContainer(); + mTvxPerBcs.emplace_back(data->mTvx); + + auto clName = o2::utils::MemFileHelper::getClassName(mTvxPerBcs.back()); + auto flName = o2::ccdb::CcdbApi::generateFileName(clName); + + std::map metaData; + mTvxPerBcInfos.emplace_back(std::make_unique("FT0/Calib/EventsPerBc", clName, flName, metaData, slot.getStartTimeMS(), slot.getEndTimeMS())); + LOG(info) << "Created object valid from " << mTvxPerBcInfos.back()->getStartValidityTimestamp() << " to " << mTvxPerBcInfos.back()->getEndValidityTimestamp(); +} + +EventsPerBcCalibrator::Slot& EventsPerBcCalibrator::emplaceNewSlot(bool front, TFType tstart, TFType tend) +{ + auto& cont = getSlots(); + auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); + slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude)); + return slot; +} +} // namespace o2::ft0 \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h index 49f72e8cbdfff..11b1ce25e9353 100644 --- a/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h +++ b/Detectors/FIT/FT0/calibration/src/FT0CalibrationLinkDef.h @@ -16,7 +16,9 @@ #pragma link off all functions; #pragma link C++ class o2::ft0::FT0TimeOffsetSlotContainer + ; +#pragma link C++ class o2::ft0::EventsPerBcCalibrator + ; #pragma link C++ class o2::calibration::TimeSlot < o2::ft0::FT0TimeOffsetSlotContainer>; #pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::FT0TimeOffsetSlotContainer>; - +#pragma link C++ class o2::calibration::TimeSlot < o2::ft0::EventsPerBcContainer> + ; +#pragma link C++ class o2::calibration::TimeSlotCalibration < o2::ft0::EventsPerBcContainer> + ; #endif diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx new file mode 100644 index 0000000000000..5cef707da2cca --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.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. + +#include "FT0EventsPerBcSpec.h" +#include "Framework/Lifetime.h" +#include + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& cfgc) +{ + using namespace o2::framework; + using o2::calibration::FT0EventsPerBcProcessor; + std::vector inputs; + inputs.emplace_back("digits", "FT0", "DIGITSBC", Lifetime::Timeframe); + auto ccdbRequest = std::make_shared(true, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs); + std::vector outputs; + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc"}, Lifetime::Timeframe); + outputs.emplace_back(ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, "EventsPerBc"}, Lifetime::Timeframe); + DataProcessorSpec dataProcessorSpec{ + "FT0EventsPerBcProcessor", + inputs, + outputs, + AlgorithmSpec(adaptFromTask(ccdbRequest)), + Options{ + {"slot-len-sec", VariantType::UInt32, 3600u, {"Duration of each slot in seconds"}}, + {"one-object-per-run", VariantType::Bool, false, {"If set, workflow creates only one calibration object per run"}}, + {"min-entries-number", VariantType::UInt32, 5000u, {"Minimum number of entries required for a slot to be valid"}}, + {"min-ampl-side-a", VariantType::Int, 0, {"Amplitude threshold for Side A events"}}, + {"min-ampl-side-c", VariantType::Int, 0, {"Amplitude threshold for Side C events"}}, + {"min-sum-of-ampl", VariantType::Int, 0, {"Amplitude threshold for sum of A-side and C-side amplitudes"}}}}; + + WorkflowSpec workflow; + workflow.emplace_back(dataProcessorSpec); + return workflow; +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h new file mode 100644 index 0000000000000..d493e2a606613 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -0,0 +1,128 @@ +// 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_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H +#define O2_CALIBRATION_FT0_EVENTS_PER_BC_CALIBRATOR_H + +#include "Framework/runDataProcessing.h" +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include +#include "Framework/DeviceSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/Task.h" +#include "DetectorsCalibration/Utils.h" +#include "DetectorsBase/GRPGeomHelper.h" + +#include "DataFormatsFT0/Digit.h" +#include "FT0Calibration/EventsPerBcCalibrator.h" + +namespace o2::calibration +{ +class FT0EventsPerBcProcessor final : public o2::framework::Task +{ + public: + FT0EventsPerBcProcessor(std::shared_ptr request) : mCCDBRequest(request) {} + + void init(o2::framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + if (ic.options().hasOption("slot-len-sec")) { + mSlotLenSec = ic.options().get("slot-len-sec"); + } + if (ic.options().hasOption("one-object-per-run")) { + mOneObjectPerRun = ic.options().get("one-object-per-run"); + } + if (ic.options().hasOption("min-entries-number")) { + mMinNumberOfEntries = ic.options().get("min-entries-number"); + } + if (ic.options().hasOption("min-ampl-side-a")) { + mMinAmplitudeSideA = ic.options().get("min-ampl-side-a"); + } + if (ic.options().hasOption("min-ampl-side-c")) { + mMinAmplitudeSideC = ic.options().get("min-ampl-side-c"); + } + if (ic.options().hasOption("min-sum-of-ampl")) { + mMinSumOfAmplitude = ic.options().get("min-sum-of-ampl"); + } + + mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude); + + if (mOneObjectPerRun) { + LOG(info) << "Only one object will be created at the end of run"; + mCalibrator->setUpdateAtTheEndOfRunOnly(); + } + if (mOneObjectPerRun == false) { + LOG(info) << "Defined slot interval to " << mSlotLenSec << " seconds"; + mCalibrator->setSlotLengthInSeconds(mSlotLenSec); + } + } + + void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) + { + o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + } + + void run(o2::framework::ProcessingContext& pc) final + { + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + auto digits = pc.inputs().get>("digits"); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); + if (digits.size() == 0) { + return; + } + mCalibrator->process(digits); + if (mOneObjectPerRun == false) { + sendOutput(pc.outputs()); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOG(info) << "Received end-of-stream, checking for slot to finalize..."; + mCalibrator->checkSlotsToFinalize(); + sendOutput(ec.outputs()); + mCalibrator->initOutput(); + } + + void sendOutput(o2::framework::DataAllocator& output) + { + using o2::framework::Output; + const auto& tvxHists = mCalibrator->getTvxPerBc(); + auto& infos = mCalibrator->getTvxPerBcCcdbInfo(); + for (unsigned int idx = 0; idx < tvxHists.size(); idx++) { + auto& info = infos[idx]; + const auto& payload = tvxHists[idx]; + + auto image = o2::ccdb::CcdbApi::createObjectImage(&payload, info.get()); + 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, "EventsPerBc", idx}, *image.get()); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, "EventsPerBc", idx}, *info.get()); + } + + if (tvxHists.size()) { + mCalibrator->initOutput(); + } + } + + private: + std::shared_ptr mCCDBRequest; + std::unique_ptr mCalibrator; + bool mOneObjectPerRun; + uint32_t mSlotLenSec; + uint32_t mMinNumberOfEntries; + int32_t mMinAmplitudeSideA; + int32_t mMinAmplitudeSideC; + int32_t mMinSumOfAmplitude; +}; +} // namespace o2::calibration +#endif \ No newline at end of file diff --git a/Detectors/FIT/FT0/macros/CMakeLists.txt b/Detectors/FIT/FT0/macros/CMakeLists.txt index c4ed27d2513ba..17491ca4962c1 100644 --- a/Detectors/FIT/FT0/macros/CMakeLists.txt +++ b/Detectors/FIT/FT0/macros/CMakeLists.txt @@ -1,14 +1,21 @@ -# Copyright CERN and copyright holders of ALICE O2. This software is distributed -# under the terms of the GNU General Public License v3 (GPL Version 3), copied -# verbatim in the file "COPYING". +# 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. # -# See http://alice-o2.web.cern.ch/license for full licensing information. +# 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_test_root_macro(FT0Misaligner.C PUBLIC_LINK_LIBRARIES O2::CCDB O2::FT0Simulation LABELS ft0) + +o2_add_test_root_macro(FT0readEventsPerBc.C + PUBLIC_LINK_LIBRARIES + O2::CCDB + O2::DataFormatsFT0 + LABELS ft0) diff --git a/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C new file mode 100644 index 0000000000000..c6afc86389b9b --- /dev/null +++ b/Detectors/FIT/FT0/macros/FT0readEventsPerBc.C @@ -0,0 +1,52 @@ +// 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 +#endif + +#include "CCDB/CcdbApi.h" +#include "CCDB/CCDBTimeStampUtils.h" +#include "TH1F.h" +#include "DataFormatsFT0/EventsPerBc.h" +#include "Framework/Logger.h" +#include "CommonConstants/LHCConstants.h" + +std::unique_ptr hist; +std::unique_ptr canvas; + +void FT0readEventsPerBc(std::string ccdbUrl, long timestamp) +{ + o2::ccdb::CcdbApi ccdbApi; + ccdbApi.init(ccdbUrl); + const std::string ccdbPath = "FT0/Calib/EventsPerBc"; + std::map metadata; + + if (timestamp < 0) { + timestamp = o2::ccdb::getCurrentTimestamp(); + } + + std::unique_ptr events(ccdbApi.retrieveFromTFileAny(ccdbPath, metadata, timestamp)); + + if (!events) { + LOGP(fatal, "EventsPerBc object not found in {}/{} for timestamp {}.", ccdbUrl, ccdbPath, timestamp); + return; + } + + hist = std::make_unique("eventsPerBcHist", "Events per BC", o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches - 1); + for (int idx = 0; idx < o2::constants::lhc::LHCMaxBunches; idx++) { + hist->Fill(idx, events->histogram[idx]); + } + canvas = std::make_unique(); + hist->Draw(); + canvas->Draw(); +} \ No newline at end of file diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 5c2e0f0627ef1..5dc367204e1a3 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ft0 class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::FT0, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FT0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h index ff3f8384f488d..9f6cd500b9e74 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CollisionTimeRecoTask.h @@ -21,6 +21,7 @@ #include "DataFormatsFT0/FT0ChannelTimeCalibrationObject.h" #include "DataFormatsFT0/SpectraInfoObject.h" #include "DataFormatsFT0/SlewingCoef.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include #include #include @@ -57,10 +58,16 @@ class CollisionTimeRecoTask LOG(info) << "Init for slewing calib object"; mCalibSlew = calibSlew->makeSlewingPlots(); }; + void SetDeadChannelMap(const o2::fit::DeadChannelMap* deadChannelMap) + { + LOG(info) << "Updated dead channel map for CollisionTimeRecoTask"; + mDeadChannelMap = deadChannelMap; + } float getTimeInPS(const o2::ft0::ChannelData& channelData); private: o2::ft0::TimeSpectraInfoObject const* mTimeCalibObject = nullptr; + const o2::fit::DeadChannelMap* mDeadChannelMap = nullptr; typename o2::ft0::SlewingCoef::SlewingPlots_t mCalibSlew{}; }; } // namespace ft0 diff --git a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx index 7363cef57cf31..3e3ffe52671e9 100644 --- a/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx +++ b/Detectors/FIT/FT0/reconstruction/src/CollisionTimeRecoTask.cxx @@ -67,6 +67,10 @@ RP CollisionTimeRecoTask::processDigit(const o2::ft0::Digit& digit, // Reference channels shouldn't participate in reco at all! continue; } + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(channelData.ChId)) { + LOG(debug) << "Channel " << channelData.ChId << " is dead - discarding data"; + continue; + } const float timeInPS = getTimeInPS(channelData); if (ChannelFilterParam::Instance().checkAll(channelData)) { outChData.emplace_back(channelData.ChId, timeInPS, (float)channelData.QTCAmpl, channelData.ChainQTC); diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h deleted file mode 100644 index 7b7e98d50368e..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h +++ /dev/null @@ -1,61 +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 FT0DataProcessDPLSpec.h - -#ifndef O2_FT0DATAPROCESSDPLSPEC_H -#define O2_FT0DATAPROCESSDPLSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FT0Raw/DigitBlockFT0.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0DataProcessDPLSpec : public Task -{ - public: - FT0DataProcessDPLSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~FT0DataProcessDPLSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; -}; - -framework::DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h deleted file mode 100644 index 9074f4f7f0f34..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h +++ /dev/null @@ -1,110 +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 FT0DataReaderDPLSpec.h - -#ifndef O2_FT0DATAREADERDPLSPEC_H -#define O2_FT0DATAREADERDPLSPEC_H -#include "DataFormatsFT0/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "Framework/InputRecordWalker.h" -#include -#include -#include -#include "CommonUtils/VerbosityConfig.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -template -class FT0DataReaderDPLSpec : public Task -{ - public: - FT0DataReaderDPLSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - FT0DataReaderDPLSpec() = default; - ~FT0DataReaderDPLSpec() override = default; - typedef RawReader RawReader_t; - void init(InitContext& ic) final { o2::ft0::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" - // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow - { - static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously - std::vector dummy{InputSpec{"dummy", ConcreteDataMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData, 0xDEADBEEF}}}; - for (const auto& ref : InputRecordWalker(pc.inputs(), dummy)) { - const auto dh = o2::framework::DataRefUtils::getHeader(ref); - auto payloadSize = DataRefUtils::getPayloadSize(ref); - if (payloadSize == 0) { - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; - if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", - dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, - contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); - } - mRawReader.makeSnapshot(pc); // send empty output - return; - } - } - contDeadBeef = 0; // if good data, reset the counter - } - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData}, Lifetime::Timeframe}}; - DPLRawParser parser(pc.inputs(), filter); - std::size_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - mRawReader.clear(); - } - RawReader_t mRawReader; -}; - -template -framework::DataProcessorSpec getFT0DataReaderDPLSpec(const RawReader& rawReader, bool askSTFDist) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFT0"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, "RAWDATA"}, Lifetime::Timeframe}}; - if (askSTFDist) { - inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); - } - return DataProcessorSpec{ - "ft0-datareader-dpl", - inputSpec, - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAREADERDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h deleted file mode 100644 index f7729394db652..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h +++ /dev/null @@ -1,156 +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 RawReaderFT0.h class for RAW data reading -// -// Artur.Furs -// afurs@cern.ch -// -//Main purpuse is to decode FT0 data blocks and push them to DigitBlockFT0 for proccess -//TODO: prepare wrappers for containers with digits and combine classes below into one template class? -#ifndef ALICEO2_FIT_RAWREADERFT0_H_ -#define ALICEO2_FIT_RAWREADERFT0_H_ -#include -#include -#include -#include "FT0Raw/RawReaderFT0Base.h" - -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include "Framework/ProcessingContext.h" -#include "Framework/DataAllocator.h" -#include "Framework/OutputSpec.h" -#include - -namespace o2 -{ -namespace ft0 -{ -//Normal TCM mode -template -class RawReaderFT0 : public RawReaderFT0BaseNorm -{ - public: - RawReaderFT0(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0(const RawReaderFT0&) = default; - - RawReaderFT0() = default; - ~RawReaderFT0() = default; - static constexpr bool sUseTrgInput = useTrgInput; - void clear() - { - mVecDigits.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - mVecChannelData.clear(); - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - if constexpr (sUseTrgInput) { - LOG(info) << "Number of TriggerInput: " << mVecTriggerInput.size(); - } - if (mDumpData) { - DigitBlockFT0::print(mVecDigits, mVecChannelData); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecTriggerInput; - std::vector mVecChannelData; -}; - -//Extended TCM mode (additional raw data struct) -template -class RawReaderFT0ext : public RawReaderFT0BaseExt -{ - public: - RawReaderFT0ext(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0ext(const RawReaderFT0ext&) = default; - static constexpr bool sUseTrgInput = useTrgInput; - RawReaderFT0ext() = default; - ~RawReaderFT0ext() = default; - void clear() - { - mVecDigits.clear(); - mVecChannelData.clear(); - mVecTrgExt.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - LOG(info) << "Number of TriggerExt: " << mVecTrgExt.size(); - if (mDumpData) { - DigitBlockFT0ext::print(mVecDigits, mVecChannelData, mVecTrgExt); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0}, mVecTrgExt); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecChannelData; - std::vector mVecTrgExt; - std::vector mVecTriggerInput; -}; - -} // namespace ft0 -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h index 3c6e4599a250c..6de23a1c66bfd 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 #endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h index 1c671352e6ba7..307b2109fe35f 100644 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FT0/workflow/include/FT0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::ft0::Geometry::Nchannels; public: - ReconstructionDPL(bool useMC, const std::string& ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib) : mUseMC(useMC), mCCDBpath(ccdbpath), mUseTimeOffsetCalib(useTimeOffsetCalib), mUseSlewingCalib(useSlewingCalib) {} + ReconstructionDPL(bool useMC, const std::string& ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) : mUseMC(useMC), mCCDBpath(ccdbpath), mUseTimeOffsetCalib(useTimeOffsetCalib), mUseSlewingCalib(useSlewingCalib), mUseDeadChannelMap(useDeadChannelMap) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -46,6 +46,8 @@ class ReconstructionDPL : public Task bool mUpdateCCDB = true; bool mUseTimeOffsetCalib = true; bool mUseSlewingCalib = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -55,7 +57,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch", bool useTimeOffsetCalib = true, bool useSlewingCalib = true); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch", bool useTimeOffsetCalib = true, bool useSlewingCalib = true, bool useDeadChannelMap = true); } // namespace ft0 } // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx deleted file mode 100644 index d7a7a689d402f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx +++ /dev/null @@ -1,52 +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 FT0DataProcessDPLSpec.cxx - -#include "FT0Workflow/FT0DataProcessDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -using namespace std; -void FT0DataProcessDPLSpec::init(InitContext& ic) -{ -} - -void FT0DataProcessDPLSpec::run(ProcessingContext& pc) -{ - LOG(info) << "FT0DataProcessDPLSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFT0::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getFT0DataProcessDPLSpec"; - return DataProcessorSpec{ - "ft0-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx b/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx deleted file mode 100644 index 156feb7dd3e2f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx +++ /dev/null @@ -1,45 +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 FT0Workflow.cxx - -#include "FT0Workflow/FT0Workflow.h" -#include "FT0Workflow/FT0DataProcessDPLSpec.h" -#include "FT0Workflow/FT0DataReaderDPLSpec.h" -#include "FT0Workflow/FT0DigitWriterSpec.h" -#include "FT0Workflow/RawReaderFT0.h" -namespace o2 -{ -namespace ft0 -{ - -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist) -{ - LOG(info) << "framework::WorkflowSpec getFT0Workflow"; - framework::WorkflowSpec specs; - if (isExtendedMode) { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0ext{dumpReader}, askSTFDist)); - } else { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0{dumpReader}, askSTFDist)); - } - if (useProcess) { - specs.emplace_back(o2::ft0::getFT0DataProcessDPLSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::ft0::getFT0DigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx b/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx deleted file mode 100644 index b2ef17e540112..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx +++ /dev/null @@ -1,13 +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 "FT0Workflow/RawReaderFT0.h" -using namespace o2::ft0; diff --git a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx index 247158164ac3b..2231011febd7f 100644 --- a/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/RecoWorkflow.cxx @@ -22,13 +22,13 @@ namespace o2 namespace ft0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::ft0::getDigitReaderSpec(useMC)); } - specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib)); + specs.emplace_back(o2::ft0::getReconstructionSpec(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::ft0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx index 40bc96ebca58e..bc5217c8d7471 100644 --- a/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/ReconstructionSpec.cxx @@ -44,6 +44,7 @@ void ReconstructionDPL::init(InitContext& ic) LOG(info) << "FT0 param mMinRMS: " << CalibParam::Instance().mMinRMS; LOG(info) << "FT0 param mMaxSigma: " << CalibParam::Instance().mMaxSigma; LOG(info) << "FT0 param mMaxDiffMean: " << CalibParam::Instance().mMaxDiffMean; + LOG(info) << "FT0 dead channel map will be applied " << mUseDeadChannelMap; } void ReconstructionDPL::run(ProcessingContext& pc) @@ -69,6 +70,12 @@ void ReconstructionDPL::run(ProcessingContext& pc) mReco.SetSlewingCalibObject(slewingCalibObject.get()); } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + LOG(debug) << "Applying dead channel map"; + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); + } + mRecPoints.reserve(digits.size()); mRecChData.reserve(channels.size()); mReco.processTF(digits, channels, mRecPoints, mRecChData); @@ -91,6 +98,11 @@ void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) mUseSlewingCalib = false; // upload only once, slewing should be stable during the run return; } + if (matcher == ConcreteDataMatcher("FT0", "DeadChannelMap", 0)) { + LOG(debug) << "New DeadChannelMap is uploaded"; + mUpdateDeadChannelMap = false; + return; + } } void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) @@ -99,12 +111,13 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib) +DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, bool useTimeOffsetCalib, bool useSlewingCalib, bool useDeadChannelMap) { std::vector inputSpec; std::vector outputSpec; inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); + if (useMC) { LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFT0, "DIGITSMCTR", 0, Lifetime::Timeframe); @@ -121,6 +134,11 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, ccdbParamSpec("FT0/Calib/SlewingCoef")); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFT0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FT0/Calib/DeadChannelMap")); + } + outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECPOINTS", 0, Lifetime::Timeframe); outputSpec.emplace_back(o2::header::gDataOriginFT0, "RECCHDATA", 0, Lifetime::Timeframe); @@ -128,7 +146,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath, "ft0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib)}, + AlgorithmSpec{adaptFromTask(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, useDeadChannelMap)}, Options{}}; } diff --git a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx index 2b4a86df0a614..144d27abeda9c 100644 --- a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx index 3e6a6bf5da090..ab39068aedb38 100644 --- a/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/ft0-reco-workflow.cxx @@ -41,7 +41,8 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, {"disable-time-offset-calib", o2::framework::VariantType::Bool, false, {"disable timeoffset calibration"}}, {"disable-slewing-calib", o2::framework::VariantType::Bool, false, {"disable slewing calibration"}}, - {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"disable-dead-channel-map", VariantType::Bool, false, {"disable dead channel map"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -64,9 +65,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto disableRootOut = configcontext.options().get("disable-root-output"); const auto useTimeOffsetCalib = !configcontext.options().get("disable-time-offset-calib"); const auto useSlewingCalib = !configcontext.options().get("disable-slewing-calib"); + const auto useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC << " CCDB " << ccdbpath; - auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, disableRootInp, disableRootOut); + auto wf = o2::ft0::getRecoWorkflow(useMC, ccdbpath, useTimeOffsetCalib, useSlewingCalib, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h index 12d89b82a13cc..c5cb5b0da6d05 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/BaseRecoTask.h @@ -18,6 +18,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/RecPoints.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include namespace o2 @@ -33,14 +34,15 @@ class BaseRecoTask ~BaseRecoTask() = default; o2::fv0::RecPoints process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData); + std::vector& outChData); void FinishTask(); void SetChannelOffset(o2::fv0::FV0ChannelTimeCalibrationObject const* caliboffsets) { mCalibOffset = caliboffsets; }; + void SetDeadChannelMap(o2::fit::DeadChannelMap const* deadChannelMap) { mDeadChannelMap = deadChannelMap; } int getOffset(int channel); private: o2::fv0::FV0ChannelTimeCalibrationObject const* mCalibOffset = nullptr; - + o2::fit::DeadChannelMap const* mDeadChannelMap = nullptr; ClassDefNV(BaseRecoTask, 3); }; } // namespace fv0 diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index fdff035b934ef..80dcd6060455b 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -33,7 +33,7 @@ namespace fv0 class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::FV0, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FV0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx index 8a217232592df..8032220f8996d 100644 --- a/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx +++ b/Detectors/FIT/FV0/reconstruction/src/BaseRecoTask.cxx @@ -27,7 +27,7 @@ using RP = o2::fv0::RecPoints; RP BaseRecoTask::process(o2::fv0::Digit const& bcd, gsl::span inChData, - gsl::span outChData) + std::vector& outChData) { LOG(debug) << "Running reconstruction on new event"; @@ -44,22 +44,27 @@ RP BaseRecoTask::process(o2::fv0::Digit const& bcd, int nch = inChData.size(); for (int ich = 0; ich < nch; ich++) { LOG(debug) << " channel " << ich << " / " << nch; + if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(inChData[ich].ChId)) { + LOG(debug) << "Channel " << ich << " is dead - discarding data"; + continue; + } int offsetChannel = getOffset(int(inChData[ich].ChId)); - outChData[ich] = o2::fv0::ChannelDataFloat{inChData[ich].ChId, - (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, - (float)inChData[ich].QTCAmpl, - inChData[ich].ChainQTC}; + outChData.emplace_back(o2::fv0::ChannelDataFloat{inChData[ich].ChId, + (inChData[ich].CFDTime - offsetChannel) * DigitizationConstant::TIME_PER_TDCCHANNEL, + (float)inChData[ich].QTCAmpl, + inChData[ich].ChainQTC}); + const auto& currentOutCh = outChData.back(); // Conditions for reconstructing collision time (3 variants: first, average-relaxed and average-tight) - if (outChData[ich].charge > FV0DigParam::Instance().chargeThrForMeanTime) { - sideAtimeFirst = std::min(static_cast(sideAtimeFirst), outChData[ich].time); + if (currentOutCh.charge > FV0DigParam::Instance().chargeThrForMeanTime) { + sideAtimeFirst = std::min(static_cast(sideAtimeFirst), currentOutCh.time); if (inChData[ich].areAllFlagsGood()) { - if (std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvg += outChData[ich].time; + if (std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvg += currentOutCh.time; ndigitsA++; } - if (outChData[ich].charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(outChData[ich].time) < FV0DigParam::Instance().mTimeThresholdForReco) { - sideAtimeAvgSelected += outChData[ich].time; + if (currentOutCh.charge > FV0DigParam::Instance().mAmpThresholdForReco && std::abs(currentOutCh.time) < FV0DigParam::Instance().mTimeThresholdForReco) { + sideAtimeAvgSelected += currentOutCh.time; ndigitsASelected++; } } diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h index 015870d9178e2..f035b2406e5ba 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/RecoWorkflow.h @@ -20,7 +20,7 @@ namespace o2 { namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut); +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap); } // namespace fv0 } // namespace o2 #endif diff --git a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h index d71e154280e3d..934ce4d2c4a66 100644 --- a/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h +++ b/Detectors/FIT/FV0/workflow/include/FV0Workflow/ReconstructionSpec.h @@ -34,7 +34,7 @@ class ReconstructionDPL : public Task static constexpr int NCHANNELS = o2::fv0::Constants::nFv0Channels; public: - ReconstructionDPL(bool useMC, const std::string ccdbpath) : mUseMC(useMC), mCCDBpath(ccdbpath) {} + ReconstructionDPL(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) : mUseMC(useMC), mUseDeadChannelMap(useDeadChannelMap), mCCDBpath(ccdbpath) {} ~ReconstructionDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -44,6 +44,8 @@ class ReconstructionDPL : public Task private: bool mUseMC = false; bool mUpdateCCDB = true; + bool mUseDeadChannelMap = true; + bool mUpdateDeadChannelMap = true; const std::string mCCDBpath = o2::base::NameConf::getCCDBServer(); std::vector mRecPoints; std::vector mRecChData; @@ -53,7 +55,7 @@ class ReconstructionDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); +framework::DataProcessorSpec getReconstructionSpec(bool useMC = false, bool useDeadChannelMap = true, const std::string ccdbpath = "http://alice-ccdb.cern.ch"); } // namespace fv0 } // namespace o2 diff --git a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx index 6bfc5479303d1..a0ef71b75765a 100644 --- a/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/RecoWorkflow.cxx @@ -22,14 +22,13 @@ namespace o2 namespace fv0 { -framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut) +framework::WorkflowSpec getRecoWorkflow(bool useMC, bool disableRootInp, bool disableRootOut, bool useDeadChannelMap) { framework::WorkflowSpec specs; if (!disableRootInp) { specs.emplace_back(o2::fv0::getDigitReaderSpec(useMC)); } - - specs.emplace_back(o2::fv0::getReconstructionSpec(useMC)); + specs.emplace_back(o2::fv0::getReconstructionSpec(useMC, useDeadChannelMap)); if (!disableRootOut) { specs.emplace_back(o2::fv0::getRecPointWriterSpec(useMC)); } diff --git a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx index 520ac4dbaa563..cdf297b334588 100644 --- a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx @@ -21,6 +21,7 @@ #include "DataFormatsFV0/ChannelData.h" #include "DataFormatsFV0/MCLabel.h" #include "DataFormatsFV0/FV0ChannelTimeCalibrationObject.h" +#include "DataFormatsFIT/DeadChannelMap.h" #include "Framework/CCDBParamSpec.h" using namespace o2::framework; @@ -41,6 +42,7 @@ void ReconstructionDPL::run(ProcessingContext& pc) { mTimer.Start(false); mRecPoints.clear(); + mRecChData.clear(); auto digits = pc.inputs().get>("digits"); auto digch = pc.inputs().get>("digch"); // RS: if we need to process MC truth, uncomment lines below @@ -53,18 +55,19 @@ void ReconstructionDPL::run(ProcessingContext& pc) auto caliboffsets = pc.inputs().get("fv0offsets"); mReco.SetChannelOffset(caliboffsets.get()); } + if (mUseDeadChannelMap && mUpdateDeadChannelMap) { + auto deadChannelMap = pc.inputs().get("deadChannelMap"); + mReco.SetDeadChannelMap(deadChannelMap.get()); + } int nDig = digits.size(); LOG(debug) << " nDig " << nDig << " | ndigch " << digch.size(); mRecPoints.reserve(nDig); - mRecChData.resize(digch.size()); for (int id = 0; id < nDig; id++) { const auto& digit = digits[id]; LOG(debug) << " ndig " << id << " bc " << digit.getBC() << " orbit " << digit.getOrbit(); auto channels = digit.getBunchChannelData(digch); - gsl::span out_ch(mRecChData); - out_ch = out_ch.subspan(digit.ref.getFirstEntry(), digit.ref.getEntries()); - mRecPoints.emplace_back(mReco.process(digit, channels, out_ch)); + mRecPoints.emplace_back(mReco.process(digit, channels, mRecChData)); } LOG(debug) << "FV0 reconstruction pushes " << mRecPoints.size() << " RecPoints"; @@ -80,6 +83,9 @@ void ReconstructionDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) mUpdateCCDB = false; return; } + if (matcher == ConcreteDataMatcher(o2::header::gDataOriginFV0, "DeadChannelMap", 0)) { + mUpdateDeadChannelMap = false; + } } void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) @@ -88,7 +94,7 @@ void ReconstructionDPL::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) +DataProcessorSpec getReconstructionSpec(bool useMC, bool useDeadChannelMap, const std::string ccdbpath) { std::vector inputSpec; std::vector outputSpec; @@ -98,6 +104,10 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) LOG(info) << "Currently Reconstruction does not consume and provide MC truth"; inputSpec.emplace_back("labels", o2::header::gDataOriginFV0, "DIGITSMCTR", 0, Lifetime::Timeframe); } + if (useDeadChannelMap) { + LOG(info) << "Dead channel map will be applied during reconstruction"; + inputSpec.emplace_back("deadChannelMap", o2::header::gDataOriginFV0, "DeadChannelMap", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/DeadChannelMap")); + } inputSpec.emplace_back("fv0offsets", "FV0", "TimeOffset", 0, Lifetime::Condition, ccdbParamSpec("FV0/Calib/ChannelTimeOffset")); @@ -109,7 +119,7 @@ DataProcessorSpec getReconstructionSpec(bool useMC, const std::string ccdbpath) "fv0-reconstructor", inputSpec, outputSpec, - AlgorithmSpec{adaptFromTask(useMC, ccdbpath)}, + AlgorithmSpec{adaptFromTask(useMC, useDeadChannelMap, ccdbpath)}, Options{}}; } diff --git a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx index f1b1bfa456316..932e0a37ee376 100644 --- a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx index 16d1383c7e8c4..309560e2d6b36 100644 --- a/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/fv0-reco-workflow.cxx @@ -39,6 +39,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"disable root-files input readers"}}, {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writers"}}, + {"disable-dead-channel-map", o2::framework::VariantType::Bool, false, {"disable dead channel map"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -59,9 +60,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto useMC = !configcontext.options().get("disable-mc"); auto disableRootInp = configcontext.options().get("disable-root-input"); auto disableRootOut = configcontext.options().get("disable-root-output"); + bool useDeadChannelMap = !configcontext.options().get("disable-dead-channel-map"); LOG(info) << "WorkflowSpec getRecoWorkflow useMC " << useMC; - auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut); + auto wf = o2::fv0::getRecoWorkflow(useMC, disableRootInp, disableRootOut, useDeadChannelMap); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, wf); diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx index 849964aeaf871..e313940b0a91e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx @@ -17,10 +17,8 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "StrangenessTracking/StrangenessTrackingConfigParam.h" #include "GlobalTrackingWorkflow/StrangenessTrackingSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITSWorkflow/TrackReaderSpec.h" -#include "ITSMFTWorkflow/ClusterReaderSpec.h" #include "Framework/CCDBParamSpec.h" #include "DataFormatsParameters/GRPObject.h" diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index 0e6694d2353ac..894c11864f061 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace hmpid class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::HMP, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::HMP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h deleted file mode 100644 index e92e8375ad0d0..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h +++ /dev/null @@ -1,63 +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 HmpidDecodeRawFile.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWFILE_H_ -#define COMMON_HMPIDDECODERAWFILE_H_ - -#include -#include -#include -#include -#include -#include - -#include "HMPIDReconstruction/HmpidDecoder.h" - -#define MAXFILENAMEBUFFER 512 -#define MAXRAWFILEBUFFER RAWBLOCKDIMENSION_W * 4 + 8 - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawFile : public HmpidDecoder -{ - public: - HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawFile(int numOfEquipments); - ~HmpidDecodeRawFile(); - - bool setUpStream(void* InpuFileName, long Size); - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size); - bool getHeaderFromStream(uint32_t** streamPtr); - bool getWordFromStream(uint32_t* word); - int fileExists(char* filewithpath); - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge); - - private: - FILE* fh; - char mInputFile[MAXFILENAMEBUFFER]; - uint32_t mFileBuffer[MAXRAWFILEBUFFER]; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h deleted file mode 100644 index d5d82d0f238e9..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h +++ /dev/null @@ -1,73 +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 HmpidDecodeRawMem.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWMEM_H_ -#define COMMON_HMPIDDECODERAWMEM_H_ - -#include -#include -#include -#include -#include -#include - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecoder.h" - -using namespace o2; - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawMem : public HmpidDecoder -{ - public: - HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawMem(int numOfEquipments); - ~HmpidDecodeRawMem(); - - bool setUpStream(void* Buffer, long BufferLen) override; - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size) override; - bool getHeaderFromStream(uint32_t** streamPtr) override; - bool getWordFromStream(uint32_t* word) override; - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; - - private: -}; - -class HmpidDecodeRawDigit : public HmpidDecodeRawMem -{ - public: - HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawDigit(int numOfEquipments); - ~HmpidDecodeRawDigit(); - - std::vector mDigits; - - private: - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx deleted file mode 100644 index df97a4d2101e0..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx +++ /dev/null @@ -1,158 +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 HmpidDecodeRawFile.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "HMPIDReconstruction/HmpidDecodeRawFile.h" - -using namespace o2::hmpid; - -/// Constructor with the default HMPID equipments map at P2 -/// @param[in] numOfEquipments : number of defined equipments [0..13] -HmpidDecodeRawFile::HmpidDecodeRawFile(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ - fh = 0; -} - -/// Constructor with the HMPID address map -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawFile::HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ - fh = 0; -} - -/// Destructor -HmpidDecodeRawFile::~HmpidDecodeRawFile() -{ -} - -/// Setup the Input Stream with a File Handle -/// verify the existence and try to open it -/// @param[in] *FileName : the string that contains the File Name -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_FILENOTEXISTS Thrown if the file doesn't exists -/// @throws TH_OPENFILE Thrown if Fails to open the file -bool HmpidDecodeRawFile::setUpStream(void* FileName, long Size) -{ - strcpy(mInputFile, (const char*)FileName); - // files section ---- - if (!fileExists(mInputFile)) { - LOG(error) << "The input file " << mInputFile << " does not exist at this time."; - throw TH_FILENOTEXISTS; - } - // open the file - fh = fopen(mInputFile, "rb"); - if (fh == 0) { - LOG(error) << "ERROR to open Input file ! [" << mInputFile << "]"; - throw TH_OPENFILE; - } - - mActualStreamPtr = 0; // sets the pointer to the Buffer - mEndStreamPtr = 0; //sets the End of buffer - mStartStreamPtr = 0; - - return (true); -} - -/// Gets a sized chunk from the stream. Read from the file and update the pointers -/// ATTENTION : in order to optimize the disk accesses the block read pre-load a -/// complete Header+Payload block, the Size parameter is recalculated with the -/// dimension of the pack extract from the header field 'Offeset' -/// -/// verify the existence and try to open it -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_WRONGFILELEN Thrown if the file doesn't contains enough words -bool HmpidDecodeRawFile::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - if (Size > MAXRAWFILEBUFFER) - return (false); - int nr = fread(mFileBuffer, sizeof(int32_t), HEADERDIMENSION_W, fh); - if (nr != HEADERDIMENSION_W) { - throw TH_WRONGFILELEN; - } - Size = ((mFileBuffer[2] & 0x0000FFFF) / sizeof(int32_t)) - HEADERDIMENSION_W; - nr = fread(mFileBuffer + HEADERDIMENSION_W, sizeof(int32_t), Size, fh); - LOG(debug) << " getBlockFromStream read " << nr << " of " << Size + HEADERDIMENSION_W << " words !"; - if (nr != Size) { - throw TH_WRONGFILELEN; - } - *streamPtr = mFileBuffer; - mStartStreamPtr = mFileBuffer; - mActualStreamPtr = mFileBuffer; - mEndStreamPtr = mFileBuffer + Size; - return (true); -} - -/// Reads the Header from the file -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawFile::getHeaderFromStream(uint32_t** streamPtr) -{ - bool flag = getBlockFromStream(streamPtr, RAWBLOCKDIMENSION_W); // reads the 8k block - mActualStreamPtr += HEADERDIMENSION_W; // Move forward for the first word - return (flag); -} - -/// Read one word from the pre-load buffer -/// @param[in] *word : the buffer for the read word -/// @returns True every time -bool HmpidDecodeRawFile::getWordFromStream(uint32_t* word) -{ - *word = *mActualStreamPtr; - mActualStreamPtr++; - return (true); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawFile::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -/// Checks if the file exists ! -/// @param[in] *filewithpath : the File Name to check -/// @returns True if the file exists -int HmpidDecodeRawFile::fileExists(char* filewithpath) -{ - if (access(filewithpath, F_OK) != -1) { - return (true); - } else { - return (false); - } -} -o2::hmpid::Digit diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx deleted file mode 100644 index 5a4f2acbfd97b..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx +++ /dev/null @@ -1,184 +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 HmpidDecodeRawMem.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecodeRawMem.h" - -using namespace o2::hmpid; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawMem::HmpidDecodeRawMem(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawMem::HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawMem::~HmpidDecodeRawMem() = default; - -/// Setup the Input Stream with a Memory Pointer -/// the buffer length is in byte, some controls are done -/// -/// @param[in] *Buffer : the pointer to Memory buffer -/// @param[in] BufferLen : the length of the buffer (bytes) -/// @returns True if the stream is set -/// @throws TH_NULLBUFFERPOINTER Thrown if the pointer to the buffer is NULL -/// @throws TH_BUFFEREMPTY Thrown if the buffer is empty -/// @throws TH_WRONGBUFFERDIM Thrown if the buffer len is less then one header -bool HmpidDecodeRawMem::setUpStream(void* Buffer, long BufferLen) -{ - long wordsBufferLen = BufferLen / (sizeof(int32_t) / sizeof(char)); // Converts the len in words - if (Buffer == nullptr) { - LOG(error) << "Raw data buffer null Pointer ! "; - throw TH_NULLBUFFERPOINTER; - } - if (wordsBufferLen == 0) { - LOG(error) << "Raw data buffer Empty ! "; - throw TH_BUFFEREMPTY; - } - if (wordsBufferLen < 16) { - LOG(error) << "Raw data buffer less then the Header Dimension = " << wordsBufferLen; - throw TH_WRONGBUFFERDIM; - } - - mActualStreamPtr = (uint32_t*)Buffer; // sets the pointer to the Buffer - mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen; //sets the End of buffer - mStartStreamPtr = ((uint32_t*)Buffer); - // std::cout << " setUpStrem : StPtr=" << mStartStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << wordsBufferLen << std::endl; - return (true); -} - -/// Gets a sized chunk from the stream. The stream pointers members are updated -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : the dimension of the chunk (words) -/// @returns True every time -/// @throw TH_WRONGBUFFERDIM Buffer length shorter then the requested -bool HmpidDecodeRawMem::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - *streamPtr = mActualStreamPtr; - mActualStreamPtr += Size; - if (mActualStreamPtr > mEndStreamPtr) { - // std::cout << " getBlockFromStream : StPtr=" << mActualStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << Size << std::endl; - // std::cout << "Beccato " << std::endl; - // throw TH_WRONGBUFFERDIM; - return (false); - } - return (true); -} - -/// Gets the Header Block from the stream. -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawMem::getHeaderFromStream(uint32_t** streamPtr) -{ - return (getBlockFromStream(streamPtr, mRDHSize)); -} - -/// Gets a Word from the stream. -/// @param[in] *word : the buffer for the read word -/// @returns True if the operation end well -bool HmpidDecodeRawMem::getWordFromStream(uint32_t* word) -{ - uint32_t* appo; - *word = *mActualStreamPtr; - return (getBlockFromStream(&appo, 1)); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawMem::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -// ======================================================================================== - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int numOfEquipments) - : HmpidDecodeRawMem(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecodeRawMem(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawDigit::~HmpidDecodeRawDigit() = default; - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawDigit::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - mDigits.push_back(o2::hmpid::Digit(charge, eq->getEquipmentId(), col, dil, ch)); - //std::cout << "DI " << mDigits.back() << " "< // for LOG -#include "Framework/Logger.h" -#include "Headers/RAWDataHeader.h" -#include "HMPIDReconstruction/HmpidDecoder.h" -#include "DataFormatsHMP/Digit.h" - -using namespace o2::hmpid; - -// ============= HmpidDecoder Class implementation ======= - -/// Decoding Error Messages Definitions -char HmpidDecoder::sErrorDescription[MAXERRORS][MAXDESCRIPTIONLENGHT] = {"Word that I don't known !", - "Row Marker Word with 0 words", "Duplicated Pad Word !", "Row Marker Wrong/Lost -> to EoE", - "Row Marker Wrong/Lost -> to EoE", "Row Marker reports an ERROR !", "Lost EoE Marker !", "Double EoE marker", - "Wrong size definition in EoE Marker", "Double Mark Word", "Wrong Size in Segment Marker", "Lost EoS Marker !", - "HMPID Header Errors"}; - -/// HMPID Firmware Error Messages Definitions -char HmpidDecoder::sHmpidErrorDescription[MAXHMPIDERRORS][MAXDESCRIPTIONLENGHT] = { - "L0 Missing," - "L1 is received without L0", - "L1A signal arrived before the L1 Latency", "L1A signal arrived after the L1 Latency", - "L1A is missing or L1 timeout", "L1A Message is missing or L1 Message"}; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecoder::HmpidDecoder(int numOfEquipments) -{ - // The standard definition of HMPID equipments at P2 - int EqIds[] = {0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 10, 11, 12, 13}; - int CruIds[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3}; - int LinkIds[] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; - - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecoder::HmpidDecoder(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) -{ - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Destructor : remove the Equipments instances -HmpidDecoder::~HmpidDecoder() -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - delete mTheEquipments[i]; - } -} - -/// Init all the members variables. -void HmpidDecoder::init() -{ - mRDHSize = sizeof(o2::header::RAWDataHeader) / sizeof(uint32_t); - - mVerbose = 0; - mHeEvent = 0; - mHeBusy = 0; - mNumberWordToRead = 0; - mPayloadTail = 0; - - mHeFEEID = 0; - mHeSize = 0; - mHeVer = 0; - mHePrior = 0; - mHeStop = 0; - mHePages = 0; - mEquipment = 0; - - mHeOffsetNewPack = 0; - mHeMemorySize = 0; - - mHeDetectorID = 0; - mHeDW = 0; - mHeCruID = 0; - mHePackNum = 0; - mHePAR = 0; - mHePageNum = 0; - mHeLinkNum = 0; - mHeFirmwareVersion = 0; - mHeHmpidError = 0; - mHeBCDI = 0; - mHeORBIT = 0; - mHeTType = 0; - - mActualStreamPtr = nullptr; - mEndStreamPtr = nullptr; - mStartStreamPtr = nullptr; - - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - } -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the FLP hardware coords (CRU_Id and Link_Id) -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the Equipment_ID (Firmaware defined Id AKA FFEID) -/// @param[in] EquipmentId : the Equipment ID [0..13] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int EquipmentId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId() == EquipmentId) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment_ID converting the FLP hardware coords -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentID : the ID of the Equipment [0..13] (-1 := error) -int HmpidDecoder::getEquipmentID(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (mTheEquipments[i]->getEquipmentId()); - } - } - return (-1); -} - -/// Scans the BitMap of Raw Data File word and detect the type -/// and the parameters -/// @param[in] wp : the word to analyze -/// @param[out] *p1 : first parameter extract (if it exists) -/// @param[out] *p2 : second parameter extract (if it exists) -/// @param[out] *p3 : third parameter extract (if it exists) -/// @param[out] *p4 : fourth parameter extract (if it exists) -/// @returns Type of Word : the type of word [0..4] (0 := undetect) -int HmpidDecoder::checkType(uint32_t wp, int* p1, int* p2, int* p3, int* p4) -{ - if ((wp & 0x0000ffff) == 0x000036A8 || (wp & 0x0000ffff) == 0x000032A8 || (wp & 0x0000ffff) == 0x000030A0 || (wp & 0x0800ffff) == 0x080010A0) { - *p2 = (wp & 0x03ff0000) >> 16; // Number of words of row - *p1 = wp & 0x0000ffff; - return (WTYPE_ROW); - } - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *p2 = (wp & 0x000fff00) >> 8; // Number of words of Segment - *p1 = (wp & 0xfff00000) >> 20; - *p3 = wp & 0x0000000F; - if (*p3 < 4 && *p3 > 0) { - return (WTYPE_EOS); - } - } - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0000007F); - if (*p1 < 25 && *p2 < 11) { - return (WTYPE_EOE); - } - } - if ((wp & 0x08000000) == 0) { // # this is a pad - // PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0003F000) >> 12; - *p4 = (wp & 0x00000FFF); - if (*p1 > 0 && *p1 < 25 && *p2 > 0 && *p2 < 11 && *p3 < 48) { - return (WTYPE_PAD); - } - } else { - return (WTYPE_NONE); - } - return (WTYPE_NONE); -} - -/// Checks if is a Raw Marker and extract the Row Size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *rowSize : the number of words of the row -/// @param[out] *mark : the row marker -/// @returns True if Row Marker is detected -bool HmpidDecoder::isRowMarker(uint32_t wp, int* Err, int* rowSize, int* mark) -{ - if ((wp & 0x0000ffff) == 0x36A8 || (wp & 0x0000ffff) == 0x32A8 || (wp & 0x0000ffff) == 0x30A0 || (wp & 0x0800ffff) == 0x080010A0) { - *rowSize = (wp & 0x03ff0000) >> 16; // # Number of words of row - *mark = wp & 0x0000ffff; - *Err = false; - return (true); - } else { - *Err = true; - return (false); - } -} - -/// Checks if is a Segment Marker and extracts the Segment number and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *segSize : the number of words of the segment -/// @param[out] *Seg : the Segment number [1..3] -/// @param[out] *mark : the Segment Marker -/// @returns True if Segment Marker is detected -bool HmpidDecoder::isSegmentMarker(uint32_t wp, int* Err, int* segSize, int* Seg, int* mark) -{ - *Err = false; - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *segSize = (wp & 0x000fff00) >> 8; // # Number of words of Segment - *mark = (wp & 0xfff00000) >> 20; - *Seg = wp & 0x0000000F; - if (*Seg > 3 || *Seg < 1) { - LOG(info) << " Wrong segment Marker Word, bad Number of segment" << *Seg << "!"; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Checks if is a PAD Word and extracts all the parameters -/// PAD map : 0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Channel : the channel number [0..47] -/// @param[out] *Charge : the value of Charge [0..4095] -/// @returns True if PAD Word is detected -bool HmpidDecoder::isPadWord(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Channel, int* Charge) -{ - *Err = false; - // if ((wp & 0x08000000) != 0) { - if ((wp & 0x08000000) != 0) { - return (false); - } - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Channel = (wp & 0x0003F000) >> 12; - *Charge = (wp & 0x00000FFF); - - if ((wp & 0x0ffff) == 0x036A8 || (wp & 0x0ffff) == 0x032A8 || (wp & 0x0ffff) == 0x030A0 || (wp & 0x0ffff) == 0x010A0) { // # ! this is a pad - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - return (false); - } - } else { - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - // LOG(warning) << " Wrong Pad values Col=" << *Col << " Dilogic=" << *Dilogic << " Channel=" << *Channel << " Charge=" << *Charge << " wp:0x" << std::hex << wp << std::dec; - *Err = true; - return (false); - } - } - return (true); -} - -/// Checks if is a EoE Marker and extracts the Column, Dilogic and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Eoesize : the number of words for dilogic -/// @returns True if EoE marker is detected -bool HmpidDecoder::isEoEmarker(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Eoesize) -{ - *Err = false; - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Eoesize = (wp & 0x0000007F); - if (*Col > 24 || *Dilogic > 10) { - LOG(info) << " EoE size wrong definition. Col=" << *Col << " Dilogic=" << *Dilogic; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Decode the HMPID error BitMap field (5 bits) and returns true if there are -/// errors and in addition the concat string that contains the error messages -/// ATTENTION : the char * outbuf MUST point to a 250 bytes buffer -/// @param[in] ErrorField : the HMPID Error field -/// @param[out] *outbuf : the output buffer that contains the error description -/// @returns True if EoE marker is detected -bool HmpidDecoder::decodeHmpidError(int ErrorField, char* outbuf) -{ - int res = false; - outbuf[0] = '\0'; - for (int i = 0; i < MAXHMPIDERRORS; i++) { - if ((ErrorField & (0x01 << i)) != 0) { - res = true; - strcat(outbuf, sHmpidErrorDescription[i]); - } - } - return (res); -} - -/// This Decode the Raw Data Header, returns the EquipmentIndex -/// that is obtained with the FLP hardware coords -/// -/// ATTENTION : the 'EquipIndex' parameter and the mEquipment member -/// are different data: the first is the pointer in the Equipments instances -/// array, the second is the FEE_ID number -/// -/// The EVENT_NUMBER : actually is calculated from the ORBIT number -/// -/// @param[in] *streamPtrAdr : the pointer to the Header buffer -/// @param[out] *EquipIndex : the Index to the Equipment Object Array [0..13] -/// @returns True every time -/// @throws TH_WRONGEQUIPINDEX Thrown if the Equipment Index is out of boundary (Equipment not recognized) -int HmpidDecoder::decodeHeader(uint32_t* streamPtrAdr, int* EquipIndex) -{ - uint32_t* buffer = streamPtrAdr; // Sets the pointer to buffer - o2::header::RAWDataHeader* hpt = (o2::header::RAWDataHeader*)buffer; - - /* - mHeFEEID = (buffer[0] & 0x000f0000) >> 16; - mHeSize = (buffer[0] & 0x0000ff00) >> 8; - mHeVer = (buffer[0] & 0x000000ff); - mHePrior = (buffer[1] & 0x000000FF); - mHeDetectorID = (buffer[1] & 0x0000FF00) >> 8; - mHeOffsetNewPack = (buffer[2] & 0x0000FFFF); - mHeMemorySize = (buffer[2] & 0xffff0000) >> 16; - mHeDW = (buffer[3] & 0xF0000000) >> 24; - mHeCruID = (buffer[3] & 0x0FF0000) >> 16; - mHePackNum = (buffer[3] & 0x0000FF00) >> 8; - mHeLinkNum = (buffer[3] & 0x000000FF); - mHeBCDI = (buffer[4] & 0x00000FFF); - mHeORBIT = buffer[5]; - mHeTType = buffer[8]; - mHePageNum = (buffer[9] & 0x0000FFFF); - mHeStop = (buffer[9] & 0x00ff0000) >> 16; - mHeBusy = (buffer[12] & 0xfffffe00) >> 9; - mHeFirmwareVersion = buffer[12] & 0x0000000f; - mHeHmpidError = (buffer[12] & 0x000001F0) >> 4; - mHePAR = buffer[13] & 0x0000FFFF; - */ - mHeFEEID = hpt->feeId; - mHeSize = hpt->headerSize; - mHeVer = hpt->version; - mHePrior = hpt->priority; - mHeDetectorID = hpt->sourceID; - mHeOffsetNewPack = hpt->offsetToNext; - mHeMemorySize = hpt->memorySize; - mHeDW = hpt->endPointID; - mHeCruID = hpt->cruID; - mHePackNum = hpt->packetCounter; - mHeLinkNum = hpt->linkID; - mHeBCDI = hpt->bunchCrossing; - mHeORBIT = hpt->orbit; - mHeTType = hpt->triggerType; - mHePageNum = hpt->pageCnt; - mHeStop = hpt->stop; - mHeBusy = (hpt->detectorField & 0xfffffe00) >> 9; - mHeFirmwareVersion = hpt->detectorField & 0x0000000f; - mHeHmpidError = (hpt->detectorField & 0x000001F0) >> 4; - mHePAR = hpt->detectorPAR; - - *EquipIndex = getEquipmentIndex(mHeCruID, mHeLinkNum); - // mEquipment = (*EquipIndex != -1) ? mTheEquipments[*EquipIndex]->getEquipmentId() : -1; - mEquipment = mHeFEEID & 0x000F; - mNumberWordToRead = ((mHeMemorySize - mHeSize) / sizeof(uint32_t)); - mPayloadTail = ((mHeOffsetNewPack - mHeMemorySize) / sizeof(uint32_t)); - - // ---- Event ID : Actualy based on ORBIT NUMBER and BC - mHeEvent = (mHeORBIT << 12) | mHeBCDI; - - LOG(debug) << "FEE-ID=" << mHeFEEID << " HeSize=" << mHeSize << " HePrior=" << mHePrior << " Det.Id=" << mHeDetectorID << " HeMemorySize=" << mHeMemorySize << " HeOffsetNewPack=" << mHeOffsetNewPack; - LOG(debug) << " Equipment=" << mEquipment << " PakCounter=" << mHePackNum << " Link=" << mHeLinkNum << " CruID=" << mHeCruID << " DW=" << mHeDW << " BC=" << mHeBCDI << " ORBIT=" << mHeORBIT; - LOG(debug) << " TType=" << mHeTType << " HeStop=" << mHeStop << " PagesCounter=" << mHePageNum << " FirmVersion=" << mHeFirmwareVersion << " BusyTime=" << mHeBusy << " Error=" << mHeHmpidError << " PAR=" << mHePAR; - LOG(debug) << " EquIdx = " << *EquipIndex << " Event = " << mHeEvent << " Payload : Words to read=" << mNumberWordToRead << " PailoadTail=" << mPayloadTail; - - if (*EquipIndex == -1) { - LOG(error) << "ERROR ! Bad equipment Number: " << mEquipment; - throw TH_WRONGEQUIPINDEX; - } - // std::cout << "HMPID ! Exit decode header" << std::endl; - return (true); -} - -/// Updates some information related to the Event -/// this function is called at the end of the event -/// @param[in] *eq : the pointer to the Equipment Object -void HmpidDecoder::updateStatistics(HmpidEquipment* eq) -{ - eq->mPadsPerEventAverage = ((eq->mPadsPerEventAverage * (eq->mNumberOfEvents - 1)) + eq->mSampleNumber) / (eq->mNumberOfEvents); - eq->mEventSizeAverage = ((eq->mEventSizeAverage * (eq->mNumberOfEvents - 1)) + eq->mEventSize) / (eq->mNumberOfEvents); - eq->mBusyTimeAverage = ((eq->mBusyTimeAverage * eq->mBusyTimeSamples) + eq->mBusyTimeValue) / (++(eq->mBusyTimeSamples)); - if (eq->mSampleNumber == 0) { - eq->mNumberOfEmptyEvents += 1; - } - if (eq->mErrorsCounter > 0) { - eq->mNumberOfWrongEvents += 1; - } - eq->mTotalPads += eq->mSampleNumber; - eq->mTotalErrors += eq->mErrorsCounter; - - //std::cout << ">>>updateStatistics() >>> "<< eq->getEquipmentId() << "="<< eq->mNumberOfEvents<<" :" << eq->mEventSize <<","<< eq->mTotalPads << ", " << eq->mSampleNumber << std::endl; - - return; -} - -/// Evaluates the content of the header and detect the change of the event -/// with the relevant updates... -/// @param[in] EquipmentIndex : the pointer to the Array of Equipments Array -/// @returns the Pointer to the modified Equipment object -HmpidEquipment* HmpidDecoder::evaluateHeaderContents(int EquipmentIndex) -{ - //std::cout << "Enter evaluateHeaderContents.."; - HmpidEquipment* eq = mTheEquipments[EquipmentIndex]; - if (mHeEvent != eq->mEventNumber) { // Is a new event - if (eq->mEventNumber != OUTRANGEEVENTNUMBER) { // skip the first - updateStatistics(eq); // update previous statistics - } - eq->mNumberOfEvents++; - eq->mEventNumber = mHeEvent; - eq->mBusyTimeValue = mHeBusy * 0.00000005; - eq->mEventSize = 0; // reset the event - eq->mSampleNumber = 0; - eq->mErrorsCounter = 0; - mIntReco = {(uint16_t)mHeBCDI, (uint32_t)mHeORBIT}; - } - eq->mEventSize += mNumberWordToRead * sizeof(uint32_t); // Calculate the size in bytes - if (mHeHmpidError != 0) { - LOG(info) << "HMPID Header reports an error : " << mHeHmpidError; - dumpHmpidError(mHeHmpidError); - eq->setError(ERR_HMPID); - } - // std::cout << ".. end evaluateHeaderContents = " << eq->mEventNumber << std::endl; - return (eq); -} - -/// --------------- Decode One Page from Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePage(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(debug) << "End main decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(error) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int p1, p2, p3, p4; - int error; - int type; - bool isIt; - - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - if (newOne == true) { - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - type = checkType(wp, &p1, &p2, &p3, &p4); - if (type == WTYPE_NONE) { - if (eq->mWillBePad == true) { // try to recover the first pad ! - type = checkType((wp & 0xF7FFFFFF), &p1, &p2, &p3, &p4); - if (type == WTYPE_PAD && p3 == 0 && eq->mWordsPerDilogicCounter == 0) { - newOne = false; // # reprocess as pad - continue; - } - } - eq->setError(ERR_NOTKNOWN); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_NOTKNOWN] << " [" << wp << "]"; - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - payIndex++; - continue; - } - } - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << std::hex << wp << std::dec << "<" << type; - } - if (eq->mWillBeRowMarker == true) { // #shoud be a Row Marker - if (type == WTYPE_ROW) { - eq->mColumnCounter++; - eq->mWordsPerSegCounter++; - eq->mRowSize = p2; - switch (p2) { - case 0: // Empty column - eq->setError(ERR_ROWMARKEMPTY); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKEMPTY] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FF: // Error in column - eq->setError(ERR_ROWMARKERROR); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKERROR] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FE: // Masked column - LOG(info) << "Equip=" << mEquipment << "The column=" << (eq->mSegment) * 8 + eq->mColumnCounter << " is Masked !"; - eq->mWillBeRowMarker = true; - break; - default: - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - break; - } - newOne = true; - } else { - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == WTYPE_EOE) { // # Could be a EoE - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKWRONG); - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_PAD) { //# Could be a PAD - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_EOS) { // # Could be a EoS - eq->mWillBeRowMarker = false; - eq->mWillBeSegmentMarker = true; - newOne = false; - } else { - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } - } - } else if (eq->mWillBePad == true) { // # We expect a pad - //# PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - // c = 1..24 d = 1..10 n = 0..47 - if (type == WTYPE_PAD) { - newOne = true; - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - } else if (p1 != (eq->mSegment * 8 + eq->mColumnCounter)) { // # Manage - // We try to recover the RowMarker misunderstanding - isIt = isRowMarker(wp, &error, &p2, &p1); - if (isIt == true && error == false) { - type = WTYPE_ROW; - newOne = false; - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } else { - LOG(debug) << "Equip=" << mEquipment << " Mismatch in column" - << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mColumnCounter = p1 % 8; - } - } else { - setPad(eq, p1 - 1, p2 - 1, p3, p4); - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << p1 - 1 << "," << p2 - 1 << "," << p3 << "," << p4; - } - eq->mWordsPerDilogicCounter++; - eq->mSampleNumber++; - if (p3 == 47) { - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } - } - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - } else if (type == WTYPE_EOE) { //# the pads are end ok - eq->mWillBeEoE = true; - eq->mWillBePad = false; - newOne = false; - } else if (type == WTYPE_ROW) { // # We Lost the EoE ! - // We try to recover the PAD misunderstanding - isIt = isPadWord(wp, &error, &p1, &p2, &p3, &p4); - if (isIt == true && error == false) { - type = WTYPE_PAD; - newOne = false; // # reprocess as pad - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (type == WTYPE_EOS) { // # We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (eq->mWillBeEoE == true) { // # We expect a EoE - if (type == WTYPE_EOE) { - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - if (wpprev == wp) { - eq->setError(ERR_DOUBLEEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEEOEMARK] << " col=" << p1; - } else if (p3 != eq->mWordsPerDilogicCounter) { - eq->setError(ERR_WRONGSIZEINEOE); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZEINEOE] << " col=" << p1; - } - eq->mWordsPerDilogicCounter = 0; - if (p2 == 10) { - if (p1 % 8 != 0) { // # we expect the Row Marker - eq->mWillBeRowMarker = true; - } else { - eq->mWillBeSegmentMarker = true; - } - } else { - eq->mWillBePad = true; - } - eq->mWillBeEoE = false; - newOne = true; - } else if (type == WTYPE_EOS) { // We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_ROW) { //# We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_PAD) { // # We Lost the EoE ! - int typb, p1b, p2b, p3b, p4b; - typb = checkType((wp | 0x08000000), &p1b, &p2b, &p3b, &p4b); - if (typb == WTYPE_EOE && p3b == 48) { - type = typb; - p1 = p1b; - p2 = p2b; - p3 = p3b; - p4 = p4b; - newOne = false; // # reprocess as EoE - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBePad = true; - eq->mWillBeEoE = false; - newOne = false; - } - } - } else if (eq->mWillBeSegmentMarker == true) { // # We expect a EoSegment - if (wpprev == wp) { - eq->setError(ERR_DOUBLEMARKWORD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEMARKWORD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == 2) { - if (abs(eq->mWordsPerSegCounter - p2) > 5) { - eq->setError(ERR_WRONGSIZESEGMENTMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZESEGMENTMARK] << " Seg=" << p2; - } - eq->mWordsPerSegCounter = 0; - eq->mWordsPerRowCounter = 0; - eq->mColumnCounter = 0; - eq->mSegment = p3 % 3; - eq->mWillBeRowMarker = true; - eq->mWillBeSegmentMarker = false; - newOne = true; - } else { - eq->setError(ERR_LOSTEOSMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOSMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = false; - eq->mWillBeRowMarker = true; - newOne = false; - } - } - if (newOne) { - payIndex += 1; - } - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } -} - -/// --------------- Read Raw Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBuffer() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - mTheEquipments[i]->resetErrors(); - } - - int type; - int equipmentIndex = -1; - int isIt; - HmpidEquipment* eq; - uint32_t* streamBuf; - LOG(debug) << "Enter decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePage(&streamBuf); - } catch (int e) { - LOG(debug) << "End main buffer decoding loop !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -/// --------- Decode One Page from Data Buffer with Fast Decoding -------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePageFast(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(info) << "End Fast Page decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(info) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int Column, Dilogic, Channel, Charge; - int pwer; - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - if (wp == wpprev) { - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << Column << "]"; - } else { - if (isPadWord(wp, &pwer, &Column, &Dilogic, &Channel, &Charge) == true) { - if (pwer != true) { - setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); - eq->mSampleNumber++; - } - } - } - payIndex += 1; - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } - return; -} -/// ---------- Read Raw Data Buffer with Fast Decoding ---------- -/// Read the stream, decode the contents and store resuls. -/// Fast alghoritm : no parsing of control words ! -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBufferFast() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - } - - uint32_t* streamBuf; - LOG(info) << "Enter FAST decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePageFast(&streamBuf); - } catch (int e) { - LOG(info) << " End Buffer Fast Decoding !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -// ========================================================= - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Number of entries for specified pad -uint16_t HmpidDecoder::getPadSamples(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getPadSum(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getPadSquares(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[c][d][h]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Number of Entries for specified pad -uint16_t HmpidDecoder::getChannelSamples(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getChannelSum(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getChannelSquare(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[Column][Dilogic][Channel]); -} - -/// Gets the Average Event Size value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Event Size value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageEventSize(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mEventSizeAverage); -} - -/// Gets the Average Busy Time value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Busy Time value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageBusyTime(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mBusyTimeAverage); -} - -// =================================================== -// Methods to dump info - -/// Prints on the standard output the table of decoding -/// errors for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -void HmpidDecoder::dumpErrors(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - std::cout << "Dump Errors for the Equipment = " << EquipmId << std::endl; - for (int i = 0; i < MAXERRORS; i++) { - std::cout << sErrorDescription[i] << " = " << mTheEquipments[EqInd]->mErrors[i] << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output a Table of statistical -/// decoding information for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @type[in] The type of info. 0 = Entries, 1 = Sum, 2 = Sum of squares -void HmpidDecoder::dumpPads(int EquipmId, int type) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - int Module = EquipmId / 2; - int StartRow = (EquipmId % 2 == 1) ? 80 : 0; - int EndRow = (EquipmId % 2 == 1) ? 160 : 80; - std::cout << "Dump Pads for the Equipment = " << EquipmId << std::endl; - for (int c = 0; c < 144; c++) { - for (int r = StartRow; r < EndRow; r++) { - switch (type) { - case 0: - std::cout << getPadSamples(Module, r, c) << ","; - break; - case 1: - std::cout << getPadSum(Module, r, c) << ","; - break; - case 2: - std::cout << getPadSquares(Module, r, c) << ","; - break; - } - } - std::cout << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output the decoded HMPID error field -/// @param[in] ErrorField : the HMPID readout error field -void HmpidDecoder::dumpHmpidError(int ErrorField) -{ - char printbuf[MAXHMPIDERRORS * MAXDESCRIPTIONLENGHT]; - if (decodeHmpidError(ErrorField, printbuf) == true) { - LOG(error) << "HMPID Error field = " << ErrorField << " : " << printbuf; - } - return; -} - -/// Writes in a ASCCI File the complete report of the decoding -/// procedure -/// @param[in] *summaryFileName : the name of the output file -/// @throws TH_CREATEFILE Thrown if was not able to create the file -void HmpidDecoder::writeSummaryFile(char* summaryFileName) -{ - FILE* fs = fopen(summaryFileName, "w"); - if (fs == nullptr) { - printf("Error opening the file %s !\n", summaryFileName); - throw TH_CREATEFILE; - } - - fprintf(fs, "HMPID Readout Raw Data Decoding Summary File\n"); - fprintf(fs, "Equipment Id\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->getEquipmentId()); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average Event Size\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mEventSizeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Total pads\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalPads); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average pads per event\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mPadsPerEventAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Busy Time average\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Event rate\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", 1 / mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of Empty Events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEmptyEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "-------------Errors--------------------\n"); - fprintf(fs, "Wrong events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfWrongEvents); - } - fprintf(fs, "\n"); - - for (int j = 0; j < MAXERRORS; j++) { - fprintf(fs, "%s\t", sErrorDescription[j]); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mErrors[j]); - } - fprintf(fs, "\n"); - } - - fprintf(fs, "Total errors\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalErrors); - } - fprintf(fs, "\n"); - - fclose(fs); - return; -} diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h deleted file mode 100644 index 6102ec481c97c..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h +++ /dev/null @@ -1,27 +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 STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ -#define STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace hmpid -{ - -o2::framework::DataProcessorSpec getHMPIDClusterizerSpec(bool useMC); - -} // end namespace hmpid -} // end namespace o2 - -#endif /* STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZERSPEC_H_ */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h deleted file mode 100644 index eea9b134bd911..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h +++ /dev/null @@ -1,53 +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 DigitReader.h - -#ifndef O2_HMPID_DIGITREADER -#define O2_HMPID_DIGITREADER - -#include "TFile.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "DataFormatsHMP/Digit.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -namespace o2 -{ -namespace hmpid -{ - -class DigitReader : public o2::framework::Task -{ - public: - DigitReader(bool useMC) : mUseMC(useMC) {} - ~DigitReader() override = default; - void init(o2::framework::InitContext& ic) final; - void run(o2::framework::ProcessingContext& pc) final; - - private: - int mState = 0; - bool mUseMC = true; - std::unique_ptr mFile = nullptr; - - std::vector mDigits, *mPdigits = &mDigits; - - o2::dataformats::MCTruthContainer mLabels, *mPlabels = &mLabels; -}; - -/// read simulated HMPID digits from a root file -framework::DataProcessorSpec getDigitReaderSpec(bool useMC); - -} // namespace hmpid -} // namespace o2 - -#endif /* O2_HMPID_DIGITREADER */ diff --git a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx index 76e7eae10508e..4a1883233b605 100644 --- a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index e236c898851f5..c8ef445e273d3 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -314,7 +314,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static const char* getITS3PixelArrayPattern(int layer) { return Form("%s%d", getITS3PixelArrayPatternRaw(), layer); }; /// sym name of the layer - static const char* composeSymNameITS(bool isITS3 = false); + static const char* composeSymNameITS(); /// sym name of the layer static const char* composeSymNameLayer(int lr, bool isITS3 = false); diff --git a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index 60570b2f204c5..5dc499d05037e 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -290,14 +290,8 @@ bool GeometryTGeo::getChipId(int index, int& lay, int& hba, int& sta, int& hsta, } //__________________________________________________________________________ -const char* GeometryTGeo::composeSymNameITS(bool isITS3) +const char* GeometryTGeo::composeSymNameITS() { - if (isITS3) { -#ifdef ENABLE_UPGRADES - return o2::detectors::DetID(o2::detectors::DetID::IT3).getName(); -#endif - } - return o2::detectors::DetID(o2::detectors::DetID::ITS).getName(); } diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index 8cfe13097d581..63d7a8ad8dfa2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -61,6 +61,7 @@ using Segmentation = o2::itsmft::SegmentationAlpide; using namespace o2::its; #ifdef ENABLE_UPGRADES +#include "ITS3Simulation/DescriptorInnerBarrelITS3.h" using namespace o2::its3; #endif @@ -1106,7 +1107,7 @@ void Detector::addAlignableVolumes() const TString detName = GetName(); TString path = Form("/cave_1/barrel_1/%s_2", GeometryTGeo::getITSVolPattern()); - TString sname = GeometryTGeo::composeSymNameITS((detName == "IT3")); + TString sname = GeometryTGeo::composeSymNameITS(); LOG(debug) << sname << " <-> " << path; @@ -1117,15 +1118,19 @@ void Detector::addAlignableVolumes() const Int_t lastUID = 0; for (Int_t lr = 0; lr < mNumberLayers; lr++) { if (lr < mNumberInnerLayers) { +#ifdef ENABLE_UPGRADES if (detName == "ITS") { ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); + } else { + ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); } +#else + ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); +#endif } else { addAlignableVolumesLayer(lr, path, lastUID); } } - - return; } void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) const @@ -1148,8 +1153,6 @@ void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) for (Int_t hb = start; hb < nhbarrel; hb++) { addAlignableVolumesHalfBarrel(lr, hb, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent, Int_t& lastUID) const @@ -1177,8 +1180,6 @@ void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent for (int st = 0; st < nstaves; st++) { addAlignableVolumesStave(lr, hb, st, path, lastUID); } - - return; } void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& parent, Int_t& lastUID) const @@ -1205,8 +1206,6 @@ void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& p for (Int_t sst = start; sst < nhstave; sst++) { addAlignableVolumesHalfStave(lr, hb, st, sst, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const @@ -1236,8 +1235,6 @@ void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t for (Int_t md = start; md < nmodules; md++) { addAlignableVolumesModule(lr, hb, st, hst, md, path, lastUID); } - - return; } void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const @@ -1266,8 +1263,6 @@ void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst for (Int_t ic = 0; ic < nchips; ic++) { addAlignableVolumesChip(lr, hb, st, hst, md, ic, path, lastUID); } - - return; } void Detector::addAlignableVolumesChip(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 4dbb9f09f6192..acc884ea68b8b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -299,7 +299,7 @@ struct TimeFrame { virtual bool isGPU() const noexcept { return false; } virtual const char* getName() const noexcept { return "CPU"; } - private: + protected: void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = nLayers); float mBz = 5.; unsigned int mNTotalLowPtVertices = 0; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 4c903ed1f3ca1..3ea382c626fed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -72,6 +72,7 @@ class Tracker bool isMatLUT() const { return mTraits->isMatLUT(); } void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } void printSummary() const; + void computeTracksMClabels(); private: void initialiseTimeFrame(int iteration) { mTraits->initialiseTimeFrame(iteration); } @@ -84,7 +85,6 @@ class Tracker // MC interaction void computeRoadsMClabels(); - void computeTracksMClabels(); void rectifyClusterIndices(); template diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 70f4e3d1d3fc7..29fb4ac4c69b5 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -344,7 +344,6 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter mMSangles.resize(trkParam.NLayers); mPhiCuts.resize(mClusters.size() - 1, 0.f); - float oneOverR{0.001f * 0.3f * std::abs(mBz) / trkParam.TrackletMinPt}; for (unsigned int iLayer{0}; iLayer < nLayers; ++iLayer) { mMSangles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); @@ -352,12 +351,14 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter if (iLayer < mClusters.size() - 1) { const float& r1 = trkParam.LayerRadii[iLayer]; const float& r2 = trkParam.LayerRadii[iLayer + 1]; + oneOverR = (0.5 * oneOverR >= 1.f / r2) ? 2.f / r2 - o2::constants::math::Almost0 : oneOverR; const float res1 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer]); const float res2 = o2::gpu::CAMath::Hypot(trkParam.PVres, mPositionResolution[iLayer + 1]); const float cosTheta1half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r1 * oneOverR)); const float cosTheta2half = o2::gpu::CAMath::Sqrt(1.f - math_utils::Sq(0.5f * r2 * oneOverR)); float x = r2 * cosTheta1half - r1 * cosTheta2half; float delta = o2::gpu::CAMath::Sqrt(1.f / (1.f - 0.25f * math_utils::Sq(x * oneOverR)) * (math_utils::Sq(0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta2half + cosTheta1half) * math_utils::Sq(res1) + math_utils::Sq(0.25f * r1 * r2 * math_utils::Sq(oneOverR) / cosTheta1half + cosTheta2half) * math_utils::Sq(res2))); + /// the expression std::asin(0.5f * x * oneOverR) is equivalent to std::aCos(0.5f * r1 * oneOverR) - std::acos(0.5 * r2 * oneOverR) mPhiCuts[iLayer] = std::min(o2::gpu::CAMath::ASin(0.5f * x * oneOverR) + 2.f * mMSangles[iLayer] + delta, o2::constants::math::PI * 0.5f); } } diff --git a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt index f0d50e59493d4..10e16e49d92b5 100644 --- a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt @@ -13,8 +13,6 @@ o2_add_library(ITSWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx src/ClusterWriterWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackWriterSpec.cxx src/TrackReaderSpec.cxx diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h deleted file mode 100644 index c5038c87fa467..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h +++ /dev/null @@ -1,65 +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 ClustererSpec.h - -#ifndef O2_ITS_CLUSTERERDPL -#define O2_ITS_CLUSTERERDPL - -#include -#include "DetectorsBase/GRPGeomHelper.h" -#include "ITSMFTReconstruction/Clusterer.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -using namespace o2::framework; - -namespace o2 -{ - -namespace itsmft -{ -class Clusterer; -} - -namespace its -{ - -class ClustererDPL : public Task -{ - public: - ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} - ~ClustererDPL() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; - void endOfStream(o2::framework::EndOfStreamContext& ec) final; - - private: - void updateTimeDependentParams(ProcessingContext& pc); - - int mState = 0; - bool mUseMC = true; - bool mUseClusterDictionary = true; - int mNThreads = 1; - std::unique_ptr mFile = nullptr; - std::unique_ptr mClusterer = nullptr; - std::shared_ptr mGGCCDBRequest; -}; - -/// create a processor spec -/// run ITS cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); - -} // namespace its -} // namespace o2 - -#endif /* O2_ITS_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index 4dffbaf88893c..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +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 ClusterWriterSpec.cxx - -#include - -#include "ITSWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace its -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "ITSClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("its-cluster-writer", - "o2clus_its.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with ITS clusters"}, - BranchDefinition{InputSpec{"compclus", "ITS", "COMPCLUSTERS", 0}, - "ITSClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "ITS", "PATTERNS", 0}, - "ITSClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "ITS", "CLUSTERSROF", 0}, - "ITSClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "ITS", "CLUSTERSMCTR", 0}, - "ITSClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "ITS", "CLUSTERSMC2ROF", 0}, - "ITSClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx index ca5db7acd63e1..aba468b3e9460 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx @@ -12,7 +12,7 @@ /// @file ClusterWriterWorkflow.cxx #include "ITSWorkflow/ClusterWriterWorkflow.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" namespace o2 { @@ -26,7 +26,7 @@ framework::WorkflowSpec getWorkflow(bool useMC) { framework::WorkflowSpec specs; - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx deleted file mode 100644 index d58e4f5d915c1..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,218 +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 ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "ITSMFTReconstruction/ClustererParam.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace its -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingITS::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - LOGP(info, "Initialising ITSClusterer with {} threads", mNThreads); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(info) << "ITSClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - LOG(info) << "ITSClusterer pulled " << labels.getNElements() << " labels "; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginITS; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(info) << "ITSClusterer pushed " << clusCompVec.size() << " clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - static bool initOnceDone = false; - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(clParams.maxBCDiffToSquashBias > 0 ? nROFsToSquash : 0); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((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("ITS", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "ITS", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "ITS", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "ITS", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/ClustererParam")); - inputs.emplace_back("alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "ITS", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "ITS", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "its-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -///_______________________________________ -void ClustererDPL::endOfStream(o2::framework::EndOfStreamContext& ec) -{ - mClusterer->print(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index 60e28556716f2..9f8cb6c83ef99 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -12,8 +12,8 @@ /// @file RecoWorkflow.cxx #include "ITSWorkflow/RecoWorkflow.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" #include "ITStracking/TrackingConfigParam.h" @@ -43,10 +43,10 @@ framework::WorkflowSpec getWorkflow(bool useMC, specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, true, "itsdigits.root")); } if (!upstreamClusters) { - specs.emplace_back(o2::its::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } if ((trmode != TrackingMode::Off) && (TrackerParamConfig::Instance().trackingMode != TrackingMode::Off)) { if (useGPUWF) { diff --git a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h b/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h deleted file mode 100644 index a8280467b14c9..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h +++ /dev/null @@ -1,101 +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 NoiseSlotCalibrator.h - -#ifndef O2_MFT_NOISESLOTCALIBRATOR -#define O2_MFT_NOISESLOTCALIBRATOR - -#include - -#include "DetectorsCalibration/TimeSlotCalibration.h" -#include "DetectorsCalibration/TimeSlot.h" - -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/Digit.h" -#include "DataFormatsITSMFT/NoiseMap.h" -#include "gsl/span" - -namespace o2 -{ - -namespace itsmft -{ -class ROFRecord; -} // namespace itsmft - -namespace mft -{ - -class NoiseSlotCalibrator : public o2::calibration::TimeSlotCalibration -{ - using Slot = calibration::TimeSlot; - - public: - NoiseSlotCalibrator() { setUpdateAtTheEndOfRunOnly(); } - NoiseSlotCalibrator(float prob, float relErr) : mProbabilityThreshold(prob), mProbRelErr(relErr) - { - setUpdateAtTheEndOfRunOnly(); - setSlotLength(INFINITE_TF); - mMinROFs = 1.1 * o2::itsmft::NoiseMap::getMinROFs(prob, relErr); - LOGP(info, "At least {} ROFs needed to apply threshold {} with relative error {}", mMinROFs, mProbabilityThreshold, mProbRelErr); - } - ~NoiseSlotCalibrator() final = default; - - void setThreshold(unsigned int t) { mThreshold = t; } - - bool processTimeFrame(calibration::TFType tf, - gsl::span const& digits, - gsl::span const& rofs); - - bool processTimeFrame(calibration::TFType tf, - gsl::span const& clusters, - gsl::span const& patterns, - gsl::span const& rofs); - - void setMinROFs(long n) { mMinROFs = n; } - - void finalize() - { - LOG(info) << "Number of processed strobes is " << mNumberOfStrobes; - auto& slot = getSlots().back(); - slot.getContainer()->applyProbThreshold(mProbabilityThreshold, mNumberOfStrobes); - } - - const o2::itsmft::NoiseMap& getNoiseMap(long& start, long& end) - { - const auto& slot = getSlots().back(); - start = slot.getTFStart(); - end = slot.getTFEnd(); - return *(slot.getContainer()); - } - - // Functions overloaded from the calibration framework - bool process(calibration::TFType tf, const gsl::span data) final; - - // Functions required by the calibration framework - void initOutput() final {} - Slot& emplaceNewSlot(bool, calibration::TFType, calibration::TFType) final; - void finalizeSlot(Slot& slot) final; - bool hasEnoughData(const Slot& slot) const final; - - private: - float mProbabilityThreshold = 1e-6f; - float mProbRelErr = 0.2; // relative error on channel noise to apply the threshold - long mMinROFs = 0; - unsigned int mThreshold = 100; - unsigned int mNumberOfStrobes = 0; -}; - -} // namespace mft -} // namespace o2 - -#endif /* O2_MFT_NOISESLOTCALIBRATOR */ diff --git a/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx b/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx deleted file mode 100644 index b9e590cca0b63..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx +++ /dev/null @@ -1,1660 +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 Alignment -/// Alignment class for the ALICE DiMuon spectrometer -/// -/// MUON specific alignment class which interface to AliMillepede. -/// For each track ProcessTrack calculates the local and global derivatives -/// at each cluster and fill the corresponding local equations. Provide methods -/// for fixing or constraining detection elements for best results. -/// -/// \author Javier Castillo Castellanos -//----------------------------------------------------------------------------- - -#include "MCHAlign/Alignment.h" -#include "MCHAlign/MillePede2.h" -#include "MCHAlign/MillePedeRecord.h" -#include - -#include "MCHTracking/Track.h" -#include "MCHTracking/TrackParam.h" -#include "MCHTracking/Cluster.h" -#include "TGeoManager.h" - -// #include "DataFormatsMCH/ROFRecord.h" -// #include "DataFormatsMCH/TrackMCH.h" -// #include "DataFormatsMCH/Cluster.h" -// #include "DataFormatsMCH/Digit.h" - -// #include "AliMUONGeometryTransformer.h" -// #include "AliMUONGeometryModuleTransformer.h" -// #include "MCHAlign/AliMUONGeometryDetElement.h" -// #include "AliMUONGeometryBuilder.h" -#include "MCHGeometryCreator/Geometry.h" -#include "MCHGeometryTest/Helpers.h" -#include "MCHGeometryTransformer/Transformations.h" -#include "TGeoManager.h" - -// #include "Align/Millepede2Record.h" //to be replaced -// #include "AliMpExMap.h" -// #include "AliMpExMapIterator.h" - -#include "DetectorsCommonDataFormats/AlignParam.h" -#include "Framework/Logger.h" - -#include -#include -#include -#include -#include -#include - -namespace o2 -{ -namespace mch -{ - -using namespace std; - -//_____________________________________________________________________ -// static variables -const Int_t Alignment::fgNDetElemCh[Alignment::fgNCh] = {4, 4, 4, 4, 18, 18, 26, 26, 26, 26}; -const Int_t Alignment::fgSNDetElemCh[Alignment::fgNCh + 1] = {0, 4, 8, 12, 16, 34, 52, 78, 104, 130, 156}; - -// number of detector elements in each half-chamber -const Int_t Alignment::fgNDetElemHalfCh[Alignment::fgNHalfCh] = {2, 2, 2, 2, 2, 2, 2, 2, 9, 9, 9, 9, 13, 13, 13, 13, 13, 13, 13, 13}; - -// list of detector elements for each half chamber -const Int_t Alignment::fgDetElemHalfCh[Alignment::fgNHalfCh][Alignment::fgNDetHalfChMax] = - { - {100, 103, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {101, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {200, 203, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {201, 202, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {300, 303, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {301, 302, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {400, 403, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {401, 402, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - - {500, 501, 502, 503, 504, 514, 515, 516, 517, 0, 0, 0, 0}, - {505, 506, 507, 508, 509, 510, 511, 512, 513, 0, 0, 0, 0}, - - {600, 601, 602, 603, 604, 614, 615, 616, 617, 0, 0, 0, 0}, - {605, 606, 607, 608, 609, 610, 611, 612, 613, 0, 0, 0, 0}, - - {700, 701, 702, 703, 704, 705, 706, 720, 721, 722, 723, 724, 725}, - {707, 708, 709, 710, 711, 712, 713, 714, 715, 716, 717, 718, 719}, - - {800, 801, 802, 803, 804, 805, 806, 820, 821, 822, 823, 824, 825}, - {807, 808, 809, 810, 811, 812, 813, 814, 815, 816, 817, 818, 819}, - - {900, 901, 902, 903, 904, 905, 906, 920, 921, 922, 923, 924, 925}, - {907, 908, 909, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919}, - - {1000, 1001, 1002, 1003, 1004, 1005, 1006, 1020, 1021, 1022, 1023, 1024, 1025}, - {1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019} - -}; - -//_____________________________________________________________________ -/// self initialized array, used for adding constraints -class Array -{ - - public: - /// contructor - Array(void) - { - for (Int_t i = 0; i < Alignment::fNGlobal; ++i) { - values[i] = 0; - } - } - - /// array - Double_t values[Alignment::fNGlobal]; - - private: - /// Not implemented - Array(const Array&); - - /// Not implemented - Array& operator=(const Array&); -}; - -//________________________________________________________________________ -Double_t Square(Double_t x) { return x * x; } - -//_____________________________________________________________________ -Alignment::Alignment() - : TObject(), - fInitialized(kFALSE), - fRunNumber(0), - fBFieldOn(kFALSE), - fRefitStraightTracks(kFALSE), - fStartFac(256), - fResCutInitial(100), - fResCut(100), - fMillepede(0L), // to be modified - fCluster(0L), - fNStdDev(3), - fDetElemNumber(0), - fTrackRecord(), - fTransformCreator(), - fGeoCombiTransInverse(), - fDoEvaluation(kFALSE), - fTrackParamOrig(0), - fTrackParamNew(0), - fTFile(0), - fTTree(0) -{ - /// constructor - fSigma[0] = 1.5e-1; - fSigma[1] = 1.0e-2; - - // default allowed variations - fAllowVar[0] = 0.5; // x - fAllowVar[1] = 0.5; // y - fAllowVar[2] = 0.01; // phi_z - fAllowVar[3] = 5; // z - - // initialize millepede - fMillepede = new MillePede2(); - // fMillepede = new o2::align::Mille("theMilleFile.txt"); // To be replaced by MillePede2 - - // initialize degrees of freedom - // by default all parameters are free - for (Int_t iPar = 0; iPar < fNGlobal; ++iPar) { - fGlobalParameterStatus[iPar] = kFreeParId; - } - - // initialize local equations - for (int i = 0; i < fNLocal; ++i) { - fLocalDerivatives[i] = 0.0; - } - - for (int i = 0; i < fNGlobal; ++i) { - fGlobalDerivatives[i] = 0.0; - } -} - -//_____________________________________________________________________ -// Alignment::~Alignment() -//{ -// /// destructor -//} -// Alignment::~Alignment() = default; -//_____________________________________________________________________ -void Alignment::init(void) -{ - - /// initialize - /** - initialize millipede - must be called after necessary detectors have been fixed, - but before constrains are added and before global parameters initial value are set - */ - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - // assign proper groupID to free parameters - Int_t nGlobal = 0; - for (Int_t iPar = 0; iPar < fNGlobal; ++iPar) { - - if (fGlobalParameterStatus[iPar] == kFixedParId) { - // fixed parameters are left unchanged - continue; - - } else if (fGlobalParameterStatus[iPar] == kFreeParId || fGlobalParameterStatus[iPar] == kGroupBaseId) { - - // free parameters or first element of group are assigned a new group id - fGlobalParameterStatus[iPar] = nGlobal++; - continue; - - } else if (fGlobalParameterStatus[iPar] < kGroupBaseId) { - - // get detector element id from status, get chamber parameter id - const Int_t iDeBase(kGroupBaseId - 1 - fGlobalParameterStatus[iPar]); - const Int_t iParBase = iPar % fgNParCh; - - // check - if (iDeBase < 0 || iDeBase >= iPar / fgNParCh) { - LOG(fatal) << "Group for parameter index " << iPar << " has wrong base detector element: " << iDeBase; - } - - // assign identical group id to current - fGlobalParameterStatus[iPar] = fGlobalParameterStatus[iDeBase * fgNParCh + iParBase]; - LOG(info) << "Parameter " << iPar << " grouped to detector " << iDeBase << " (" << GetParameterMaskString(1 << iParBase).Data() << ")"; - - } else - LOG(fatal) << "Unrecognized parameter status for index " << iPar << ": " << fGlobalParameterStatus[iPar]; - } - - LOG(info) << "Free Parameters: " << nGlobal << " out of " << fNGlobal; - - // initialize millepede - // fMillepede->InitMille(fNGlobal, fNLocal, fNStdDev, fResCut, fResCutInitial, fGlobalParameterStatus); - fMillepede->InitMille(fNGlobal, fNLocal, fNStdDev, fResCut, fResCutInitial); // MillePede2 implementation - - fInitialized = kTRUE; - - // some debug output - for (Int_t iPar = 0; iPar < fgNParCh; ++iPar) { - LOG(info) << "fAllowVar[" << iPar << "]= " << fAllowVar[iPar]; - } - - // set allowed variations for all parameters - for (Int_t iDet = 0; iDet < fgNDetElem; ++iDet) { - for (Int_t iPar = 0; iPar < fgNParCh; ++iPar) { - fMillepede->SetParSigma(iDet * fgNParCh + iPar, fAllowVar[iPar]); - } - } - - // Set iterations - if (fStartFac > 1) { - fMillepede->SetIterations(fStartFac); - } - // setup monitoring TFile - if (fDoEvaluation && fRefitStraightTracks) { - fTFile = new TFile("Alignment.root", "RECREATE"); - fTTree = new TTree("TreeE", "Evaluation"); - - const Int_t kSplitlevel = 98; - const Int_t kBufsize = 32000; - - fTrackParamOrig = new LocalTrackParam(); - fTTree->Branch("fTrackParamOrig", "LocalTrackParam", &fTrackParamOrig, kBufsize, kSplitlevel); - - fTrackParamNew = new LocalTrackParam(); - fTTree->Branch("fTrackParamNew", "LocalTrackParam", &fTrackParamNew, kBufsize, kSplitlevel); - } -} - -//_____________________________________________________ -void Alignment::terminate(void) -{ - LOG(info) << "Closing Evaluation TFile"; - if (fTFile && fTTree) { - fTFile->cd(); - fTTree->Write(); - fTFile->Close(); - } -} - -//_____________________________________________________ -MillePedeRecord* Alignment::ProcessTrack(Track& track, Bool_t doAlignment, Double_t weight) -{ - /// process track for alignment minimization - /** - returns the alignment records for this track. - They can be stored in some output for later reprocessing. - */ - - // reset track records - fTrackRecord.Reset(); - if (fMillepede->GetRecord()) { - fMillepede->GetRecord()->Reset(); - } - - // loop over clusters to get starting values - Bool_t first(kTRUE); - // if (!trackParam) - // continue; - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get cluster - const Cluster* Cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // for first valid cluster, save track position as "starting" values - if (first) { - - first = kFALSE; - FillTrackParamData(&*itTrackParam); - fTrackPos0[0] = fTrackPos[0]; - fTrackPos0[1] = fTrackPos[1]; - fTrackPos0[2] = fTrackPos[2]; - fTrackSlope0[0] = fTrackSlope[0]; - fTrackSlope0[1] = fTrackSlope[1]; - - break; - } - } - - // redo straight track fit - if (fRefitStraightTracks) { - - // refit straight track - const LocalTrackParam trackParam(RefitStraightTrack(track, fTrackPos0[2])); - - // fill evaluation tree - if (fTrackParamOrig) { - fTrackParamOrig->fTrackX = fTrackPos0[0]; - fTrackParamOrig->fTrackY = fTrackPos0[1]; - fTrackParamOrig->fTrackZ = fTrackPos0[2]; - fTrackParamOrig->fTrackSlopeX = fTrackSlope[0]; - fTrackParamOrig->fTrackSlopeY = fTrackSlope[1]; - } - - // new ones - if (fTrackParamNew) { - fTrackParamNew->fTrackX = trackParam.fTrackX; - fTrackParamNew->fTrackY = trackParam.fTrackY; - fTrackParamNew->fTrackZ = trackParam.fTrackZ; - fTrackParamNew->fTrackSlopeX = trackParam.fTrackSlopeX; - fTrackParamNew->fTrackSlopeY = trackParam.fTrackSlopeY; - } - - if (fTTree) - fTTree->Fill(); - - /* - copy new parameters to stored ones for derivatives calculation - this is done only if BFieldOn is false, for which these parameters are used - */ - if (!fBFieldOn) { - fTrackPos0[0] = trackParam.fTrackX; - fTrackPos0[1] = trackParam.fTrackY; - fTrackPos0[2] = trackParam.fTrackZ; - fTrackSlope[0] = trackParam.fTrackSlopeX; - fTrackSlope[1] = trackParam.fTrackSlopeY; - } - } - - // second loop to perform alignment - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get track parameters - if (!&*itTrackParam) - continue; - - // get cluster - const Cluster* cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // fill local variables for this position --> one measurement - FillDetElemData(cluster); - FillRecPointData(cluster); - FillTrackParamData(&*itTrackParam); - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // calculate measurements - if (fBFieldOn) { - - // use residuals (cluster - track) for measurement - fMeas[0] = r[0] * (fClustPos[0] - fTrackPos[0]) + r[1] * (fClustPos[1] - fTrackPos[1]); - fMeas[1] = r[3] * (fClustPos[0] - fTrackPos[0]) + r[4] * (fClustPos[1] - fTrackPos[1]); - - } else { - - // use cluster position for measurement - fMeas[0] = (r[0] * fClustPos[0] + r[1] * fClustPos[1]); - fMeas[1] = (r[3] * fClustPos[0] + r[4] * fClustPos[1]); - } - - // Set local equations - LocalEquationX(); - LocalEquationY(); - } - - // copy track record - fMillepede->SetRecordRun(fRunNumber); - fMillepede->SetRecordWeight(weight); - fTrackRecord = *fMillepede->GetRecord(); - - // save record data - if (doAlignment) { - fMillepede->SaveRecordData(); - fMillepede->CloseDataRecStorage(); - } - - // return record - return &fTrackRecord; -} - -//______________________________________________________________________________ -void Alignment::ProcessTrack(MillePedeRecord* trackRecord) -{ - LOG(fatal) << __PRETTY_FUNCTION__ << " is disabled"; - - /// process track record - if (!trackRecord) - return; - - // // make sure record storage is initialized - if (!fMillepede->GetRecord()) { - fMillepede->InitDataRecStorage(kFalse); - } - // // copy content - *fMillepede->GetRecord() = *trackRecord; - - // save record - fMillepede->SaveRecordData(); - // write to local file - fMillepede->CloseDataRecStorage(); - - return; -} - -//_____________________________________________________________________ -void Alignment::FixAll(UInt_t mask) -{ - /// fix parameters matching mask, for all chambers - LOG(info) << "Fixing " << GetParameterMaskString(mask).Data() << " for all detector elements"; - - // fix all stations - for (Int_t i = 0; i < fgNDetElem; ++i) { - if (mask & ParX) - FixParameter(i, 0); - if (mask & ParY) - FixParameter(i, 1); - if (mask & ParTZ) - FixParameter(i, 2); - if (mask & ParZ) - FixParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::FixChamber(Int_t iCh, UInt_t mask) -{ - /// fix parameters matching mask, for all detector elements in a given chamber, counting from 1 - - // check boundaries - if (iCh < 1 || iCh > 10) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - // get first and last element - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - LOG(info) << "Fixing " << GetParameterMaskString(mask).Data() << " for detector element " << i; - - if (mask & ParX) - FixParameter(i, 0); - if (mask & ParY) - FixParameter(i, 1); - if (mask & ParTZ) - FixParameter(i, 2); - if (mask & ParZ) - FixParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::FixDetElem(Int_t iDetElemId, UInt_t mask) -{ - /// fix parameters matching mask, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - FixParameter(iDet, 0); - if (mask & ParY) - FixParameter(iDet, 1); - if (mask & ParTZ) - FixParameter(iDet, 2); - if (mask & ParZ) - FixParameter(iDet, 3); -} - -//_____________________________________________________________________ -void Alignment::FixHalfSpectrometer(const Bool_t* lChOnOff, UInt_t sidesMask, UInt_t mask) -{ - - /// Fix parameters matching mask for all detectors in selected chambers and selected sides of the spectrometer - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - if (!lChOnOff[iCh - 1]) - continue; - - // get detector element in chamber - Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - - // skip detector if its side is off - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber == 1 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber == 2 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber == 3 && !(sidesMask & SideBottomRight)) - continue; - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 5 && lDetElemNumber <= 10 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 11 && lDetElemNumber <= 13 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(sidesMask & SideBottomRight)) - continue; - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(sidesMask & SideBottomRight)) - continue; - } - - // detector is accepted, fix it - FixDetElem(i, mask); - } -} - -//______________________________________________________________________ -void Alignment::FixParameter(Int_t iPar) -{ - - /// fix a given parameter, counting from 0 - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - fGlobalParameterStatus[iPar] = kFixedParId; -} - -//_____________________________________________________________________ -void Alignment::ReleaseChamber(Int_t iCh, UInt_t mask) -{ - /// release parameters matching mask, for all detector elements in a given chamber, counting from 1 - - // check boundaries - if (iCh < 1 || iCh > 10) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - // get first and last element - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - LOG(info) << "Releasing " << GetParameterMaskString(mask).Data() << " for detector element " << i; - - if (mask & ParX) - ReleaseParameter(i, 0); - if (mask & ParY) - ReleaseParameter(i, 1); - if (mask & ParTZ) - ReleaseParameter(i, 2); - if (mask & ParZ) - ReleaseParameter(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::ReleaseDetElem(Int_t iDetElemId, UInt_t mask) -{ - /// release parameters matching mask, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - ReleaseParameter(iDet, 0); - if (mask & ParY) - ReleaseParameter(iDet, 1); - if (mask & ParTZ) - ReleaseParameter(iDet, 2); - if (mask & ParZ) - ReleaseParameter(iDet, 3); -} - -//______________________________________________________________________ -void Alignment::ReleaseParameter(Int_t iPar) -{ - - /// release a given parameter, counting from 0 - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - fGlobalParameterStatus[iPar] = kFreeParId; -} - -//_____________________________________________________________________ -void Alignment::GroupChamber(Int_t iCh, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in a given chamber, counting from 1 - if (iCh < 1 || iCh > fgNCh) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - const Int_t detElemMin = 100 * iCh; - const Int_t detElemMax = 100 * iCh + fgNDetElemCh[iCh] - 1; - GroupDetElems(detElemMin, detElemMax, mask); -} - -//_____________________________________________________________________ -void Alignment::GroupHalfChamber(Int_t iCh, Int_t iHalf, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in a given tracking module (= half chamber), counting from 0 - if (iCh < 1 || iCh > fgNCh) { - LOG(fatal) << "Invalid chamber index " << iCh; - } - - if (iHalf < 0 || iHalf > 1) { - LOG(fatal) << "Invalid half chamber index " << iHalf; - } - - const Int_t iHalfCh = 2 * (iCh - 1) + iHalf; - GroupDetElems(&fgDetElemHalfCh[iHalfCh][0], fgNDetElemHalfCh[iHalfCh], mask); -} - -//_____________________________________________________________________ -void Alignment::GroupDetElems(Int_t detElemMin, Int_t detElemMax, UInt_t mask) -{ - /// group parameters matching mask for all detector elements between min and max - // check number of detector elements - const Int_t nDetElem = detElemMax - detElemMin + 1; - if (nDetElem < 2) { - LOG(fatal) << "Requested group of DEs " << detElemMin << "-" << detElemMax << " contains less than 2 DE's"; - } - - // create list - Int_t* detElemList = new int[nDetElem]; - for (Int_t i = 0; i < nDetElem; ++i) { - detElemList[i] = detElemMin + i; - } - - // group - GroupDetElems(detElemList, nDetElem, mask); - delete[] detElemList; -} - -//_____________________________________________________________________ -void Alignment::GroupDetElems(const Int_t* detElemList, Int_t nDetElem, UInt_t mask) -{ - /// group parameters matching mask for all detector elements in list - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - const Int_t iDeBase(GetDetElemNumber(detElemList[0])); - for (Int_t i = 0; i < nDetElem; ++i) { - const Int_t iDeCurrent(GetDetElemNumber(detElemList[i])); - if (mask & ParX) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 0] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParY) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 1] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParTZ) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 2] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - if (mask & ParZ) - fGlobalParameterStatus[iDeCurrent * fgNParCh + 3] = (i == 0) ? kGroupBaseId : (kGroupBaseId - iDeBase - 1); - - if (i == 0) - LOG(info) << "Creating new group for detector " << detElemList[i] << " and variable " << GetParameterMaskString(mask).Data(); - else - LOG(info) << "Adding detector element " << detElemList[i] << " to current group"; - } -} - -//______________________________________________________________________ -void Alignment::SetChamberNonLinear(Int_t iCh, UInt_t mask) -{ - /// Set parameters matching mask as non linear, for all detector elements in a given chamber, counting from 1 - const Int_t iDetElemFirst = fgSNDetElemCh[iCh - 1]; - const Int_t iDetElemLast = fgSNDetElemCh[iCh]; - for (Int_t i = iDetElemFirst; i < iDetElemLast; ++i) { - - if (mask & ParX) - SetParameterNonLinear(i, 0); - if (mask & ParY) - SetParameterNonLinear(i, 1); - if (mask & ParTZ) - SetParameterNonLinear(i, 2); - if (mask & ParZ) - SetParameterNonLinear(i, 3); - } -} - -//_____________________________________________________________________ -void Alignment::SetDetElemNonLinear(Int_t iDetElemId, UInt_t mask) -{ - /// Set parameters matching mask as non linear, for a given detector element, counting from 0 - const Int_t iDet(GetDetElemNumber(iDetElemId)); - if (mask & ParX) - SetParameterNonLinear(iDet, 0); - if (mask & ParY) - SetParameterNonLinear(iDet, 1); - if (mask & ParTZ) - SetParameterNonLinear(iDet, 2); - if (mask & ParZ) - SetParameterNonLinear(iDet, 3); -} - -//______________________________________________________________________ -void Alignment::SetParameterNonLinear(Int_t iPar) -{ - /// Set nonlinear flag for parameter iPar - if (!fInitialized) { - LOG(fatal) << "Millepede not initialized"; - } - - fMillepede->SetNonLinear(iPar); - LOG(info) << "Parameter " << iPar << " set to non linear "; -} - -//______________________________________________________________________ -void Alignment::AddConstraints(const Bool_t* lChOnOff, UInt_t mask) -{ - /// Add constraint equations for selected chambers and degrees of freedom - - Array fConstraintX; - Array fConstraintY; - Array fConstraintTZ; - Array fConstraintZ; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - if (lChOnOff[iCh - 1]) { - - if (mask & ParX) - fConstraintX.values[i * fgNParCh + 0] = 1.0; - if (mask & ParY) - fConstraintY.values[i * fgNParCh + 1] = 1.0; - if (mask & ParTZ) - fConstraintTZ.values[i * fgNParCh + 2] = 1.0; - if (mask & ParZ) - fConstraintTZ.values[i * fgNParCh + 3] = 1.0; - } - } - - if (mask & ParX) - AddConstraint(fConstraintX.values, 0.0); - if (mask & ParY) - AddConstraint(fConstraintY.values, 0.0); - if (mask & ParTZ) - AddConstraint(fConstraintTZ.values, 0.0); - if (mask & ParZ) - AddConstraint(fConstraintZ.values, 0.0); -} - -//______________________________________________________________________ -void Alignment::AddConstraints(const Bool_t* lChOnOff, const Bool_t* lVarXYT, UInt_t sidesMask) -{ - /* - questions: - - is there not redundancy/inconsistency between lDetTLBR and lSpecLROnOff ? shouldn't we use only lDetTLBR ? - - why is weight ignored for ConstrainT and ConstrainB - - why is there no constrain on z - */ - - /// Add constraint equations for selected chambers, degrees of freedom and detector half - Double_t lMeanY = 0.; - Double_t lSigmaY = 0.; - Double_t lMeanZ = 0.; - Double_t lSigmaZ = 0.; - Int_t lNDetElem = 0; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - - // skip detector if chamber is off - if (lChOnOff[iCh - 1]) - continue; - - // get detector element id from detector element number - const Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - const Int_t lDetElemId = iCh * 100 + lDetElemNumber; - - // skip detector if its side is off - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber == 1 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber == 2 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber == 3 && !(sidesMask & SideBottomRight)) - continue; - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 5 && lDetElemNumber <= 10 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 11 && lDetElemNumber <= 13 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(sidesMask & SideBottomRight)) - continue; - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(sidesMask & SideTopRight)) - continue; - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(sidesMask & SideTopLeft)) - continue; - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(sidesMask & SideBottomLeft)) - continue; - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(sidesMask & SideBottomRight)) - continue; - } - - // get global x, y and z position - Double_t lDetElemGloX = 0.; - Double_t lDetElemGloY = 0.; - Double_t lDetElemGloZ = 0.; - - auto fTransform = fTransformCreator(lDetElemId); - o2::math_utils::Point3D SlatPos{0.0, 0.0, 0.0}; - o2::math_utils::Point3D GlobalPos; - - fTransform.LocalToMaster(SlatPos, GlobalPos); - lDetElemGloX = GlobalPos.x(); - lDetElemGloY = GlobalPos.y(); - lDetElemGloZ = GlobalPos.z(); - // fTransform->Local2Global(lDetElemId, 0, 0, 0, lDetElemGloX, lDetElemGloY, lDetElemGloZ); - - // increment mean Y, mean Z, sigmas and number of accepted detectors - lMeanY += lDetElemGloY; - lSigmaY += lDetElemGloY * lDetElemGloY; - lMeanZ += lDetElemGloZ; - lSigmaZ += lDetElemGloZ * lDetElemGloZ; - lNDetElem++; - } - - // calculate mean values - lMeanY /= lNDetElem; - lSigmaY /= lNDetElem; - lSigmaY = TMath::Sqrt(lSigmaY - lMeanY * lMeanY); - lMeanZ /= lNDetElem; - lSigmaZ /= lNDetElem; - lSigmaZ = TMath::Sqrt(lSigmaZ - lMeanZ * lMeanZ); - LOG(info) << "Used " << lNDetElem << " DetElem, MeanZ= " << lMeanZ << ", SigmaZ= " << lSigmaZ; - - // create all possible arrays - Array fConstraintX[4]; // Array for constraint equation X - Array fConstraintY[4]; // Array for constraint equation Y - Array fConstraintP[4]; // Array for constraint equation P - Array fConstraintXZ[4]; // Array for constraint equation X vs Z - Array fConstraintYZ[4]; // Array for constraint equation Y vs Z - Array fConstraintPZ[4]; // Array for constraint equation P vs Z - - // do we really need these ? - Array fConstraintXY[4]; // Array for constraint equation X vs Y - Array fConstraintYY[4]; // Array for constraint equation Y vs Y - Array fConstraintPY[4]; // Array for constraint equation P vs Y - - // fill Bool_t sides array based on masks, for convenience - Bool_t lDetTLBR[4]; - lDetTLBR[0] = sidesMask & SideTop; - lDetTLBR[1] = sidesMask & SideLeft; - lDetTLBR[2] = sidesMask & SideBottom; - lDetTLBR[3] = sidesMask & SideRight; - - for (Int_t i = 0; i < fgNDetElem; ++i) { - - // get chamber matching detector - const Int_t iCh(GetChamberId(i)); - - // skip detector if chamber is off - if (!lChOnOff[iCh - 1]) - continue; - - // get detector element id from detector element number - const Int_t lDetElemNumber = i - fgSNDetElemCh[iCh - 1]; - const Int_t lDetElemId = iCh * 100 + lDetElemNumber; - - // get global x, y and z position - Double_t lDetElemGloX = 0.; - Double_t lDetElemGloY = 0.; - Double_t lDetElemGloZ = 0.; - - auto fTransform = fTransformCreator(lDetElemId); - o2::math_utils::Point3D SlatPos{0.0, 0.0, 0.0}; - o2::math_utils::Point3D GlobalPos; - - fTransform.LocalToMaster(SlatPos, GlobalPos); - lDetElemGloX = GlobalPos.x(); - lDetElemGloY = GlobalPos.y(); - lDetElemGloZ = GlobalPos.z(); - // fTransform->Local2Global(lDetElemId, 0, 0, 0, lDetElemGloX, lDetElemGloY, lDetElemGloZ); - - // loop over sides - for (Int_t iSide = 0; iSide < 4; iSide++) { - - // skip if side is not selected - if (!lDetTLBR[iSide]) - continue; - - // skip detector if it is not in the selected side - // stations 1 and 2 - if (iCh >= 1 && iCh <= 4) { - if (lDetElemNumber == 0 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber == 1 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber == 2 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber == 3 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // station 3 - if (iCh >= 5 && iCh <= 6) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 4 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber >= 5 && lDetElemNumber <= 9 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber >= 10 && lDetElemNumber <= 13 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber >= 14 && lDetElemNumber <= 17 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // stations 4 and 5 - if (iCh >= 7 && iCh <= 10) { - if (lDetElemNumber >= 0 && lDetElemNumber <= 6 && !(iSide == 0 || iSide == 3)) - continue; // top-right - if (lDetElemNumber >= 7 && lDetElemNumber <= 13 && !(iSide == 0 || iSide == 1)) - continue; // top-left - if (lDetElemNumber >= 14 && lDetElemNumber <= 19 && !(iSide == 2 || iSide == 1)) - continue; // bottom-left - if (lDetElemNumber >= 20 && lDetElemNumber <= 25 && !(iSide == 2 || iSide == 3)) - continue; // bottom-right - } - - // constrain x - if (lVarXYT[0]) - fConstraintX[iSide].values[i * fgNParCh + 0] = 1; - - // constrain y - if (lVarXYT[1]) - fConstraintY[iSide].values[i * fgNParCh + 1] = 1; - - // constrain phi (rotation around z) - if (lVarXYT[2]) - fConstraintP[iSide].values[i * fgNParCh + 2] = 1; - - // x-z shearing - if (lVarXYT[3]) - fConstraintXZ[iSide].values[i * fgNParCh + 0] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // y-z shearing - if (lVarXYT[4]) - fConstraintYZ[iSide].values[i * fgNParCh + 1] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // phi-z shearing - if (lVarXYT[5]) - fConstraintPZ[iSide].values[i * fgNParCh + 2] = (lDetElemGloZ - lMeanZ) / lSigmaZ; - - // x-y shearing - if (lVarXYT[6]) - fConstraintXY[iSide].values[i * fgNParCh + 0] = (lDetElemGloY - lMeanY) / lSigmaY; - - // y-y shearing - if (lVarXYT[7]) - fConstraintYY[iSide].values[i * fgNParCh + 1] = (lDetElemGloY - lMeanY) / lSigmaY; - - // phi-y shearing - if (lVarXYT[8]) - fConstraintPY[iSide].values[i * fgNParCh + 2] = (lDetElemGloY - lMeanY) / lSigmaY; - } - } - - // pass constraints to millepede - for (Int_t iSide = 0; iSide < 4; iSide++) { - // skip if side is not selected - if (!lDetTLBR[iSide]) - continue; - - if (lVarXYT[0]) - AddConstraint(fConstraintX[iSide].values, 0.0); - if (lVarXYT[1]) - AddConstraint(fConstraintY[iSide].values, 0.0); - if (lVarXYT[2]) - AddConstraint(fConstraintP[iSide].values, 0.0); - if (lVarXYT[3]) - AddConstraint(fConstraintXZ[iSide].values, 0.0); - if (lVarXYT[4]) - AddConstraint(fConstraintYZ[iSide].values, 0.0); - if (lVarXYT[5]) - AddConstraint(fConstraintPZ[iSide].values, 0.0); - if (lVarXYT[6]) - AddConstraint(fConstraintXY[iSide].values, 0.0); - if (lVarXYT[7]) - AddConstraint(fConstraintYY[iSide].values, 0.0); - if (lVarXYT[8]) - AddConstraint(fConstraintPY[iSide].values, 0.0); - } -} - -//______________________________________________________________________ -void Alignment::InitGlobalParameters(Double_t* par) -{ - /// Initialize global parameters with par array - if (!fInitialized) { - LOG(fatal) << "Millepede is not initialized"; - } - - fMillepede->SetGlobalParameters(par); -} - -//______________________________________________________________________ -void Alignment::SetAllowedVariation(Int_t iPar, Double_t value) -{ - /// "Encouraged" variation for degrees of freedom - // check initialization - if (fInitialized) { - LOG(fatal) << "Millepede already initialized"; - } - - // check initialization - if (!(iPar >= 0 && iPar < fgNParCh)) { - LOG(fatal) << "Invalid index: " << iPar; - } - - fAllowVar[iPar] = value; -} - -//______________________________________________________________________ -void Alignment::SetSigmaXY(Double_t sigmaX, Double_t sigmaY) -{ - - /// Set expected measurement resolution - fSigma[0] = sigmaX; - fSigma[1] = sigmaY; - - // print - for (Int_t i = 0; i < 2; ++i) { - LOG(info) << "fSigma[" << i << "] =" << fSigma[i]; - } -} - -//_____________________________________________________ -void Alignment::GlobalFit(Double_t* parameters, Double_t* errors, Double_t* pulls) -{ - - /// Call global fit; Global parameters are stored in parameters - fMillepede->GlobalFit(parameters, errors, pulls); - - LOG(info) << "Done fitting global parameters"; - for (int iDet = 0; iDet < fgNDetElem; ++iDet) { - LOG(info) << iDet << " " << parameters[iDet * fgNParCh + 0] << " " << parameters[iDet * fgNParCh + 1] << " " << parameters[iDet * fgNParCh + 3] << " " << parameters[iDet * fgNParCh + 2]; - } -} - -//_____________________________________________________ -void Alignment::PrintGlobalParameters() const -{ - fMillepede->PrintGlobalParameters(); -} - -//_____________________________________________________ -Double_t Alignment::GetParError(Int_t iPar) const -{ - return fMillepede->GetParError(iPar); -} - -// //______________________________________________________________________ -// AliMUONGeometryTransformer* Alignment::ReAlign( -// const AliMUONGeometryTransformer* transformer, -// const double* misAlignments, Bool_t) -// { - -// /// Returns a new AliMUONGeometryTransformer with the found misalignments -// /// applied. - -// // Takes the internal geometry module transformers, copies them -// // and gets the Detection Elements from them. -// // Takes misalignment parameters and applies these -// // to the local transform of the Detection Element -// // Obtains the global transform by multiplying the module transformer -// // transformation with the local transformation -// // Applies the global transform to a new detection element -// // Adds the new detection element to a new module transformer -// // Adds the new module transformer to a new geometry transformer -// // Returns the new geometry transformer - -// Double_t lModuleMisAlignment[fgNParCh] = {0}; -// Double_t lDetElemMisAlignment[fgNParCh] = {0}; -// const TClonesArray* oldMisAlignArray(transformer->GetMisAlignmentData()); - -// AliMUONGeometryTransformer* newGeometryTransformer = new AliMUONGeometryTransformer(); -// for (Int_t iMt = 0; iMt < transformer->GetNofModuleTransformers(); ++iMt) { - -// // module transformers -// const AliMUONGeometryModuleTransformer* kModuleTransformer = transformer->GetModuleTransformer(iMt, kTRUE); - -// AliMUONGeometryModuleTransformer* newModuleTransformer = new AliMUONGeometryModuleTransformer(iMt); -// newGeometryTransformer->AddModuleTransformer(newModuleTransformer); - -// // get transformation -// TGeoHMatrix deltaModuleTransform(DeltaTransform(lModuleMisAlignment)); - -// // update module -// TGeoHMatrix moduleTransform(*kModuleTransformer->GetTransformation()); -// TGeoHMatrix newModuleTransform(AliMUONGeometryBuilder::Multiply(deltaModuleTransform, moduleTransform)); -// newModuleTransformer->SetTransformation(newModuleTransform); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const Int_t moduleId(kModuleTransformer->GetModuleId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, moduleId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// deltaModuleTransform.Multiply(&oldMatrix); -// } -// } - -// // Create module mis alignment matrix -// newGeometryTransformer->AddMisAlignModule(kModuleTransformer->GetModuleId(), deltaModuleTransform); - -// AliMpExMap* detElements = kModuleTransformer->GetDetElementStore(); - -// TIter next(detElements->CreateIterator()); -// AliMUONGeometryDetElement* detElement; -// Int_t iDe(-1); -// while ((detElement = static_cast(next()))) { -// ++iDe; -// // make a new detection element -// AliMUONGeometryDetElement* newDetElement = new AliMUONGeometryDetElement(detElement->GetId(), detElement->GetVolumePath()); -// TString lDetElemName(detElement->GetDEName()); -// lDetElemName.ReplaceAll("DE", ""); - -// // store detector element id and number -// const Int_t iDetElemId = lDetElemName.Atoi(); -// if (DetElemIsValid(iDetElemId)) { - -// const Int_t iDetElemNumber(GetDetElemNumber(iDetElemId)); - -// for (int i = 0; i < fgNParCh; ++i) { -// lDetElemMisAlignment[i] = 0.0; -// if (iMt < fgNTrkMod) { -// lDetElemMisAlignment[i] = misAlignments[iDetElemNumber * fgNParCh + i]; -// } -// } - -// // get transformation -// TGeoHMatrix deltaGlobalTransform(DeltaTransform(lDetElemMisAlignment)); - -// // update module -// TGeoHMatrix globalTransform(*detElement->GetGlobalTransformation()); -// TGeoHMatrix newGlobalTransform(AliMUONGeometryBuilder::Multiply(deltaGlobalTransform, globalTransform)); -// newDetElement->SetGlobalTransformation(newGlobalTransform); -// newModuleTransformer->GetDetElementStore()->Add(newDetElement->GetId(), newDetElement); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const int detElemId(detElement->GetId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, detElemId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// deltaGlobalTransform.Multiply(&oldMatrix); -// } -// } - -// // Create misalignment matrix -// newGeometryTransformer->AddMisAlignDetElement(detElement->GetId(), deltaGlobalTransform); - -// } else { - -// // "invalid" detector elements come from MTR and are left unchanged -// Aliinfo(Form("Keeping detElement %i unchanged", iDetElemId)); - -// // update module -// TGeoHMatrix globalTransform(*detElement->GetGlobalTransformation()); -// newDetElement->SetGlobalTransformation(globalTransform); -// newModuleTransformer->GetDetElementStore()->Add(newDetElement->GetId(), newDetElement); - -// // Get matching old alignment and update current matrix accordingly -// if (oldMisAlignArray) { - -// const AliAlignObjMatrix* oldAlignObj(0); -// const int detElemId(detElement->GetId()); -// const Int_t volId = AliGeomManager::LayerToVolUID(AliGeomManager::kMUON, detElemId); -// for (Int_t pos = 0; pos < oldMisAlignArray->GetEntriesFast(); ++pos) { - -// const AliAlignObjMatrix* localAlignObj(dynamic_cast(oldMisAlignArray->At(pos))); -// if (localAlignObj && localAlignObj->GetVolUID() == volId) { -// oldAlignObj = localAlignObj; -// break; -// } -// } - -// // multiply -// if (oldAlignObj) { - -// TGeoHMatrix oldMatrix; -// oldAlignObj->GetMatrix(oldMatrix); -// newGeometryTransformer->AddMisAlignDetElement(detElement->GetId(), oldMatrix); -// } -// } -// } -// } - -// newGeometryTransformer->AddModuleTransformer(newModuleTransformer); -// } - -// return newGeometryTransformer; -// } - -//______________________________________________________________________ -void Alignment::SetAlignmentResolution(const TClonesArray* misAlignArray, Int_t rChId, Double_t chResX, Double_t chResY, Double_t deResX, Double_t deResY) -{ - - /// Set alignment resolution to misalign objects to be stored in CDB - /// if rChId is > 0 set parameters for this chamber only, counting from 1 - TMatrixDSym mChCorrMatrix(6); - mChCorrMatrix[0][0] = chResX * chResX; - mChCorrMatrix[1][1] = chResY * chResY; - - TMatrixDSym mDECorrMatrix(6); - mDECorrMatrix[0][0] = deResX * deResX; - mDECorrMatrix[1][1] = deResY * deResY; - - o2::detectors::AlignParam* alignMat = 0x0; - - for (Int_t chId = 0; chId <= 9; ++chId) { - - // skip chamber if selection is valid, and does not match - if (rChId > 0 && chId + 1 != rChId) - continue; - - TString chName1; - TString chName2; - if (chId < 4) { - - chName1 = Form("GM%d", chId); - chName2 = Form("GM%d", chId); - - } else { - - chName1 = Form("GM%d", 4 + (chId - 4) * 2); - chName2 = Form("GM%d", 4 + (chId - 4) * 2 + 1); - } - - for (int i = 0; i < misAlignArray->GetEntries(); ++i) { - - alignMat = (o2::detectors::AlignParam*)misAlignArray->At(i); - TString volName(alignMat->getSymName()); - if ((volName.Contains(chName1) && - ((volName.Last('/') == volName.Index(chName1) + chName1.Length()) || - (volName.Length() == volName.Index(chName1) + chName1.Length()))) || - (volName.Contains(chName2) && - ((volName.Last('/') == volName.Index(chName2) + chName2.Length()) || - (volName.Length() == volName.Index(chName2) + chName2.Length())))) { - - volName.Remove(0, volName.Last('/') + 1); - // if (volName.Contains("GM")){ - // alignMat->SetCorrMatrix(mChCorrMatrix); - // }else if (volName.Contains("DE")){ - // alignMat->SetCorrMatrix(mDECorrMatrix); - // } - } - } - } -} - -//_____________________________________________________ -LocalTrackParam Alignment::RefitStraightTrack(Track& track, Double_t z0) const -{ - - // initialize matrices - TMatrixD AtGASum(4, 4); - AtGASum.Zero(); - - TMatrixD AtGMSum(4, 1); - AtGMSum.Zero(); - - // loop over clusters - for (auto itTrackParam(track.begin()); itTrackParam != track.end(); ++itTrackParam) { - - // get track parameters - if (!&*itTrackParam) - continue; - - // get cluster - const Cluster* cluster = itTrackParam->getClusterPtr(); - if (!cluster) - continue; - - // projection matrix - TMatrixD A(2, 4); - A.Zero(); - A(0, 0) = 1; - A(0, 2) = (cluster->getZ() - z0); - A(1, 1) = 1; - A(1, 3) = (cluster->getZ() - z0); - - TMatrixD At(TMatrixD::kTransposed, A); - - // gain matrix - TMatrixD G(2, 2); - G.Zero(); - G(0, 0) = 1.0 / Square(cluster->getEx()); - G(1, 1) = 1.0 / Square(cluster->getEy()); - - const TMatrixD AtG(At, TMatrixD::kMult, G); - const TMatrixD AtGA(AtG, TMatrixD::kMult, A); - AtGASum += AtGA; - - // measurement - TMatrixD M(2, 1); - M(0, 0) = cluster->getX(); - M(1, 0) = cluster->getY(); - const TMatrixD AtGM(AtG, TMatrixD::kMult, M); - AtGMSum += AtGM; - } - - // perform inversion - TMatrixD AtGASumInv(TMatrixD::kInverted, AtGASum); - TMatrixD X(AtGASumInv, TMatrixD::kMult, AtGMSum); - - // // TODO: compare with initial track parameters - // Aliinfo( Form( "x: %.3f vs %.3f", fTrackPos0[0], X(0,0) ) ); - // Aliinfo( Form( "y: %.3f vs %.3f", fTrackPos0[1], X(1,0) ) ); - // Aliinfo( Form( "dxdz: %.6g vs %.6g", fTrackSlope0[0], X(2,0) ) ); - // Aliinfo( Form( "dydz: %.6g vs %.6g\n", fTrackSlope0[1], X(3,0) ) ); - - // fill output parameters - LocalTrackParam out; - out.fTrackX = X(0, 0); - out.fTrackY = X(1, 0); - out.fTrackZ = z0; - out.fTrackSlopeX = X(2, 0); - out.fTrackSlopeY = X(3, 0); - - return out; -} - -//_____________________________________________________ -void Alignment::FillDetElemData(const Cluster* cluster) -{ - // LOG(fatal) << __PRETTY_FUNCTION__ << " is disabled"; - LOG(info) << __PRETTY_FUNCTION__ << " is enabled"; - - /// Get information of current detection element - // get detector element number from Alice ID - const Int_t detElemId = cluster->getDEId(); - fDetElemNumber = GetDetElemNumber(detElemId); - - // get detector element - // const AliMUONGeometryDetElement detElement(detElemId); - auto fTransform = fTransformCreator(detElemId); - /* - get the global transformation matrix and store its inverse, in order to manually perform - the global to Local transformations needed to calculate the derivatives - */ - // fTransform = fTransform.Inverse(); - // fTransform.GetTransformMatrix(fGeoCombiTransInverse); -} - -//______________________________________________________________________ -void Alignment::FillRecPointData(const Cluster* cluster) -{ - - /// Get information of current cluster - fClustPos[0] = cluster->getX(); - fClustPos[1] = cluster->getY(); - fClustPos[2] = cluster->getZ(); -} - -//______________________________________________________________________ -void Alignment::FillTrackParamData(const TrackParam* trackParam) -{ - - /// Get information of current track at current cluster - fTrackPos[0] = trackParam->getNonBendingCoor(); - fTrackPos[1] = trackParam->getBendingCoor(); - fTrackPos[2] = trackParam->getZ(); - fTrackSlope[0] = trackParam->getNonBendingSlope(); - fTrackSlope[1] = trackParam->getBendingSlope(); -} - -//______________________________________________________________________ -void Alignment::LocalEquationX(void) -{ - /// local equation along X - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // local derivatives - SetLocalDerivative(0, r[0]); - SetLocalDerivative(1, r[0] * (fTrackPos[2] - fTrackPos0[2])); - SetLocalDerivative(2, r[1]); - SetLocalDerivative(3, r[1] * (fTrackPos[2] - fTrackPos0[2])); - - // global derivatives - /* - alignment parameters are - 0: delta_x - 1: delta_y - 2: delta_phiz - 3: delta_z - */ - - SetGlobalDerivative(fDetElemNumber * fgNParCh + 0, -r[0]); - SetGlobalDerivative(fDetElemNumber * fgNParCh + 1, -r[1]); - - if (fBFieldOn) { - - // use local position for derivatives vs 'delta_phi_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[1] * fTrackPos[0] + r[0] * fTrackPos[1]); - - // use local slopes for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[0] * fTrackSlope[0] + r[1] * fTrackSlope[1]); - - } else { - - // local copy of extrapolated track positions - const Double_t trackPosX = fTrackPos0[0] + fTrackSlope0[0] * (fTrackPos[2] - fTrackPos0[2]); - const Double_t trackPosY = fTrackPos0[1] + fTrackSlope0[1] * (fTrackPos[2] - fTrackPos0[2]); - - // use properly extrapolated position for derivatives vs 'delta_phi_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[1] * trackPosX + r[0] * trackPosY); - - // use slopes at origin for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[0] * fTrackSlope0[0] + r[1] * fTrackSlope0[1]); - } - - // store local equation - fMillepede->SetLocalEquation(fGlobalDerivatives, fLocalDerivatives, fMeas[0], fSigma[0]); -} - -//______________________________________________________________________ -void Alignment::LocalEquationY(void) -{ - /// local equation along Y - - // 'inverse' (GlobalToLocal) rotation matrix - const Double_t* r(fGeoCombiTransInverse.GetRotationMatrix()); - - // store local derivatives - SetLocalDerivative(0, r[3]); - SetLocalDerivative(1, r[3] * (fTrackPos[2] - fTrackPos0[2])); - SetLocalDerivative(2, r[4]); - SetLocalDerivative(3, r[4] * (fTrackPos[2] - fTrackPos0[2])); - - // set global derivatives - SetGlobalDerivative(fDetElemNumber * fgNParCh + 0, -r[3]); - SetGlobalDerivative(fDetElemNumber * fgNParCh + 1, -r[4]); - - if (fBFieldOn) { - - // use local position for derivatives vs 'delta_phi' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[4] * fTrackPos[0] + r[3] * fTrackPos[1]); - - // use local slopes for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[3] * fTrackSlope[0] + r[4] * fTrackSlope[1]); - - } else { - - // local copy of extrapolated track positions - const Double_t trackPosX = fTrackPos0[0] + fTrackSlope0[0] * (fTrackPos[2] - fTrackPos0[2]); - const Double_t trackPosY = fTrackPos0[1] + fTrackSlope0[1] * (fTrackPos[2] - fTrackPos0[2]); - - // use properly extrapolated position for derivatives vs 'delta_phi' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 2, -r[4] * trackPosX + r[3] * trackPosY); - - // use slopes at origin for derivatives vs 'delta_z' - SetGlobalDerivative(fDetElemNumber * fgNParCh + 3, r[3] * fTrackSlope0[0] + r[4] * fTrackSlope0[1]); - } - - // store local equation - fMillepede->SetLocalEquation(fGlobalDerivatives, fLocalDerivatives, fMeas[1], fSigma[1]); -} - -//_________________________________________________________________________ -TGeoCombiTrans Alignment::DeltaTransform(const double* lMisAlignment) const -{ - /// Get Delta Transformation, based on alignment parameters - - // translation - const TGeoTranslation deltaTrans(lMisAlignment[0], lMisAlignment[1], lMisAlignment[3]); - - // rotation - TGeoRotation deltaRot; - deltaRot.RotateZ(lMisAlignment[2] * 180. / TMath::Pi()); - - // combined rotation and translation. - return TGeoCombiTrans(deltaTrans, deltaRot); -} - -//______________________________________________________________________ -void Alignment::AddConstraint(Double_t* par, Double_t value) -{ - /// Constrain equation defined by par to value - if (!fInitialized) { - LOG(fatal) << "Millepede is not initialized"; - } - - fMillepede->SetGlobalConstraint(par, value); -} - -//______________________________________________________________________ -Bool_t Alignment::DetElemIsValid(Int_t iDetElemId) const -{ - /// return true if given detector element is valid (and belongs to muon tracker) - const Int_t iCh = iDetElemId / 100; - const Int_t iDet = iDetElemId % 100; - return (iCh > 0 && iCh <= fgNCh && iDet < fgNDetElemCh[iCh - 1]); -} - -//______________________________________________________________________ -Int_t Alignment::GetDetElemNumber(Int_t iDetElemId) const -{ - /// get det element number from ID - // get chamber and element number in chamber - const Int_t iCh = iDetElemId / 100; - const Int_t iDet = iDetElemId % 100; - - // make sure detector index is valid - if (!(iCh > 0 && iCh <= fgNCh && iDet < fgNDetElemCh[iCh - 1])) { - LOG(fatal) << "Invalid detector element id: " << iDetElemId; - } - - // add number of detectors up to this chamber - return iDet + fgSNDetElemCh[iCh - 1]; -} - -//______________________________________________________________________ -Int_t Alignment::GetChamberId(Int_t iDetElemNumber) const -{ - /// get chamber (counting from 1) matching a given detector element id - Int_t iCh(0); - for (iCh = 0; iCh < fgNCh; iCh++) { - if (iDetElemNumber < fgSNDetElemCh[iCh]) - break; - } - - return iCh; -} - -//______________________________________________________________________ -TString Alignment::GetParameterMaskString(UInt_t mask) const -{ - TString out; - if (mask & ParX) - out += "X"; - if (mask & ParY) - out += "Y"; - if (mask & ParZ) - out += "Z"; - if (mask & ParTZ) - out += "T"; - return out; -} - -//______________________________________________________________________ -TString Alignment::GetSidesMaskString(UInt_t mask) const -{ - TString out; - if (mask & SideTop) - out += "T"; - if (mask & SideLeft) - out += "L"; - if (mask & SideBottom) - out += "B"; - if (mask & SideRight) - out += "R"; - return out; -} - -} // namespace mch -} // namespace o2 \ No newline at end of file diff --git a/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx b/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx deleted file mode 100644 index 13d6f3b3f567b..0000000000000 --- a/Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx +++ /dev/null @@ -1,145 +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 NoiseSlotCalibrator.cxx - -#include "MFTCalibration/NoiseSlotCalibrator.h" - -#include -#include "TFile.h" -#include "DataFormatsITSMFT/Digit.h" -#include "DataFormatsITSMFT/ClusterPattern.h" -#include "DataFormatsITSMFT/ROFRecord.h" - -namespace o2 -{ -using Slot = calibration::TimeSlot; - -namespace mft -{ -bool NoiseSlotCalibrator::processTimeFrame(calibration::TFType nTF, - gsl::span const& digits, - gsl::span const& rofs) -{ - LOG(detail) << "Processing TF# " << nTF; - - auto& slotTF = getSlotForTF(nTF); - auto& noiseMap = *(slotTF.getContainer()); - - for (const auto& rof : rofs) { - auto digitsInFrame = rof.getROFData(digits); - for (const auto& d : digitsInFrame) { - auto id = d.getChipIndex(); - auto row = d.getRow(); - auto col = d.getColumn(); - - noiseMap.increaseNoiseCount(id, row, col); - } - } - noiseMap.addStrobes(rofs.size()); - mNumberOfStrobes += rofs.size(); - return hasEnoughData(slotTF); -} - -bool NoiseSlotCalibrator::processTimeFrame(calibration::TFType nTF, - gsl::span const& clusters, - gsl::span const& patterns, - gsl::span const& rofs) -{ - LOG(detail) << "Processing TF# " << nTF; - - auto& slotTF = getSlotForTF(nTF); - auto& noiseMap = *(slotTF.getContainer()); - - auto pattIt = patterns.begin(); - for (const auto& rof : rofs) { - auto clustersInFrame = rof.getROFData(clusters); - for (const auto& c : clustersInFrame) { - if (c.getPatternID() != o2::itsmft::CompCluster::InvalidPatternID) { - // For the noise calibration, we use "pass1" clusters... - continue; - } - o2::itsmft::ClusterPattern patt(pattIt); - - auto id = c.getSensorID(); - auto row = c.getRow(); - auto col = c.getCol(); - auto colSpan = patt.getColumnSpan(); - auto rowSpan = patt.getRowSpan(); - - // Fast 1-pixel calibration - if ((rowSpan == 1) && (colSpan == 1)) { - noiseMap.increaseNoiseCount(id, row, col); - continue; - } - - // All-pixel calibration - auto nBits = rowSpan * colSpan; - int ic = 0, ir = 0; - for (unsigned int i = 2; i < patt.getUsedBytes() + 2; i++) { - unsigned char tempChar = patt.getByte(i); - int s = 128; // 0b10000000 - while (s > 0) { - if ((tempChar & s) != 0) { - noiseMap.increaseNoiseCount(id, row + ir, col + ic); - } - ic++; - s >>= 1; - if ((ir + 1) * ic == nBits) { - break; - } - if (ic == colSpan) { - ic = 0; - ir++; - } - } - if ((ir + 1) * ic == nBits) { - break; - } - } - } - } - noiseMap.addStrobes(rofs.size()); - mNumberOfStrobes += rofs.size(); - return hasEnoughData(slotTF); -} - -// Functions overloaded from the calibration framework -bool NoiseSlotCalibrator::process(calibration::TFType tf, const gsl::span data) -{ - LOG(warning) << "Only 1-pix noise calibraton is possible !"; - return calibration::TimeSlotCalibration::process(tf, data); -} - -// Functions required by the calibration framework - -Slot& NoiseSlotCalibrator::emplaceNewSlot(bool front, calibration::TFType tstart, calibration::TFType tend) -{ - auto& cont = getSlots(); - auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); - slot.setContainer(std::make_unique(936)); - return slot; -} - -bool NoiseSlotCalibrator::hasEnoughData(const Slot& slot) const -{ - return slot.getContainer()->getNumberOfStrobes() > mMinROFs ? true : false; -} - -void NoiseSlotCalibrator::finalizeSlot(Slot& slot) -{ - o2::itsmft::NoiseMap* map = slot.getContainer(); - LOG(info) << "Number of processed strobes is " << map->getNumberOfStrobes(); - map->applyProbThreshold(mProbabilityThreshold, map->getNumberOfStrobes(), mProbRelErr); -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt index acb3d0b3e835f..b83699498a6b8 100644 --- a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt @@ -12,8 +12,6 @@ o2_add_library(MFTWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackReaderSpec.cxx src/TrackWriterSpec.cxx diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index c8061310e34f6..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +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 ClusterWriterSpec.cxx - -#include - -#include "MFTWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace mft -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "MFTClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("mft-cluster-writer", - "mftclusters.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with MFT clusters"}, - BranchDefinition{InputSpec{"compclus", "MFT", "COMPCLUSTERS", 0}, - "MFTClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "MFT", "PATTERNS", 0}, - "MFTClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "MFT", "CLUSTERSROF", 0}, - "MFTClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "MFT", "CLUSTERSMCTR", 0}, - "MFTClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "MFT", "CLUSTERSMC2ROF", 0}, - "MFTClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx deleted file mode 100644 index 766d7c1a0729e..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,212 +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 ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingMFT.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "DetectorsBase/GeometryManager.h" -#include "MFTBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTReconstruction/ClustererParam.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace mft -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingMFT::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - const o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(debug) << "MFTClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginMFT; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(debug) << "MFTClusterer pushed " << clusCompVec.size() << " compressed clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(nROFsToSquash); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((const 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("MFT", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "MFT", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "MFT", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "MFT", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "MFT", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/ClustererParam")); - inputs.emplace_back("alppar", "MFT", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "MFT", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "MFT", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "mft-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx index 615c9c1b275d4..5d85c0ef81670 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx @@ -12,9 +12,9 @@ /// @file RecoWorkflow.cxx #include +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/RecoWorkflow.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "MFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/TrackerSpec.h" #include "MFTWorkflow/TrackWriterSpec.h" #include "ITSMFTWorkflow/DigitReaderSpec.h" @@ -52,10 +52,10 @@ framework::WorkflowSpec getWorkflow( } } if (!upstreamClusters) { - specs.emplace_back(o2::mft::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); } if (runTracking) { diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx index f42b2e0c92a4a..b656970693808 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "MFTWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -34,6 +34,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); WorkflowSpec specs; - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 57d989038342a..7e266052efb3c 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -45,7 +45,7 @@ class CTFCoder final : public o2::ctf::CTFCoderBase using PMatrix = std::array, ClusterPattern::MaxColSpan + 2>; using RowColBuff = std::vector; - CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, det, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), det, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index c66468905d0aa..0bdbb701a9356 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -215,13 +215,15 @@ class Clusterer int getMaxRowColDiffToMask() const { return mMaxRowColDiffToMask; } void setMaxRowColDiffToMask(int v) { mMaxRowColDiffToMask = v; } - int getMaxROFDepthToSquash() const { return mSquashingDepth; } + int getMaxROFDepthToSquash(int layer = -1) const { return (layer < 0) ? mSquashingDepth : mSquashingLayerDepth[layer]; } void setMaxROFDepthToSquash(int v) { mSquashingDepth = v; } + void addMaxROFDepthToSquash(int v) { mSquashingLayerDepth.push_back(v); } - int getMaxBCSeparationToSquash() const { return mMaxBCSeparationToSquash; } + int getMaxBCSeparationToSquash(int layer = -1) const { return (layer < 0) ? mMaxBCSeparationToSquash : mMaxBCSeparationToSquashLayer[layer]; } void setMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquash = n; } + void addMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquashLayer.push_back(n); } - void print() const; + void print(bool showTiming = true) const; void clear(); void reset(); @@ -245,7 +247,7 @@ class Clusterer bool mContinuousReadout = true; ///< flag continuous readout bool mDropHugeClusters = false; ///< don't include clusters that would be split in more than one - ///< mask continuosly fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) + ///< mask continuously fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) int mMaxBCSeparationToMask = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; int mMaxRowColDiffToMask = 0; ///< provide their difference in col/row is <= than this int mNHugeClus = 0; ///< number of encountered huge clusters @@ -253,6 +255,8 @@ class Clusterer ///< Squashing options int mSquashingDepth = 0; ///< squashing is applied to next N rofs int mMaxBCSeparationToSquash = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; + std::vector mSquashingLayerDepth; + std::vector mMaxBCSeparationToSquashLayer; std::vector> mThreads; // buffers for threads std::vector mChips; // currently processed ROF's chips data @@ -288,7 +292,7 @@ void Clusterer::streamCluster(const std::vector& pixbuf, const std::a uint16_t row = bbox.rowMin, col = bbox.colMin; if (pattID == CompCluster::InvalidPatternID || pattIdConverter.isGroup(pattID)) { if (pattID != CompCluster::InvalidPatternID) { - // For groupped topologies, the reference pixel is the COG pixel + // For grouped topologies, the reference pixel is the COG pixel float xCOG = 0., zCOG = 0.; ClusterPattern::getCOG(rowSpanW, colSpanW, patt.data(), xCOG, zCOG); row += round(xCOG); diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h index a71e5f3095b06..3188a4f3b0010 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h @@ -29,16 +29,26 @@ template struct ClustererParam : public o2::conf::ConfigurableParamHelper> { static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS or DetID:: MFT are allowed"); + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } + static constexpr std::string_view getParamName() { return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; } - int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired - int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking - int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing - float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus - bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired + int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking + int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing + float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus + bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxBCDiffToSquashBiasLayer[getNLayers()] = {}; ///< squash mask per layer + int getMaxBCDiffToSquashBias(int layer) const noexcept + { + return maxBCDiffToSquashBiasLayer[layer] ? maxBCDiffToSquashBiasLayer[layer] : maxBCDiffToSquashBias; + } O2ParamDef(ClustererParam, getParamName().data()); @@ -46,7 +56,7 @@ struct ClustererParam : public o2::conf::ConfigurableParamHelper(nFired, nThreads); #ifndef WITH_OPENMP nThreads = 1; #endif @@ -173,7 +168,7 @@ void Clusterer::ClustererThread::process(uint16_t chip, uint16_t nChips, CompClu const ConstMCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr) { if (stats.empty() || stats.back().firstChip + stats.back().nChips != chip) { // there is a jump, register new block - stats.emplace_back(ThreadStat{chip, 0, uint32_t(compClusPtr->size()), patternsPtr ? uint32_t(patternsPtr->size()) : 0, 0, 0}); + stats.emplace_back(ThreadStat{.firstChip = chip, .nChips = 0, .firstClus = uint32_t(compClusPtr->size()), .firstPatt = patternsPtr ? uint32_t(patternsPtr->size()) : 0, .nClus = 0, .nPatt = 0}); } for (int ic = 0; ic < nChips; ic++) { auto* curChipData = parent->mFiredChipsPtr[chip + ic]; @@ -476,22 +471,31 @@ void Clusterer::clear() } //__________________________________________________ -void Clusterer::print() const +void Clusterer::print(bool showsTiming) const { // print settings - LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + if (mSquashingLayerDepth.empty()) { + LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + } else { + LOGP(info, "Clusterizer squashes overflow pixels <= {} in row/col", mMaxRowColDiffToMask); + for (size_t i{0}; i < mSquashingLayerDepth.size(); ++i) { + LOGP(info, "\tlay:{} separated by {} BC seeking down to {} neighbour ROFs", i, mMaxBCSeparationToSquashLayer[i], mSquashingLayerDepth[i]); + } + } LOGP(info, "Clusterizer masks overflow pixels separated by < {} BC and <= {} in row/col", mMaxBCSeparationToMask, mMaxRowColDiffToMask); LOGP(info, "Clusterizer does {} drop huge clusters", mDropHugeClusters ? "" : "not"); + if (showsTiming) { #ifdef _PERFORM_TIMING_ - auto& tmr = const_cast(mTimer); // ugly but this is what root does internally - auto& tmrm = const_cast(mTimerMerge); - LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() - << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; - LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() - << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; + auto& tmr = const_cast(mTimer); // ugly but this is what root does internally + auto& tmrm = const_cast(mTimerMerge); + LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() + << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; + LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() + << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; #endif + } } //__________________________________________________ diff --git a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx index b8d88a6fc4223..5c1dbde074649 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx @@ -330,3 +330,14 @@ void DigitPixelReader::clear() mROFRecVec = gsl::span(); mMC2ROFRecVec = gsl::span(); } + +//______________________________________________________________________________ +void DigitPixelReader::reset() +{ + clear(); + mSquashedDigitsMask.clear(); + mBookmarkNextROFs.clear(); + mIdDig = 0; + mIdROF = 0; + mIdROFLast = 0; +} diff --git a/Detectors/ITSMFT/common/workflow/CMakeLists.txt b/Detectors/ITSMFT/common/workflow/CMakeLists.txt index 63cd8d6c0bcee..ead08c4422260 100644 --- a/Detectors/ITSMFT/common/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/common/workflow/CMakeLists.txt @@ -11,6 +11,8 @@ o2_add_library(ITSMFTWorkflow SOURCES src/ClusterReaderSpec.cxx + src/ClusterWriterSpec.cxx + src/ClustererSpec.cxx src/DigitWriterSpec.cxx src/DigitReaderSpec.cxx src/STFDecoderSpec.cxx diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h index 99318df1cd9d9..82e3890de7475 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h @@ -23,46 +23,51 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/DetID.h" using namespace o2::framework; -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { +template class ClusterReader : public Task { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{(N == o2::detectors::DetID::ITS) ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + ClusterReader() = delete; - ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns = true, bool triggers = true); + ClusterReader(bool useMC, bool usePatterns = true, bool triggers = true); ~ClusterReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; protected: void connectTree(const std::string& filename); + template + void setBranchAddress(const std::string& base, Ptr& addr, int layer); + std::string getBranchName(const std::string& base, int index) const; - std::vector mClusROFRec, *mClusROFRecPtr = &mClusROFRec; - std::vector mClusterCompArray, *mClusterCompArrayPtr = &mClusterCompArray; - std::vector mPatternsArray, *mPatternsArrayPtr = &mPatternsArray; - o2::dataformats::MCTruthContainer mClusterMCTruth, *mClusterMCTruthPtr = &mClusterMCTruth; - std::vector mClusMC2ROFs, *mClusMC2ROFsPtr = &mClusMC2ROFs; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + std::array*, NLayers> mClusROFRec; + std::array*, NLayers> mClusterCompArray; + std::array*, NLayers> mPatternsArray; + std::array*, NLayers> mClusterMCTruth; + std::array*, NLayers> mClusMC2ROFs; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth + bool mUseMC = true; // use MC truth bool mUsePatterns = true; // send patterns bool mTriggerOut = true; // send dummy triggers vector - std::string mDetName = ""; - std::string mDetNameLC = ""; - std::string mFileName = ""; + std::string mDetName; + std::string mDetNameLC; + std::string mFileName; std::string mClusTreeName = "o2sim"; std::string mClusROFBranchName = "ClustersROF"; std::string mClusterPattBranchName = "ClusterPatt"; @@ -71,24 +76,18 @@ class ClusterReader : public Task std::string mClustMC2ROFBranchName = "ClustersMC2ROF"; }; -class ITSClusterReader : public ClusterReader +class ITSClusterReader : public ClusterReader { public: ITSClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::ITS, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginITS; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; -class MFTClusterReader : public ClusterReader +class MFTClusterReader : public ClusterReader { public: MFTClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::MFT, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginMFT; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; /// create a processor spec @@ -96,7 +95,6 @@ class MFTClusterReader : public ClusterReader framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); -} // namespace itsmft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITSMFT_CLUSTERREADER */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h similarity index 73% rename from Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h index 42b96786af27a..5ae371e7e09c4 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h @@ -11,21 +11,19 @@ /// @file ClusterWriterSpec.h -#ifndef O2_ITS_CLUSTERWRITER -#define O2_ITS_CLUSTERWRITER +#ifndef O2_ITSMFT_CLUSTERWRITER +#define O2_ITSMFT_CLUSTERWRITER #include "Framework/DataProcessorSpec.h" -namespace o2 -{ -namespace its +namespace o2::itsmft { -/// create a processor spec -/// write ITS clusters to ROOT file +template framework::DataProcessorSpec getClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC); -} // namespace its -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITS_CLUSTERWRITER */ diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h similarity index 64% rename from Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h index f0a763597ff74..b6ebc282c2a27 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h @@ -11,24 +11,27 @@ /// @file ClustererSpec.h -#ifndef O2_MFT_CLUSTERERDPL_H_ -#define O2_MFT_CLUSTERERDPL_H_ +#ifndef O2_ITSMFT_CLUSTERERDPL_H_ +#define O2_ITSMFT_CLUSTERERDPL_H_ -#include #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ITSMFTReconstruction/Clusterer.h" +#include "ITSMFTBase/DPLAlpideParam.h" using namespace o2::framework; -namespace o2 -{ -namespace mft +namespace o2::itsmft { +template class ClustererDPL : public Task { + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + public: ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} ~ClustererDPL() override = default; @@ -39,20 +42,19 @@ class ClustererDPL : public Task private: void updateTimeDependentParams(ProcessingContext& pc); - int mState = 0; + std::string mDetName; bool mUseMC = true; bool mUseClusterDictionary = true; int mNThreads = 1; - std::unique_ptr mFile = nullptr; std::unique_ptr mClusterer = nullptr; std::shared_ptr mGGCCDBRequest; + int mLayers{NLayers}; + std::vector mFilter; }; -/// create a processor spec -/// run MFT cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); +framework::DataProcessorSpec getITSClustererSpec(bool useMC); +framework::DataProcessorSpec getMFTClustererSpec(bool useMC); -} // namespace mft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_MFT_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index ea906056c7898..bc6418a077810 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -12,15 +12,16 @@ /// @file ClusterReaderSpec.cxx #include +#include -#include "TTree.h" +#include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" -#include #include "CommonUtils/NameConf.h" using namespace o2::framework; @@ -31,45 +32,48 @@ namespace o2 namespace itsmft { -ClusterReader::ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns, bool triggerOut) +template +ClusterReader::ClusterReader(bool useMC, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) { - assert(id == o2::detectors::DetID::ITS || id == o2::detectors::DetID::MFT); - mDetNameLC = mDetName = id.getName(); - mUseMC = useMC; - mUsePatterns = usePatterns; - mTriggerOut = triggerOut; std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); + + mClusROFRec.fill(nullptr); + mClusterCompArray.fill(nullptr); + mPatternsArray.fill(nullptr); + mClusterMCTruth.fill(nullptr); + mClusMC2ROFs.fill(nullptr); } -void ClusterReader::init(InitContext& ic) +template +void ClusterReader::init(InitContext& ic) { mFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get((mDetNameLC + "-cluster-infile").c_str())); connectTree(mFileName); } -void ClusterReader::run(ProcessingContext& pc) +template +void ClusterReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << mDetName << "ClusterReader pushes " << mClusROFRec.size() << " ROFRecords," - << mClusterCompArray.size() << " compact clusters at entry " << ent; - - // This is a very ugly way of providing DataDescription, which anyway does not need to contain detector name. - // To be fixed once the names-definition class is ready - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSROF", 0}, mClusROFRec); - pc.outputs().snapshot(Output{mOrigin, "COMPCLUSTERS", 0}, mClusterCompArray); - if (mUsePatterns) { - pc.outputs().snapshot(Output{mOrigin, "PATTERNS", 0}, mPatternsArray); - } - if (mUseMC) { - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMCTR", 0}, mClusterMCTruth); - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMC2ROF", 0}, mClusMC2ROFs); + + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + LOG(info) << mDetName << "ClusterReader:" << iLayer << " pushes " << mClusROFRec[iLayer]->size() << " ROFRecords, " << mClusterCompArray[iLayer]->size() << " compact clusters at entry " << ent; + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, *mClusROFRec[iLayer]); + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, *mClusterCompArray[iLayer]); + if (mUsePatterns) { + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, *mPatternsArray[iLayer]); + } + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *mClusterMCTruth[iLayer]); + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, *mClusMC2ROFs[iLayer]); + } } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); @@ -77,7 +81,8 @@ void ClusterReader::run(ProcessingContext& pc) } } -void ClusterReader::connectTree(const std::string& filename) +template +void ClusterReader::connectTree(const std::string& filename) { mTree.reset(nullptr); // in case it was already loaded mFile.reset(TFile::Open(filename.c_str())); @@ -85,70 +90,89 @@ void ClusterReader::connectTree(const std::string& filename) mTree.reset((TTree*)mFile->Get(mClusTreeName.c_str())); assert(mTree); - mTree->SetBranchAddress((mDetName + mClusROFBranchName).c_str(), &mClusROFRecPtr); - mTree->SetBranchAddress((mDetName + mClusterCompBranchName).c_str(), &mClusterCompArrayPtr); - if (mUsePatterns) { - mTree->SetBranchAddress((mDetName + mClusterPattBranchName).c_str(), &mPatternsArrayPtr); - } - if (mUseMC) { - if (mTree->GetBranch((mDetName + mClustMCTruthBranchName).c_str()) && - mTree->GetBranch((mDetName + mClustMC2ROFBranchName).c_str())) { - mTree->SetBranchAddress((mDetName + mClustMCTruthBranchName).c_str(), &mClusterMCTruthPtr); - mTree->SetBranchAddress((mDetName + mClustMC2ROFBranchName).c_str(), &mClusMC2ROFsPtr); - } else { - LOG(info) << "MC-truth is missing"; - mUseMC = false; + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + setBranchAddress(mClusROFBranchName, mClusROFRec[iLayer], iLayer); + setBranchAddress(mClusterCompBranchName, mClusterCompArray[iLayer], iLayer); + if (mUsePatterns) { + setBranchAddress(mClusterPattBranchName, mPatternsArray[iLayer], iLayer); + } + if (mUseMC) { + if (mTree->GetBranch(getBranchName(mClustMCTruthBranchName, iLayer).c_str()) && + mTree->GetBranch(getBranchName(mClustMC2ROFBranchName, iLayer).c_str())) { + setBranchAddress(mClustMCTruthBranchName, mClusterMCTruth[iLayer], iLayer); + setBranchAddress(mClustMC2ROFBranchName, mClusMC2ROFs[iLayer], iLayer); + } else { + LOG(info) << "MC-truth is missing"; + mUseMC = false; + } } } LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; } -DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +template +std::string ClusterReader::getBranchName(const std::string& base, int index) const +{ + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return mDetName + base + "_" + std::to_string(index); + } + return mDetName + base; +} + +template +template +void ClusterReader::setBranchAddress(const std::string& base, Ptr& addr, int layer) { - std::vector outputSpec; - outputSpec.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); + const auto name = getBranchName(base, layer); + if (Int_t ret = mTree->SetBranchAddress(name.c_str(), &addr); ret != 0) { + LOGP(fatal, "failed to set branch address for {} ret={}", name, ret); } - if (useMC) { - outputSpec.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); +} + +namespace +{ +template +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool usePatterns, bool triggerOut) +{ + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < ((o2::itsmft::DPLAlpideParam::supportsStaggering()) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { + outputs.emplace_back(detOrig, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + if (usePatterns) { + outputs.emplace_back(detOrig, "PATTERNS", iLayer, Lifetime::Timeframe); + } + if (mctruth) { + outputs.emplace_back(detOrig, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } } if (triggerOut) { - outputSpec.emplace_back("ITS", "PHYSTRIG", 0, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "PHYSTRIG", 0, Lifetime::Timeframe); } + return outputs; +} +} // namespace + +DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +{ return DataProcessorSpec{ - "its-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "its-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("ITS", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"its-cluster-infile", VariantType::String, "o2clus_its.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } DataProcessorSpec getMFTClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) { - std::vector outputSpec; - outputSpec.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - } - if (useMC) { - outputSpec.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - if (triggerOut) { - outputSpec.emplace_back("MFT", "PHYSTRIG", 0, Lifetime::Timeframe); - } return DataProcessorSpec{ - "mft-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "mft-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("MFT", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"mft-cluster-infile", VariantType::String, "mftclusters.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx new file mode 100644 index 0000000000000..c1900c346133b --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx @@ -0,0 +1,107 @@ +// 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 ClusterWriterSpec.cxx + +#include +#include +#include +#include +#include + +#include "Framework/ConcreteDataMatcher.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2::itsmft +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using CompClusType = std::vector; +using PatternsType = std::vector; +using ROFrameRType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; +using ROFRecLblT = std::vector; +using namespace o2::header; + +template +DataProcessorSpec getClusterWriterSpec(bool useMC) +{ + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr int NLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + const auto detName = Origin.as(); + // Spectators for logging + auto compClusterSizes = std::make_shared>(); + auto compClustersSizeGetter = [compClusterSizes](CompClusType const& compClusters, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*compClusterSizes)[dh->subSpecification] = compClusters.size(); + }; + auto logger = [detName, compClusterSizes](std::vector const& rofs, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + const auto i = dh->subSpecification; + LOG(info) << detName << "ClusterWriter:" << i << " pulled " << (*compClusterSizes)[i] << " clusters, in " << rofs.size() << " RO frames"; + }; + auto getIndex = [](DataRef const& ref) -> size_t { + auto const* dh = DataRefUtils::getHeader(ref); + return static_cast(dh->subSpecification); + }; + auto getName = [](std::string base, size_t index) -> std::string { + if constexpr (DPLAlpideParam::supportsStaggering()) { + return base += "_" + std::to_string(index); + } + return base; + }; + auto detNameLC = detName; + std::transform(detNameLC.begin(), detNameLC.end(), detNameLC.begin(), [](unsigned char c) { return std::tolower(c); }); + return MakeRootTreeWriterSpec(std::format("{}-cluster-writer", detNameLC).c_str(), + (o2::detectors::DetID::ITS == N) ? "o2clus_its.root" : "mftclusters.root", + MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = std::format("Tree with {} clusters", detName)}, + BranchDefinition{InputSpec{"compclus", ConcreteDataTypeMatcher{Origin, "COMPCLUSTERS"}}, + (detName + "ClusterComp").c_str(), "compact-cluster-branch", + NLayers, + compClustersSizeGetter, + getIndex, + getName}, + BranchDefinition{InputSpec{"patterns", ConcreteDataTypeMatcher{Origin, "PATTERNS"}}, + (detName + "ClusterPatt").c_str(), "cluster-pattern-branch", + NLayers, + getIndex, + getName}, + BranchDefinition{InputSpec{"ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSROF"}}, + (detName + "ClustersROF").c_str(), "cluster-rof-branch", + NLayers, + logger, + getIndex, + getName}, + BranchDefinition{InputSpec{"labels", ConcreteDataTypeMatcher{Origin, "CLUSTERSMCTR"}}, + (detName + "ClusterMCTruth").c_str(), "cluster-label-branch", + (useMC ? NLayers : 0), + getIndex, + getName}, + BranchDefinition{InputSpec{"MC2ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSMC2ROF"}}, + (detName + "ClustersMC2ROF").c_str(), "cluster-mc2rof-branch", + (useMC ? NLayers : 0), + getIndex, + getName})(); +} + +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } + +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx new file mode 100644 index 0000000000000..0b6bb44ee78c8 --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -0,0 +1,325 @@ +// 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 ClustererSpec.cxx + +#include + +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsITSMFT/Digit.h" +#include "Framework/InputRecordWalker.h" +#include "ITSMFTReconstruction/ChipMappingMFT.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsParameters/GRPObject.h" +#include "ITSMFTReconstruction/DigitPixelReader.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "CommonConstants/LHCConstants.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "ITSMFTReconstruction/ClustererParam.h" + +namespace o2::itsmft +{ + +template +void ClustererDPL::init(InitContext& ic) +{ + mClusterer = std::make_unique(); + mClusterer->setNChips((N == o2::detectors::DetID::ITS) ? o2::itsmft::ChipMappingITS::getNChips() : o2::itsmft::ChipMappingMFT::getNChips()); + mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mNThreads = std::max(1, ic.options().get("nthreads")); + mDetName = Origin.as(); + + // prepare data filter + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + mFilter.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("ROframe", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mUseMC) { + mFilter.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } +} + +template +void ClustererDPL::run(ProcessingContext& pc) +{ + updateTimeDependentParams(pc); + + // filter input and compose + std::array, NLayers> digits; + std::array, NLayers> rofs; + std::array, NLayers> labelsbuffer; + std::array, NLayers> mc2rofs; + for (const DataRef& ref : InputRecordWalker{pc.inputs(), mFilter}) { + auto const* dh = DataRefUtils::getHeader(ref); + if (DataRefUtils::match(ref, {"digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}})) { + digits[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"ROframe", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}})) { + rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"labels", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}})) { + labelsbuffer[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"MC2ROframes", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}})) { + mc2rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + } + + // query the first orbit in this TF + const auto firstTForbit = pc.services().get().firstTForbit; + const o2::InteractionRecord firstIR(0, firstTForbit); + const auto& par = DPLAlpideParam::Instance(); + + // process received inputs + uint64_t nClusters{0}; + TStopwatch sw; + o2::itsmft::DigitPixelReader reader; + for (uint32_t iLayer{0}; iLayer < NLayers; ++iLayer) { + int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + sw.Start(); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; + + mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(layer)); + o2::dataformats::ConstMCTruthContainerView labels(labelsbuffer[iLayer]); + reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash(layer)); + reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking + reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash(layer)); + reader.setDigits(digits[iLayer]); + reader.setROFRecords(rofs[iLayer]); + if (mUseMC) { + reader.setMC2ROFRecords(mc2rofs[iLayer]); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << labels.getNElements() << " labels "; + reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); + } + reader.init(); + std::vector clusCompVec; + std::vector clusROFVec; + std::vector clusPattVec; + + std::unique_ptr> clusterLabels; + if (mUseMC) { + clusterLabels = std::make_unique>(); + } + mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); + + // ensure that the rof output is continuous + size_t nROFs = clusROFVec.size(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * o2::base::GRPGeomHelper::getNHBFPerTF(); + if (nROFsTF != clusROFVec.size()) { + // it can happen that in the digitization rofs without contributing hits are skipped + // however downstream consumers of the clusters cannot know apriori the time structure + // the cluster rofs do not account for the bias so it will start always at BC=0 + // if we receive more cluster rofs then there supposed to be, do not throw away this data + // the clusterer should be blind to this! + const size_t nROFsLayer = std::max((size_t)nROFsTF, clusROFVec.size()); + std::vector expClusRofVec(nROFsLayer); + for (int iROF{0}; iROF < nROFsLayer; ++iROF) { + auto& rof = expClusRofVec[iROF]; + int orb = iROF * par.getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + firstTForbit; + int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + uint32_t prevEntry{0}; + for (const auto& rof : clusROFVec) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const int irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer); + auto& expROF = expClusRofVec[irROF]; + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + if (expROF.getBCData() != rof.getBCData()) { + LOGP(fatal, "detected mismatch between expected ROF:{} and received ROF:{}", expROF.asString(), rof.asString()); + } + } + int prevFirst{0}; + for (auto& rof : expClusRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } + nROFs = expClusRofVec.size(); + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); + } else { + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, clusROFVec); + } + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, clusCompVec); + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, clusPattVec); + + nClusters += clusCompVec.size(); + + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *clusterLabels); // at the moment requires snapshot + std::vector clusterMC2ROframes(mc2rofs[iLayer].size()); + for (int i = mc2rofs[iLayer].size(); i--;) { + clusterMC2ROframes[i] = mc2rofs[iLayer][i]; // Simply, replicate it from digits ? + } + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, clusterMC2ROframes); + } + reader.reset(); + + // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF + // -> consider recalculationg maxROF + sw.Stop(); + LOG(info) << mDetName << "Clusterer:" << layer << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s"; + } + + LOG(info) << mDetName << "Clusterer produced " << nClusters << " clusters"; +} + +///_______________________________________ +template +void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + pc.inputs().get("cldict"); // just to trigger the finaliseCCDB + pc.inputs().get*>("alppar"); + pc.inputs().get*>("cluspar"); + mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(N)); + // settings for the fired pixel overflow masking + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + const auto& clParams = o2::itsmft::ClustererParam::Instance(); + if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { + LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); + } + mClusterer->setDropHugeClusters(clParams.dropHugeClusters); + auto nbc = clParams.maxBCDiffToMaskBias; + nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); + mClusterer->setMaxBCSeparationToMask(nbc); + mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); + // Squasher + int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC + mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); + int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. + if (clParams.maxSOTMUS > 0 && rofBC > 0) { + nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing + } + mClusterer->setMaxROFDepthToSquash(nROFsToSquash); + if constexpr (DPLAlpideParam::supportsStaggering()) { + if (mClusterer->isContinuousReadOut()) { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + mClusterer->addMaxBCSeparationToSquash(alpParams.getROFLengthInBC(iLayer) + clParams.getMaxBCDiffToSquashBias(iLayer)); + mClusterer->addMaxROFDepthToSquash((clParams.getMaxBCDiffToSquashBias(iLayer) > 0) ? 2 + int(clParams.maxSOTMUS / (alpParams.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingMUS)) : 0); + } + } + } + mClusterer->print(false); + } + // we may have other params which need to be queried regularly +} + +///_______________________________________ +template +void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); + if (mUseClusterDictionary) { + mClusterer->setDictionary((const 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(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << "Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSPARAM", 0)) { + LOG(info) << "Cluster param updated"; + const auto& par = o2::itsmft::ClustererParam::Instance(); + par.printKeyValues(); + return; + } +} + +namespace +{ +template +DataProcessorSpec getClustererSpec(bool useMC) +{ + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + std::vector inputs; + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + inputs.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("ROframes", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + inputs.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + inputs.emplace_back("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Calib/ClusterDictionary")); + inputs.emplace_back("cluspar", Origin, "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/ClustererParam")); + inputs.emplace_back("alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/AlpideParam")); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + outputs.emplace_back(Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "PATTERNS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(Origin, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + return DataProcessorSpec{ + .name = (N == o2::detectors::DetID::ITS) ? "its-clusterer" : "mft-clusterer", + .inputs = inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask>(ggRequest, useMC)}, + .options = Options{ + {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, + {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; +} +} // namespace + +framework::DataProcessorSpec getITSClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +framework::DataProcessorSpec getMFTClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index 5f09fd6c69a97..b2a1b6ce75591 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -24,7 +24,7 @@ void customize(std::vector& workflowOptions) std::vector options{ ConfigParamSpec{"runmft", VariantType::Bool, false, {"source detector is MFT (default ITS)"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h index 5c9da95a98354..a5f2af646c778 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mch class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::MCH, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::MCH, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx b/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx deleted file mode 100644 index b614346f1a42a..0000000000000 --- a/Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx +++ /dev/null @@ -1,117 +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. - -/// -/// @author Laurent Aphecetche - -#include "MCHMappingSegContour/CathodeSegmentationSVGWriter.h" -#include "MCHMappingInterface/CathodeSegmentation.h" -#include "MCHMappingSegContour/CathodeSegmentationContours.h" -#include "MCHContour/SVGWriter.h" -#include - -using namespace o2::mch::contour; - -namespace o2 -{ -namespace mch -{ -namespace mapping -{ - -std::string svgCathodeSegmentationDefaultStyle() -{ - return R"( -.pads { - fill: #EEEEEE; - stroke-width: 0.025px; - stroke: #AAAAAA; -} -.padchannels { - font-size: 0.4px; - font-family: arial; - fill: blue; - text-anchor: middle; -} -.dualsampas { - fill:none; - stroke-width: 0.025px; - stroke: #333333; -} -.detectionelements { - fill:none; - stroke-width:0.025px; - stroke: #000000; -} -.testpoints { - fill:red; - stroke-width:0.025px; - stroke: black; - opacity: 0.5; -} -)"; -} - -void svgCathodeSegmentation(const CathodeSegmentation& seg, SVGWriter& w, bool showdes, bool showdualsampas, bool showpads, - bool showpadchannels) -{ - std::vector> dualSampaContours = getDualSampaContours(seg); - std::vector>> dualSampaPads = getPadPolygons(seg); - std::vector> dualSampaPadChannels = getPadChannels(seg); - - if (dualSampaPadChannels.size() != dualSampaPads.size()) { - throw std::runtime_error("gouze"); - } - - auto deContour = getEnvelop(seg); - auto box = getBBox(seg); - - if (showpads) { - w.svgGroupStart("pads"); - for (auto& dsp : dualSampaPads) { - for (auto& p : dsp) { - w.polygon(p); - } - } - w.svgGroupEnd(); - } - - if (showpadchannels) { - w.svgGroupStart("padchannels"); - for (auto i = 0; i < dualSampaPads.size(); ++i) { - auto& dsp = dualSampaPads[i]; - auto& dspch = dualSampaPadChannels[i]; - for (auto j = 0; j < dsp.size(); j++) { - auto bbox = getBBox(dsp[j]); - w.text(std::to_string(dspch[j]), bbox.xcenter(), - bbox.ymax() - 0.05 * bbox.height()); // SVG text y position is the bottom of the text - } - } - w.svgGroupEnd(); - } - - if (showdualsampas) { - w.svgGroupStart("dualsampas"); - for (auto& dsp : dualSampaContours) { - w.contour(dsp); - } - w.svgGroupEnd(); - } - - if (showdes) { - w.svgGroupStart("detectionelements"); - w.contour(deContour); - } -} - -} // namespace mapping -} // namespace mch -} // namespace o2 diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx deleted file mode 100644 index 52e4581da1a71..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx +++ /dev/null @@ -1,938 +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 "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_CRU_BARE_CHARGESUM; -template <> -gsl::span REF_BUFFER_CRU() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_CRU_BARE_CHARGESUM[0]), REF_BUFFER_CRU_BARE_CHARGESUM.size()); -} -std::array REF_BUFFER_CRU_BARE_CHARGESUM = { - // clang-format off -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xB0, 0x12, 0xB0, 0x12, -0x00, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA9, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAB, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA9, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFC, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xA8, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xA8, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x01, 0x0D, 0x10, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x40, 0x0A, 0x40, 0x0A, -0x00, 0x00, 0x0F, 0x00, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAE, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF3, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA2, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA6, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA2, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1E, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x00, 0x01, 0x0F, 0x00, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x10, 0x0D, 0x10, 0x0D, -0x07, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAB, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAB, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA9, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x40, 0x00, 0x00, -0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x07, 0x01, 0x0D, 0x10, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx deleted file mode 100644 index 3c3781460f4d1..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx +++ /dev/null @@ -1,67 +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 "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_CRU_USERLOGIC_CHARGESUM; -template <> -gsl::span REF_BUFFER_CRU() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_CRU_USERLOGIC_CHARGESUM[0]), REF_BUFFER_CRU_USERLOGIC_CHARGESUM.size()); -} -std::array REF_BUFFER_CRU_USERLOGIC_CHARGESUM = { - // clang-format off -0x04, 0x40, 0x00, 0x00, 0x1E, 0x01, 0x00, 0x00, 0x80, 0x00, 0x80, 0x00, -0x0F, 0x00, 0x0F, 0x00, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0xA1, 0x00, -0x03, 0x12, 0x00, 0xE3, 0x46, 0x00, 0xA0, 0x00, 0x01, 0x60, 0xD0, 0x00, -0x00, 0x58, 0xA2, 0x00, 0x04, 0x40, 0xBB, 0x11, 0x00, 0x01, 0xA0, 0x00, -0x18, 0x14, 0x02, 0x40, 0x90, 0x04, 0xA0, 0x00, 0x70, 0x6F, 0x04, 0x40, -0x00, 0x18, 0xA0, 0x00, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x00, -0xED, 0xDE, 0xED, 0xFE, 0xED, 0xDE, 0xED, 0xFE, 0x04, 0x40, 0x00, 0x00, -0x1E, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, 0x0F, 0x01, 0x0F, 0x00, -0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, 0x03, 0x08, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0xF0, 0x00, 0xF0, 0x00, -0x0F, 0x00, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x81, 0x38, -0x1A, 0x12, 0x80, 0xE0, 0x46, 0x00, 0x80, 0x38, 0x01, 0x60, 0xA0, 0x00, -0x00, 0x4D, 0x82, 0x38, 0x04, 0x60, 0xB8, 0x11, 0x00, 0x01, 0x80, 0x38, -0x18, 0x50, 0x00, 0x80, 0x90, 0x04, 0x80, 0x38, 0x28, 0x6E, 0x04, 0x40, -0x00, 0x18, 0x80, 0x38, 0x1E, 0x00, 0x50, 0x21, 0x01, 0x38, 0x82, 0x38, -0x1B, 0x01, 0x10, 0x00, 0x06, 0x28, 0x80, 0x38, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x80, 0x38, 0xED, 0xDE, 0xED, 0xFE, 0xED, 0xDE, 0xED, 0xFE, -0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x01, 0x04, 0x03, 0x12, 0x40, 0xF6, -0x46, 0x00, 0x00, 0x04, 0x01, 0x60, 0x40, 0x1A, 0x00, 0x54, 0x02, 0x04, -0x04, 0xD0, 0xBD, 0x11, 0x00, 0x01, 0x00, 0x04, 0x18, 0xB8, 0x06, 0x00, -0x96, 0x04, 0x00, 0x04, 0x84, 0x6F, 0x04, 0x40, 0x00, 0x18, 0x00, 0x04, -0xB8, 0x01, 0xF0, 0x20, 0x01, 0x94, 0x03, 0x04, 0x1B, 0x01, 0x10, 0x00, -0x06, 0xC2, 0x01, 0x04, 0x00, 0x00, 0x48, 0x00, 0xE9, 0x1B, 0x01, 0x04, -0x00, 0x04, 0x80, 0x01, 0x73, 0x00, 0x00, 0x04, 0x48, 0x12, 0x50, 0xEA, -0x46, 0x00, 0x00, 0x04, 0x01, 0x60, 0x40, 0x1A, 0x00, 0x00, 0x00, 0x04, -0x04, 0x40, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x40, 0x00, 0x40, 0x00, -0x0F, 0x01, 0x0D, 0x10, 0x39, 0x30, 0x00, 0x00, 0x39, 0x30, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA6, 0x02, 0xA6, 0x02, -0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx deleted file mode 100644 index 89b1602cb0489..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx +++ /dev/null @@ -1,239 +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 "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_GBT_BARE_CHARGESUM; -template <> -gsl::span REF_BUFFER_GBT() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_GBT_BARE_CHARGESUM[0]), REF_BUFFER_GBT_BARE_CHARGESUM.size()); -} -std::array REF_BUFFER_GBT_BARE_CHARGESUM = { - // clang-format off -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xEB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xBD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x7D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xEB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x3C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6A, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xA8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0xE8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xE8, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x3E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, -0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x2A, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x28, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, -0xAA, 0xAA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -0x00, 0x00, 0x00, 0x00 - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx b/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx deleted file mode 100644 index 9487037328ad2..0000000000000 --- a/Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx +++ /dev/null @@ -1,34 +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 "RefBuffers.h" -#include -#include "MCHRawCommon/DataFormats.h" - -extern std::array REF_BUFFER_GBT_USERLOGIC_CHARGESUM; -template <> -gsl::span REF_BUFFER_GBT() -{ - return gsl::span(reinterpret_cast(&REF_BUFFER_GBT_USERLOGIC_CHARGESUM[0]), REF_BUFFER_GBT_USERLOGIC_CHARGESUM.size()); -} -std::array REF_BUFFER_GBT_USERLOGIC_CHARGESUM = { - // clang-format off -0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x01, 0x58, 0x0C, 0x12, 0x00, 0xA0, -0x50, 0x03, 0x00, 0x58, 0x01, 0x30, 0xA0, 0x00, 0x00, 0x5B, 0x02, 0x58, -0x04, 0xC0, 0x2F, 0xD4, 0x00, 0x01, 0x00, 0x58, 0x0C, 0x80, 0x02, 0x00, -0x00, 0x00, 0x00, 0x58, 0x13, 0x01, 0xF0, 0x40, 0x55, 0x55, 0x61, 0x58, -0x19, 0x12, 0x60, 0xAD, 0x50, 0x03, 0x60, 0x58, 0x01, 0x30, 0xD0, 0x00, -0x00, 0x09, 0x62, 0x58, 0x04, 0x5C, 0x28, 0xD4, 0x00, 0x01, 0x60, 0x58, -0x0C, 0x14, 0x02, 0x40, 0x82, 0x04, 0x60, 0x58, 0xF7, 0x0B, 0x35, 0x40, -0x00, 0x0C, 0x60, 0x58, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x58 - - // clang-format on -}; diff --git a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx index b5f371edfc759..c66365922233e 100644 --- a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx @@ -118,7 +118,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h index defec7207f808..0a6ee12316921 100644 --- a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mid class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::MID, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::MID, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx b/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx deleted file mode 100644 index a54ea9c1733a8..0000000000000 --- a/Detectors/MUON/MID/Filtering/test/bench_Filter.cxx +++ /dev/null @@ -1,98 +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 MID/Tracking/test/bench_Tracker.cxx -/// \brief Benchmark tracker device for MID -/// \author Diego Stocco -/// \date 17 March 2018 - -#include "benchmark/benchmark.h" -#include -#include "DataFormatsMID/Cluster.h" -#include "DataFormatsMID/Track.h" -#include "MIDBase/HitFinder.h" -#include "MIDBase/Mapping.h" -#include "MIDBase/MpArea.h" -#include "MIDTestingSimTools/TrackGenerator.h" -#include "MIDTracking/Tracker.h" - -std::vector generateTestData(int nTracks, o2::mid::TrackGenerator& trackGen, - const o2::mid::HitFinder& hitFinder, const o2::mid::Mapping& mapping) -{ - o2::mid::Mapping::MpStripIndex stripIndex; - o2::mid::MpArea area; - std::vector clusters; - o2::mid::Cluster cl; - std::vector tracks = trackGen.generate(nTracks); - for (auto& track : tracks) { - for (int ich = 0; ich < 4; ++ich) { - auto hits = hitFinder.getLocalPositions(track, ich); - bool isFired = false; - for (auto& hit : hits) { - int deId = hit.deId; - float xPos = hit.xCoor; - float yPos = hit.yCoor; - stripIndex = mapping.stripByPosition(xPos, yPos, 0, deId, false); - if (!stripIndex.isValid()) { - continue; - } - cl.deId = deId; - area = mapping.stripByLocation(stripIndex.strip, 0, stripIndex.line, stripIndex.column, deId); - cl.yCoor = area.getCenterY(); - cl.yErr = area.getHalfSizeY() / std::sqrt(3.); - stripIndex = mapping.stripByPosition(xPos, yPos, 1, deId, false); - area = mapping.stripByLocation(stripIndex.strip, 1, stripIndex.line, stripIndex.column, deId); - cl.xCoor = area.getCenterX(); - cl.xErr = area.getHalfSizeX() / std::sqrt(3.); - clusters.push_back(cl); - } // loop on fired pos - } // loop on chambers - } // loop on tracks - return clusters; -} - -static void BM_TRACKER(benchmark::State& state) -{ - o2::mid::GeometryTransformer geoTrans = o2::mid::createDefaultTransformer(); - o2::mid::TrackGenerator trackGen; - o2::mid::HitFinder hitFinder(geoTrans); - o2::mid::Mapping mapping; - o2::mid::Tracker tracker(geoTrans); - - int nTracksPerEvent = state.range(0); - tracker.init((state.range(1) == 1)); - double num{0}; - - std::vector inputData; - - for (auto _ : state) { - state.PauseTiming(); - inputData = generateTestData(nTracksPerEvent, trackGen, hitFinder, mapping); - state.ResumeTiming(); - tracker.process(inputData); - ++num; - } - - state.counters["num"] = benchmark::Counter(num, benchmark::Counter::kIsRate); -} - -static void CustomArguments(benchmark::internal::Benchmark* bench) -{ - for (int itrack = 1; itrack <= 8; ++itrack) { - for (int imethod = 0; imethod < 2; ++imethod) { - bench->Args({itrack, imethod}); - } - } -} - -BENCHMARK(BM_TRACKER)->Apply(CustomArguments)->Unit(benchmark::kNanosecond); - -BENCHMARK_MAIN(); diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h deleted file mode 100644 index 4d104aacac15c..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h +++ /dev/null @@ -1,30 +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 MIDWorkflow/RawDumpSpec.h -/// \brief Device to dump decoded raw data -/// \author Diego Stocco -/// \date 17 February 2022 - -#ifndef O2_MID_RAWDUMPSPEC_H -#define O2_MID_RAWDUMPSPEC_H - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::DataProcessorSpec getRawDumpSpec(); -} // namespace mid -} // namespace o2 - -#endif // O2_MID_RAWDUMPSPEC_H diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h deleted file mode 100644 index b5a6b33530c8f..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h +++ /dev/null @@ -1,30 +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 MIDWorkflow/RawAggregatorSpec.h -/// \brief Data processor spec for MID raw data aggregator devices -/// \author Diego Stocco -/// \date 26 February 2020 - -#ifndef O2_MID_RAWAGGREGATORSPEC_H -#define O2_MID_RAWAGGREGATORSPEC_H - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::DataProcessorSpec getRawAggregatorSpec(); -} // namespace mid -} // namespace o2 - -#endif //O2_MID_RAWAGGREGATORSPEC_H diff --git a/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx b/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx deleted file mode 100644 index 77d05a8b3374f..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx +++ /dev/null @@ -1,84 +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 MID/Workflow/src/RawDumpSpec.cxx -/// \brief Device to dump decoded raw data -/// \author Diego Stocco -/// \date 17 February 2022 - -#include "MIDWorkflow/RawDumpSpec.h" - -#include -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Logger.h" -#include "Framework/Task.h" -#include "fmt/format.h" -#include "DataFormatsMID/ROBoard.h" -#include "DataFormatsMID/ROFRecord.h" - -namespace o2 -{ -namespace mid -{ - -class RawDumpDeviceDPL -{ - public: - void init(o2::framework::InitContext& ic) - { - auto outFilename = ic.options().get("mid-dump-outfile"); - - if (!outFilename.empty()) { - mOutFile.open(outFilename.c_str()); - } - } - - void - run(o2::framework::ProcessingContext& pc) - { - - auto data = pc.inputs().get>("mid_decoded"); - auto dataROFs = pc.inputs().get>("mid_decoded_rof"); - std::stringstream ss; - for (auto& rof : dataROFs) { - ss << fmt::format("BCid: 0x{:x} Orbit: 0x{:x} EvtType: {:d}", rof.interactionRecord.bc, rof.interactionRecord.orbit, static_cast(rof.eventType)) << std::endl; - for (auto colIt = data.begin() + rof.firstEntry, end = data.begin() + rof.getEndIndex(); colIt != end; ++colIt) { - ss << *colIt << std::endl; - } - } - if (mOutFile.is_open()) { - mOutFile << ss.str(); - } else { - LOG(info) << ss.str(); - } - } - - private: - std::ofstream mOutFile; /// Output file -}; - -framework::DataProcessorSpec getRawDumpSpec() -{ - std::vector inputSpecs{ - o2::framework::InputSpec{"mid_decoded", header::gDataOriginMID, "DECODED", 0, o2::framework::Lifetime::Timeframe}, - o2::framework::InputSpec{"mid_decoded_rof", header::gDataOriginMID, "DECODEDROF", 0, o2::framework::Lifetime::Timeframe}}; - - return o2::framework::DataProcessorSpec{ - "MIDRawDataDumper", - {inputSpecs}, - {}, - o2::framework::AlgorithmSpec{o2::framework::adaptFromTask()}, - o2::framework::Options{{"mid-dump-outfile", o2::framework::VariantType::String, "", {"Dump output to file"}}}}; -} - -} // namespace mid -} // namespace o2 diff --git a/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx b/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx deleted file mode 100644 index 036b63bc75338..0000000000000 --- a/Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx +++ /dev/null @@ -1,65 +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 MID/Workflow/src/raw-dump-workflow.cxx -/// \brief MID raw dump workflow -/// \author Diego Stocco -/// \date 17 February 2022 - -#include -#include -#include "Framework/Variant.h" -#include "Framework/ConfigParamSpec.h" -#include "MIDRaw/CrateMasks.h" -#include "MIDRaw/ElectronicsDelay.h" -#include "MIDRaw/FEEIdConfig.h" -#include "MIDWorkflow/RawDumpSpec.h" -#include "MIDWorkflow/RawDecoderSpec.h" - -using namespace o2::framework; - -// add workflow options, note that customization needs to be declared before -// including Framework/runDataProcessing -void customize(std::vector& workflowOptions) -{ - std::vector - options{ - {"feeId-config-file", VariantType::String, "", {"Filename with crate FEE ID correspondence"}}, - {"crate-masks-file", VariantType::String, "", {"Filename with crate masks"}}, - {"electronics-delay-file", VariantType::String, "", {"Filename with electronics delay"}}}; - workflowOptions.insert(workflowOptions.end(), options.begin(), options.end()); -} - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - auto feeIdConfigFilename = cfgc.options().get("feeId-config-file"); - o2::mid::FEEIdConfig feeIdConfig; - if (!feeIdConfigFilename.empty()) { - feeIdConfig = o2::mid::FEEIdConfig(feeIdConfigFilename.c_str()); - } - auto crateMasksFilename = cfgc.options().get("crate-masks-file"); - o2::mid::CrateMasks crateMasks; - if (!crateMasksFilename.empty()) { - crateMasks = o2::mid::CrateMasks(crateMasksFilename.c_str()); - } - auto electronicsDelayFilename = cfgc.options().get("electronics-delay-file"); - o2::mid::ElectronicsDelay electronicsDelay; - if (!electronicsDelayFilename.empty()) { - electronicsDelay = o2::mid::readElectronicsDelay(electronicsDelayFilename.c_str()); - } - - WorkflowSpec specs; - specs.emplace_back(o2::mid::getRawDecoderSpec(true, feeIdConfig, crateMasks, electronicsDelay, false)); - specs.emplace_back(o2::mid::getRawDumpSpec()); - return specs; -} diff --git a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx index 25b038190281a..a109250894ce9 100644 --- a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h index e222328a351c0..8c73114bc53bb 100644 --- a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace phos class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::PHS, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::PHS, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx index 41642cd026089..1fc827a5a7525 100644 --- a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/Passive/CMakeLists.txt b/Detectors/Passive/CMakeLists.txt index 0976530bc6571..a24954ad10539 100644 --- a/Detectors/Passive/CMakeLists.txt +++ b/Detectors/Passive/CMakeLists.txt @@ -23,6 +23,7 @@ o2_add_library(DetectorsPassive src/Hall.cxx src/HallSimParam.cxx src/PassiveBase.cxx + src/ExternalModule.cxx PUBLIC_LINK_LIBRARIES O2::Field O2::DetectorsBase O2::SimConfig) o2_target_root_dictionary(DetectorsPassive @@ -39,6 +40,7 @@ o2_target_root_dictionary(DetectorsPassive include/DetectorsPassive/Hall.h include/DetectorsPassive/HallSimParam.h include/DetectorsPassive/PassiveBase.h + include/DetectorsPassive/ExternalModule.h LINKDEF src/PassiveLinkDef.h) # FIXME: if PutFrameInTop really depends on TRD, then the following can not work diff --git a/Detectors/Passive/include/DetectorsPassive/ExternalModule.h b/Detectors/Passive/include/DetectorsPassive/ExternalModule.h new file mode 100644 index 0000000000000..155870ae42a6d --- /dev/null +++ b/Detectors/Passive/include/DetectorsPassive/ExternalModule.h @@ -0,0 +1,64 @@ +// 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_PASSIVE_EXTERNALMODULE_H +#define ALICEO2_PASSIVE_EXTERNALMODULE_H + +#include "DetectorsPassive/PassiveBase.h" // base class of passive modules +#include "Rtypes.h" // for Pipe::Class, ClassDef, Pipe::Streamer + +class TGeoVolume; +class TGeoTransformation; + +namespace o2 +{ +namespace passive +{ + +// options used to configure a generic plug and play external module +struct ExternalModuleOptions { + std::string root_macro_file; // the file where to lookup the ROOT geometry building macro + std::string top_volume; // the volume to be added + std::string anchor_volume; // the volume into which the detector will be hooked + TGeoMatrix const* placement = nullptr; // how to place the module inside anchor_volume +}; + +// a module (passive material) defined externally (ROOT macro / GDML / TGeo geometry) +class ExternalModule : public PassiveBase +{ + public: + ExternalModule(const char* name, const char* long_title, ExternalModuleOptions options); + ExternalModule() = default; // default constructor + + ~ExternalModule() override = default; + void ConstructGeometry() override; + + /// Clone this object (used in MT mode only) + FairModule* CloneModule() const override { return nullptr; } + + typedef std::function GeomBuilderFcn; // function hook for external geometry builder + + private: + // void createMaterials(); + ExternalModule(const ExternalModule& orig); + ExternalModule& operator=(const ExternalModule&); + + GeomBuilderFcn mGeomHook; + ExternalModuleOptions mOptions; + + bool initGeomBuilderHook(); // function to load/JIT Geometry builder hook + void remapMedia(TGeoVolume* vol); // performs a remapping of materials/media IDs after registration with VMC + + // ClassDefOverride(ExternalModule, 0); +}; +} // namespace passive +} // namespace o2 +#endif diff --git a/Detectors/Passive/src/ExternalModule.cxx b/Detectors/Passive/src/ExternalModule.cxx new file mode 100644 index 0000000000000..fc6bd6953b82d --- /dev/null +++ b/Detectors/Passive/src/ExternalModule.cxx @@ -0,0 +1,175 @@ +// 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. + +// Sandro Wenzel (CERN), 2026 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ClassImp(o2::passive::ExternalModule) + +namespace o2::passive +{ + +ExternalModule::ExternalModule(const char* name, const char* long_title, ExternalModuleOptions options) : PassiveBase(name, long_title), mOptions(options) +{ +} + +void ExternalModule::remapMedia(TGeoVolume* top_volume) +{ + std::unordered_map medium_ptr_mapping; + std::unordered_set volumes_already_treated; + int counter = 1; + + auto modulename = GetName(); + + // The transformer function + auto transform_media = [&](TGeoVolume* vol_) { + if (volumes_already_treated.find(vol_) != volumes_already_treated.end()) { + // this volume was already transformed + return; + } + volumes_already_treated.insert(vol_); + + if (dynamic_cast(vol_)) { + // do nothing for assemblies (they don't have a medium) + return; + } + + auto medium = vol_->GetMedium(); + if (!medium) { + return; + } + + auto iter = medium_ptr_mapping.find(medium); + if (iter != medium_ptr_mapping.end()) { + // This medium has already been transformed, so + // we just update the volume + vol_->SetMedium(iter->second); + return; + } else { + std::cout << "Transforming media with name " << medium->GetName() << " for volume " << vol_->GetName() << "\n"; + + // we found a medium, not yet treated + auto curr_mat = medium->GetMaterial(); + auto& matmgr = o2::base::MaterialManager::Instance(); + + matmgr.Material(modulename, counter, curr_mat->GetName(), curr_mat->GetA(), curr_mat->GetZ(), curr_mat->GetDensity(), curr_mat->GetRadLen(), curr_mat->GetIntLen()); + // TGeo medium params are stored in a flat array with the following convention + // fParams[0] = isvol; + // fParams[1] = ifield; + // fParams[2] = fieldm; + // fParams[3] = tmaxfd; + // fParams[4] = stemax; + // fParams[5] = deemax; + // fParams[6] = epsil; + // fParams[7] = stmin; + const auto isvol = medium->GetParam(0); + const auto isxfld = medium->GetParam(1); + const auto sxmgmx = medium->GetParam(2); + const auto tmaxfd = medium->GetParam(3); + const auto stemax = medium->GetParam(4); + const auto deemax = medium->GetParam(5); + const auto epsil = medium->GetParam(6); + const auto stmin = medium->GetParam(7); + + matmgr.Medium(modulename, counter, medium->GetName(), counter, isvol, isxfld, sxmgmx, tmaxfd, stemax, deemax, epsil, stmin); + + // there will be new Material and Medium objects; fetch them + auto new_med = matmgr.getTGeoMedium(modulename, counter); + + // insert into cache + medium_ptr_mapping[medium] = new_med; + vol_->SetMedium(new_med); + counter++; + } + }; // end transformer lambda + + // a generic volume walker + std::function visit_volume; + visit_volume = [&](TGeoVolume* vol) -> void { + if (!vol) { + return; + } + + // call the transformer + transform_media(vol); + + // Recurse into daughters + const int nd = vol->GetNdaughters(); + for (int i = 0; i < nd; ++i) { + TGeoNode* node = vol->GetNode(i); + if (!node) { + continue; + } + TGeoVolume* child = node->GetVolume(); + if (!child) { + continue; + } + + visit_volume(child); + } + }; + + visit_volume(top_volume); +} + +void ExternalModule::ConstructGeometry() +{ + // JIT the geom builder hook + if (!initGeomBuilderHook()) { + LOG(error) << " Could not load geometry builder hook"; + return; + } + + // otherwise execute it and obtain pointer to top most module volume + auto module_top = mGeomHook(); + if (!module_top) { + LOG(error) << "No module found\n"; + return; + } + + remapMedia(const_cast(module_top)); + + // place it into the provided anchor volume (needs to exist) + auto anchor = gGeoManager->FindVolumeFast(mOptions.anchor_volume.c_str()); + if (!anchor) { + LOG(error) << "Anchor volume " << mOptions.anchor_volume << " not found. Aborting"; + return; + } + anchor->AddNode(const_cast(module_top), 1, const_cast(mOptions.placement)); +} + +bool ExternalModule::initGeomBuilderHook() +{ + if (mOptions.root_macro_file.size() > 0) { + LOG(info) << "Initializing the hook for geometry module building"; + auto expandedHookFileName = o2::utils::expandShellVarsInFileName(mOptions.root_macro_file); + if (std::filesystem::exists(expandedHookFileName)) { + // if this file exists we will compile the hook on the fly (the last one is an identifier --> maybe make it dependent on this class) + mGeomHook = o2::conf::GetFromMacro(mOptions.root_macro_file, "get_builder_hook_unchecked()", "function", "o2_passive_extmodule_builder"); + LOG(info) << "Hook initialized from file " << expandedHookFileName; + return true; + } + } + return false; +} + +} // namespace o2::passive \ No newline at end of file diff --git a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h index aaab8a06e5e86..4c8f5cdae8654 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/LHCClockCalibrator.h @@ -99,6 +99,8 @@ class LHCClockCalibrator final : public o2::calibration::TimeSlotCalibration("nbins")); auto slotL = ic.options().get("tf-per-slot"); auto delay = ic.options().get("max-delay"); + std::string path = ic.options().get("output-path"); + mCalibrator = std::make_unique(minEnt, nb); + mCalibrator->setPath(path.data()); mCalibrator->setSlotLength(slotL); mCalibrator->setMaxSlotsDelay(delay); @@ -216,6 +219,7 @@ DataProcessorSpec getLHCClockCalibDeviceSpec(bool useCCDB) AlgorithmSpec{adaptFromTask(ccdbRequest, useCCDB)}, Options{ {"tf-per-slot", VariantType::UInt32, 5u, {"number of TFs per calibration time slot"}}, + {"output-path", VariantType::String, "TOF/Calib/LHCphaseSync", {"path to ccdb output"}}, {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}, {"min-entries", VariantType::Int, 500, {"minimum number of entries to fit single time slot"}}, {"nbins", VariantType::Int, 4000, {"number of bins for "}}}}; diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h index 53cdf59d08572..4ae99bae86eba 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h @@ -34,7 +34,7 @@ namespace tof class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TOF, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TOF, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx index 5cf882e2723d6..547b3235ca684 100644 --- a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h index 20e470702a89a..ff7c763efcd2b 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CalibdEdx.h @@ -129,7 +129,9 @@ class CalibdEdx /// Compute MIP position from dEdx histograms and save result in the correction container. /// To retrieve the correction call `CalibdEdx::getCalib()` /// \param useGausFits make gaussian fits of dEdx vs tgl instead of fitting the mean dEdx - void finalize(const bool useGausFits = true); + /// \param averageSectors If true, the correction is averaged over all sectors. + /// In this case, no mean-sector scaling is applied when statistics are low. + void finalize(const bool useGausFits = true, const bool averageSectors = false); /// Return calib data histogram const Hist& getHist() const { return mHist; } diff --git a/Detectors/TPC/calibration/src/CalibdEdx.cxx b/Detectors/TPC/calibration/src/CalibdEdx.cxx index 4eb29c8833565..938ab8ae91065 100644 --- a/Detectors/TPC/calibration/src/CalibdEdx.cxx +++ b/Detectors/TPC/calibration/src/CalibdEdx.cxx @@ -351,7 +351,7 @@ auto ProjectBoostHistoXFastAllSectors(const Hist& hist, std::vector& bin_in // access the bin content specified by bin_indices const float counts = hist.at(bin_indices); - float dEdx = hist.axis(ax::dEdx).value(i); + float dEdx = hist.axis(ax::dEdx).bin(i).center(); // scale the dedx to the mean if (stackMean != nullptr) { @@ -532,7 +532,7 @@ void CalibdEdx::fitHistGaus(TLinearFitter& fitter, CalibdEdxCorrection& corr, co LOGP(info, "Calibration fits took: {}", time.count()); } -void CalibdEdx::finalize(const bool useGausFits) +void CalibdEdx::finalize(const bool useGausFits, const bool averageSectors) { const float entries = minStackEntries(); mCalib.clear(); @@ -565,10 +565,15 @@ void CalibdEdx::finalize(const bool useGausFits) // get mean of each GEM stack CalibdEdxCorrection meanCorr{}; meanCorr.setDims(0); - TLinearFitter meanFitter(0); - meanFitter.SetFormula("1"); - // get the mean dEdx for each stack - fitHist(mHist, meanCorr, meanFitter, mFitCut, mFitLowCutFactor, mFitPasses); + if (averageSectors) { + // set mean dEdx per stack to unity + meanCorr.setUnity(); + } else { + // get the mean dEdx for each stack + TLinearFitter meanFitter(0); + meanFitter.SetFormula("1"); + fitHist(mHist, meanCorr, meanFitter, mFitCut, mFitLowCutFactor, mFitPasses, nullptr, mDebugOutputStreamer.get()); + } if (!useGausFits) { // get higher dimension corrections with projected sectors fitHist(mHist, mCalib, fitter, mFitCut, mFitLowCutFactor, mFitPasses, &meanCorr, mDebugOutputStreamer.get()); diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index 2c6fac7dcde2a..0d2d37a73d7db 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -122,7 +122,7 @@ struct MergedColumnsDecoder { class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TPC, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TPC, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode compressed clusters to flat buffer diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h deleted file mode 100644 index d86a845b0fe4c..0000000000000 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h +++ /dev/null @@ -1,65 +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 ClusterContainer.h -/// \brief Container class for TPC clusters -#ifndef _ALICEO2_TPC_ClusterContainer_ -#define _ALICEO2_TPC_ClusterContainer_ - -#include -#include -#include // for Float_t etc - -namespace o2 -{ -namespace tpc -{ - -/// \class ClusterContainer -/// \brief Container class for TPC clusters -class ClusterContainer -{ - public: - // Initialize the clones array - // @param clusterType Possibility to store different types of clusters - // void InitArray(const Char_t* clusterType="o2::tpc::Cluster"); - - /// Add cluster to array - /// @param output, the vector to append to - /// @param cru CRU (sector) - /// @param row Row - /// @param q Total charge of cluster - /// @param qmax Maximum charge in a single cell (pad, time) - /// @param padmean Mean position of cluster in pad direction - /// @param padsigma Sigma of cluster in pad direction - /// @param timemean Mean position of cluster in time direction - /// @param timesigma Sigma of cluster in time direction - template - static ClusterType* addCluster(std::vector* output, - Int_t cru, Int_t row, Float_t qTot, Float_t qMax, - Float_t meanpad, Float_t meantime, Float_t sigmapad, - Float_t sigmatime) - { - assert(output); - output->emplace_back(); // emplace_back a defaut constructed cluster of type ClusterType - auto& cluster = output->back(); - // set its concrete parameters: - // ATTENTION: the order of parameters in setParameters is different than in AddCluster! - cluster.setParameters(cru, row, qTot, qMax, - meanpad, sigmapad, - meantime, sigmatime); - return &cluster; - } -}; -} // namespace tpc -} // namespace o2 - -#endif diff --git a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx index c09eb193e0fbf..3256b875447a8 100644 --- a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}, ConfigParamSpec{"inputFromFile", VariantType::Bool, false, {"Expect COMPCLUSTERS from file"}}}; diff --git a/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx b/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx deleted file mode 100644 index c17b68e307328..0000000000000 --- a/Detectors/TPC/workflow/src/time-series-merge-integrator.cxx +++ /dev/null @@ -1,34 +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 "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 deleted file mode 100644 index ccedbdf4f9599..0000000000000 --- a/Detectors/TPC/workflow/src/time-series-reader.cxx +++ /dev/null @@ -1,25 +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 "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-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 07b1c293bff98..f3d4d639ddfd2 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -71,7 +71,7 @@ void customize(std::vector& workflowOptions) {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, {"filtered-input", VariantType::Bool, false, {"Filtered tracks, clusters input, prefix dataDescriptors with F"}}, {"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}, - {"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + {"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, {"tpc-deadMap-sources", VariantType::Int, -1, {"Sources to consider for TPC dead channel map creation; -1=all, 0=deactivated"}}, {"tpc-mc-time-gain", VariantType::Bool, false, {"use time gain calibration for MC (true) or for data (false)"}}, }; diff --git a/Detectors/TRD/base/src/TrackletTransformer.cxx b/Detectors/TRD/base/src/TrackletTransformer.cxx index 58938cfd99161..7f9976b8ce714 100644 --- a/Detectors/TRD/base/src/TrackletTransformer.cxx +++ b/Detectors/TRD/base/src/TrackletTransformer.cxx @@ -50,7 +50,8 @@ float TrackletTransformer::calculateDy(int detector, int slope, const PadPlane* // NOTE: check what drift height is used in calibration code to ensure consistency // NOTE: check sign convention of Lorentz angle // NOTE: confirm the direction in which vDrift is measured/determined. Is it in x or in direction of drift? - double lorentzCorrection = TMath::Tan(exb) * mXAnode; + // The Lorentz correction have to be applied both at the point of entrance and at the end of the drift region + double lorentzCorrection = TMath::Tan(exb) * mGeo->cdrHght(); // assuming angle in Bailhache, fig. 4.17 would be positive in our calibration code double calibratedDy = rawDy - lorentzCorrection; diff --git a/Detectors/TRD/calibration/README.md b/Detectors/TRD/calibration/README.md index bdbfc9e709800..f769fa99b5778 100644 --- a/Detectors/TRD/calibration/README.md +++ b/Detectors/TRD/calibration/README.md @@ -27,15 +27,15 @@ For 'o2-calibration-trd-workflow --vDriftAndExB' there are also the following ke *Hint: You can get information on the meaning of the parameters by running `o2-calibration-trd-workflow --vDriftAndExB -b --help full`* -If you want to run the calibration from a local file with residuals, trdangreshistos.root, you can run: +If you want to run the calibration from a local file with residuals, trdcaliboutput.root, you can run: - o2-calibration-trd-workflow --vDriftAndExB -b --enable-root-input --calib-vdexb-calibration '--tf-per-slot 1' --configKeyValues "TRDCalibParams.minEntriesChamber=100;TRDCalibParams.minEntriesTotal=50000" + o2-calibration-trd-workflow --vDriftAndExB -b --enable-root-input --calib-vdexb-calibration '--tf-per-slot 1' --configKeyValues "TRDCalibParams.minEntriesChamber=100;TRDCalibParams.minEntriesTotal=50000" --trd-calib-infile trdcaliboutput.root Additionally it is possible to perform the calibrations fit manually per chamber if you have TPC-TRD or ITS-TPC-TRD tracks, you can run: o2-trd-global-tracking -b --enable-trackbased-calib -This produces `trdangreshistos.root` which holds the residuals of the angles and differences. +This produces `trdcaliboutput.root` which holds the residuals of the angles and differences. Then run the macro `Detectors/TRD/calibration/macros/manualCalibFit.C`. This produces a file of similar name with the fitted data and prints out the fit results. This is equivalent to running: diff --git a/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h b/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h index 677673a7f85f3..cadd21af2a55d 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h @@ -25,12 +25,13 @@ namespace trd /// VDrift and ExB calibration parameters. struct TRDCalibParams : public o2::conf::ConfigurableParamHelper { unsigned int nTrackletsMin = 5; ///< minimum amount of tracklets + unsigned int nTrackletsMinLoose = 4; ///< minimum amount of tracklets if two layers with a large lever arm both have a hit unsigned int chi2RedMax = 6; ///< maximum reduced chi2 acceptable for track quality - size_t minEntriesChamber = 75; ///< minimum number of entries per chamber to fit single time slot - size_t minEntriesTotal = 40'500; ///< minimum total required for meaningful fits + size_t minEntriesChamber = 200; ///< minimum number of entries per chamber to fit single time slot + size_t minEntriesTotal = 400'000; ///< minimum total required for meaningful fits // For gain calibration - unsigned int nTrackletsMinGainCalib = 5; + unsigned int nTrackletsMinGainCalib = 3; size_t minEntriesChamberGainCalib = 500; ///< minimum number of entries per chamber to fit single time slot size_t minEntriesTotalGainCalib = 1'000'000; ///< minimum total required for meaningful fits // Cuts for selecting clean pion candidates for gain calibration diff --git a/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h b/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h index 7d55850af9fd4..16223f3e78112 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h @@ -88,8 +88,8 @@ class CalibratorVdExB final : public o2::calibration::TimeSlotCalibration mOutFile{nullptr}; ///< output file std::unique_ptr mOutTree{nullptr}; ///< output tree diff --git a/Detectors/TRD/calibration/macros/manualCalibFit.C b/Detectors/TRD/calibration/macros/manualCalibFit.C index d31744a2e727c..877202d4e211e 100644 --- a/Detectors/TRD/calibration/macros/manualCalibFit.C +++ b/Detectors/TRD/calibration/macros/manualCalibFit.C @@ -30,18 +30,19 @@ // O2 header #include +#include "DetectorsBase/Propagator.h" #endif // This root macro reads in 'trdangreshistos.root' and // performs the calibration fits manually as in CalibratorVdExB.cxx // This can be used for checking if the calibration fits make sense. -void manualCalibFit() +void manualCalibFit(int runNumber = 563335, bool usePreCorrFromCCDB = false) { //---------------------------------------------------- // TTree and File //---------------------------------------------------- - std::unique_ptr inFilePtr(TFile::Open("trdangreshistos.root")); + std::unique_ptr inFilePtr(TFile::Open("trdcaliboutput.root")); if (inFilePtr == nullptr) { printf("Input File could not be read!\n'"); return; @@ -60,18 +61,46 @@ void manualCalibFit() tree->SetBranchAddress("mHistogramEntries[13500]", &mHistogramEntries); tree->SetBranchAddress("mNEntriesPerBin[13500]", &mNEntriesPerBin); + // use precorr values from ccdb + // necessary when the angular residuals were calculated already using ccdb calibration (e.g. in a local run) + + o2::trd::CalVdriftExB* calObject; + if (usePreCorrFromCCDB) { + auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); + + o2::ccdb::CcdbApi ccdb; + ccdb.init("http://alice-ccdb.cern.ch"); + auto runDuration = ccdbmgr.getRunDuration(runNumber); + + std::map metadata; + std::map headers; + + calObject = ccdb.retrieveFromTFileAny("TRD/Calib/CalVdriftExB", metadata, runDuration.first + 60000, &headers, "", "", "1689478811721"); + } + //---------------------------------------------------- // Configure Fitter //---------------------------------------------------- o2::trd::FitFunctor mFitFunctor; std::array, 540> profiles; ///< profile histograms for each TRD chamber + int counter = 0; for (int iDet = 0; iDet < 540; ++iDet) { mFitFunctor.profiles[iDet] = std::make_unique(Form("profAngleDiff_%i", iDet), Form("profAngleDiff_%i", iDet), 25, -25.f, 25.f); + if (usePreCorrFromCCDB) { + if (calObject->isGoodExB(iDet)) + counter++; + mFitFunctor.vdPreCorr[iDet] = calObject->getVdrift(iDet, true); + mFitFunctor.laPreCorr[iDet] = calObject->getExB(iDet, true); + } } + std::cout << counter << " good entries in the CCDB " << std::endl; + mFitFunctor.mAnodePlane = 3.35; // don't really care as long as it's not zero, this parameter could be removed mFitFunctor.lowerBoundAngleFit = 80 * TMath::DegToRad(); mFitFunctor.upperBoundAngleFit = 100 * TMath::DegToRad(); - mFitFunctor.vdPreCorr.fill(1.546); - mFitFunctor.laPreCorr.fill(0.0); + if (!usePreCorrFromCCDB) { + mFitFunctor.vdPreCorr.fill(1.546); + mFitFunctor.laPreCorr.fill(0.0); + } //---------------------------------------------------- // Loop @@ -88,15 +117,18 @@ void manualCalibFit() //---------------------------------------------------- // Fill profiles //---------------------------------------------------- + int nEntriesDetTotal[540] = {}; for (int iDet = 0; iDet < 540; ++iDet) { for (int iBin = 0; iBin < 25; ++iBin) { auto angleDiffSum = mHistogramEntriesSum[iDet * 25 + iBin]; auto nEntries = mNEntriesPerBinSum[iDet * 25 + iBin]; + nEntriesDetTotal[iDet] += nEntries; if (nEntries > 0) { // skip entries which have no entries; ? // add to the respective profile for fitting later on mFitFunctor.profiles[iDet]->Fill(2 * iBin - 25.f, angleDiffSum / nEntries, nEntries); } } + printf("Det %d: nEntries=%d \n", iDet, nEntriesDetTotal[iDet]); } //---------------------------------------------------- @@ -105,16 +137,23 @@ void manualCalibFit() printf("-------- Started fits\n"); std::array laFitResults{}; std::array vdFitResults{}; + + TH1F* hVd = new TH1F("hVd", "v drift", 150, 0.5, 2.); + TH1F* hLa = new TH1F("hLa", "lorentz angle", 200, -25., 25.); + o2::trd::CalVdriftExB* calObjectOut = new o2::trd::CalVdriftExB(); + for (int iDet = 0; iDet < 540; ++iDet) { + if (nEntriesDetTotal[iDet] < 75) + continue; mFitFunctor.currDet = iDet; ROOT::Fit::Fitter fitter; double paramsStart[2]; - paramsStart[0] = 0. * TMath::DegToRad(); + paramsStart[0] = 0.; paramsStart[1] = 1.; fitter.SetFCN(2, mFitFunctor, paramsStart); fitter.Config().ParSettings(0).SetLimits(-0.7, 0.7); fitter.Config().ParSettings(0).SetStepSize(.01); - fitter.Config().ParSettings(1).SetLimits(0., 3.); + fitter.Config().ParSettings(1).SetLimits(0.01, 3.); fitter.Config().ParSettings(1).SetStepSize(.01); ROOT::Math::MinimizerOptions opt; opt.SetMinimizerType("Minuit2"); @@ -127,14 +166,28 @@ void manualCalibFit() auto fitResult = fitter.Result(); laFitResults[iDet] = fitResult.Parameter(0); vdFitResults[iDet] = fitResult.Parameter(1); - printf("Det %d: la=%f\tvd=%f\n", iDet, laFitResults[iDet] * TMath::RadToDeg(), vdFitResults[iDet]); + if (fitResult.MinFcnValue() > 0.03) + continue; + printf("Det %d: la=%.3f \tvd=%.3f \t100*minValue=%f \tentries=%d\n", iDet, laFitResults[iDet] * TMath::RadToDeg(), vdFitResults[iDet], 100 * fitResult.MinFcnValue(), nEntriesDetTotal[iDet]); + hVd->Fill(vdFitResults[iDet]); + hLa->Fill(laFitResults[iDet] * TMath::RadToDeg()); + calObjectOut->setVdrift(iDet, vdFitResults[iDet]); + calObjectOut->setExB(iDet, laFitResults[iDet]); } printf("-------- Finished fits\n"); + std::cout << "number of chambers with enough entries: " << hVd->GetEntries() << std::endl; + ; + std::cout << "vdrift mean: " << hVd->GetMean() << " sigma: " << hVd->GetStdDev() << std::endl; + std::cout << "lorentz angle mean: " << hLa->GetMean() << " sigma: " << hLa->GetStdDev() << std::endl; + //---------------------------------------------------- // Write //---------------------------------------------------- std::unique_ptr outFilePtr(TFile::Open("manualCalibFit.root", "RECREATE")); + hVd->Write(); + hLa->Write(); + outFilePtr->WriteObjectAny(calObjectOut, "o2::trd::CalVdriftExB", "calObject"); for (auto& p : mFitFunctor.profiles) p->Write(); } diff --git a/Detectors/TRD/calibration/src/CalibratorVdExB.cxx b/Detectors/TRD/calibration/src/CalibratorVdExB.cxx index 64a8664640e41..fef7bdecef38c 100644 --- a/Detectors/TRD/calibration/src/CalibratorVdExB.cxx +++ b/Detectors/TRD/calibration/src/CalibratorVdExB.cxx @@ -24,6 +24,7 @@ #include "CCDB/BasicCCDBManager.h" #include "CommonUtils/NameConf.h" #include "CommonUtils/MemFileHelper.h" +#include "DetectorsBase/Propagator.h" #include #include @@ -105,17 +106,37 @@ void CalibratorVdExB::initProcessing() return; } - mFitFunctor.lowerBoundAngleFit = 80 * TMath::DegToRad(); - mFitFunctor.upperBoundAngleFit = 100 * TMath::DegToRad(); + // fit is done in region where ion tails are small, close to lorentz angle + // we want an approximate value of the lorentz angle in order to define better fit boundaries, coinciding with profile bin edges + float bz = o2::base::Propagator::Instance()->getNominalBz(); + // default angle with zero field is slightly shifted + float lorentzAngleAvg = -1.f; + if (TMath::Abs(bz - 2) < 0.1f) { + lorentzAngleAvg = 3.f; + } + if (TMath::Abs(bz + 2) < 0.1f) { + lorentzAngleAvg = -5.f; + } + if (TMath::Abs(bz - 5) < 0.1f) { + lorentzAngleAvg = 7.f; + } + if (TMath::Abs(bz + 5) < 0.1f) { + lorentzAngleAvg = -9.f; + } + + LOGP(info, "b field: {} lorentz angle start: {}", bz, lorentzAngleAvg); + + mFitFunctor.lowerBoundAngleFit = (80 + lorentzAngleAvg) * TMath::DegToRad(); + mFitFunctor.upperBoundAngleFit = (100 + lorentzAngleAvg) * TMath::DegToRad(); mFitFunctor.mAnodePlane = GeometryBase::camHght() / (2.f * 100.f); for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) { mFitFunctor.profiles[iDet] = std::make_unique(Form("profAngleDiff_%i", iDet), Form("profAngleDiff_%i", iDet), NBINSANGLEDIFF, -MAXIMPACTANGLE, MAXIMPACTANGLE); } mFitter.SetFCN(2, mFitFunctor, mParamsStart); - mFitter.Config().ParSettings(ParamIndex::LA).SetLimits(-0.7, 0.7); + mFitter.Config().ParSettings(ParamIndex::LA).SetLimits(constants::EXBMIN, constants::EXBMAX); mFitter.Config().ParSettings(ParamIndex::LA).SetStepSize(.01); - mFitter.Config().ParSettings(ParamIndex::VD).SetLimits(0.01, 3.); + mFitter.Config().ParSettings(ParamIndex::VD).SetLimits(constants::VDRIFTMIN, constants::VDRIFTMAX); mFitter.Config().ParSettings(ParamIndex::VD).SetStepSize(.01); ROOT::Math::MinimizerOptions opt; opt.SetMinimizerType("Minuit2"); @@ -184,17 +205,30 @@ void CalibratorVdExB::finalizeSlot(Slot& slot) } // Check if we have the minimum amount of entries if (sumEntries < mMinEntriesChamber) { - LOGF(debug, "Chamber %d did not reach minimum amount of entries for refit", iDet); + LOGF(debug, "Chamber %d did not reach minimum amount of entries for refit: %d", iDet, sumEntries); continue; } + float laPreCorrTemp = mFitFunctor.laPreCorr[iDet]; + float vdPreCorrTemp = mFitFunctor.vdPreCorr[iDet]; + // Here we start from uncalibrated values, otherwise online calibration does not work properly + mFitFunctor.laPreCorr[iDet] = EXBDEFAULT; + mFitFunctor.vdPreCorr[iDet] = VDRIFTDEFAULT; + // Reset Start Parameter mParamsStart[ParamIndex::LA] = 0.0; mParamsStart[ParamIndex::VD] = 1.0; mFitter.FitFCN(); auto fitResult = mFitter.Result(); + if (fitResult.MinFcnValue() > 0.03) { + LOGF(debug, "Chamber %d fit did not converge properly, minimization value too high: %f", iDet, fitResult.MinFcnValue()); + // The fit did not work properly, so we keep previous values + mFitFunctor.laPreCorr[iDet] = laPreCorrTemp; + mFitFunctor.vdPreCorr[iDet] = vdPreCorrTemp; + continue; + } laFitResults[iDet] = fitResult.Parameter(ParamIndex::LA); vdFitResults[iDet] = fitResult.Parameter(ParamIndex::VD); - LOGF(debug, "Fit result for chamber %i: vd=%f, la=%f", iDet, vdFitResults[iDet], laFitResults[iDet] * TMath::RadToDeg()); + LOGF(debug, "Fit result for chamber %i: vd=%f, la=%f, minimizer value=%f", iDet, vdFitResults[iDet], laFitResults[iDet] * TMath::RadToDeg(), fitResult.MinFcnValue()); // Update fit values for next fit mFitFunctor.laPreCorr[iDet] = laFitResults[iDet]; mFitFunctor.vdPreCorr[iDet] = vdFitResults[iDet]; @@ -222,7 +256,7 @@ void CalibratorVdExB::finalizeSlot(Slot& slot) auto flName = o2::ccdb::CcdbApi::generateFileName(clName); std::map metadata; // TODO: do we want to store any meta data? long startValidity = slot.getStartTimeMS() - 10 * o2::ccdb::CcdbObjectInfo::SECOND; - mInfoVector.emplace_back("TRD/Calib/CalVdriftExB", clName, flName, metadata, startValidity, startValidity + o2::ccdb::CcdbObjectInfo::HOUR); + mInfoVector.emplace_back("TRD/Calib/CalVdriftExB", clName, flName, metadata, startValidity, startValidity + 1 * o2::ccdb::CcdbObjectInfo::HOUR); mObjectVector.push_back(calObject); } diff --git a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx index 8fe195f861389..ae1f7b33c6bba 100644 --- a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx +++ b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx @@ -190,7 +190,10 @@ int TrackBasedCalib::doTrdOnlyTrackFits(gsl::span& tracks) for (const auto& trkIn : tracks) { if (trkIn.getNtracklets() < params.nTrackletsMin) { // with less than 3 tracklets the TRD-only refit not meaningful - continue; + if (trkIn.getNtracklets() < params.nTrackletsMinLoose || !((trkIn.getTrackletIndex(0) >= 0 && (trkIn.getTrackletIndex(NLAYER - 1) >= 0 || trkIn.getTrackletIndex(NLAYER - 2) >= 0))) || (trkIn.getTrackletIndex(1) >= 0 && trkIn.getTrackletIndex(NLAYER - 1) >= 0)) { + // we check if we have enough lever arm, i.e. (first and last) or (second and last) or (first and before last) are present + continue; + } } auto trkWork = trkIn; // input is const, so we need to create a copy bool trackFailed = false; @@ -262,9 +265,20 @@ int TrackBasedCalib::doTrdOnlyTrackFits(gsl::span& tracks) } float trkAngle = o2::math_utils::asin(trkWork.getSnp()) * TMath::RadToDeg(); - float trkltAngle = o2::math_utils::atan(mTrackletsCalib[trkWork.getTrackletIndex(iLayer)].getDy() / Geometry::cdrHght()) * TMath::RadToDeg(); + int trkltId = trkWork.getTrackletIndex(iLayer); + // tracklet angle, corrected for pad tilt + const PadPlane* pad = Geometry::instance()->getPadPlane(mTrackletsRaw[trkltId].getDetector()); + float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees + float tiltCorrUp = tilt * trkWork.getTgl() * Geometry::cdrHght(); + float padLength = pad->getRowSize(mTrackletsRaw[trkltId].getPadRow()); + if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::fabs(mTrackletsCalib[trkltId].getZ() - trkWork.getZ()) < padLength))) { + tiltCorrUp = 0.f; + } + // use uncalibrated dy because online calibration does not work otherwise + float trkltDy = mTrackletsRaw[trkltId].getUncalibratedDy(30.f / o2::trd::constants::VDRIFTDEFAULT) + tiltCorrUp; + float trkltAngle = o2::math_utils::atan(trkltDy / Geometry::cdrHght()) * TMath::RadToDeg(); float angleDeviation = trkltAngle - trkAngle; - if (mAngResHistos.addEntry(angleDeviation, trkAngle, mTrackletsRaw[trkWork.getTrackletIndex(iLayer)].getDetector())) { + if (mAngResHistos.addEntry(angleDeviation, trkAngle, mTrackletsRaw[trkltId].getDetector())) { // track impact angle out of histogram range continue; } diff --git a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h index adb584ef15ec4..b34c2da395c5e 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace trd class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TRD, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TRD, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h index f45b7a1808287..e4d6a1641ed50 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h @@ -147,7 +147,7 @@ DataProcessorSpec getTRDVdAndExBCalibSpec() auto ccdbRequest = std::make_shared(true, // orbitResetTime true, // GRPECS=true false, // GRPLHCIF - false, // GRPMagField + true, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::None, // geometry inputs); diff --git a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx index 177f6e4913a26..dff3831cd78a5 100644 --- a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 9303979ada930..4b139272834f1 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -129,7 +129,8 @@ void Detector::buildFT3FromFile(std::string configFileName) //_________________________________________________________________________________________________ void Detector::exportLayout() { - // Export FT3 Layout description to file. One line per disk + // Export FT3 Layout description to file. + // One line per disk: // z_layer r_in r_out Layerx2X0 std::string configFileName = "FT3_layout.cfg"; @@ -795,8 +796,8 @@ void Detector::defineSensitiveVolumes() AddSensitiveVolume(v); } else { // OT disks for (int sensor_count = 0; sensor_count < MAX_SENSORS; ++sensor_count) { - std::string sensor_name_front = "FT3sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - std::string sensor_name_back = "FT3sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name_front = "FT3Sensor_front_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name_back = "FT3Sensor_back_" + std::to_string(iLayer) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); v = geoManager->GetVolume(sensor_name_front.c_str()); if (v) { AddSensitiveVolume(v); diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index efcad74bc2cb9..9318554837706 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -108,7 +108,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double double carbonFiberThickness = 0.01; - double foamSpacingThickness = 0.5; + double foamSpacingThickness = 1.0; int dist_offset = 0; @@ -481,7 +481,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double if (sensor_width == 2.5) { // silicon - std::string sensor_name = "FT3sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); @@ -495,7 +495,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } else { - std::string sensor_name = "FT3sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); @@ -652,7 +652,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double if (sensor_width == 2.5) { - std::string sensor_name = "FT3sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); @@ -666,7 +666,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } else { // active (4.6 cm centered) - std::string sensor_name = "FT3sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + std::string sensor_name = "FT3Sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); sensor = geoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); sensor->SetLineColor(SiColor); sensor->SetFillColorAlpha(SiColor, 0.4); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h index 177426e8dba09..577bd1bcabaf1 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h @@ -32,11 +32,15 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache // Inner TOF static const char* getITOFLayerPattern() { return sITOFLayerName.c_str(); } + static const char* getITOFStavePattern() { return sITOFStaveName.c_str(); } + static const char* getITOFModulePattern() { return sITOFModuleName.c_str(); } static const char* getITOFChipPattern() { return sITOFChipName.c_str(); } static const char* getITOFSensorPattern() { return sITOFSensorName.c_str(); } // Outer TOF static const char* getOTOFLayerPattern() { return sOTOFLayerName.c_str(); } + static const char* getOTOFStavePattern() { return sOTOFStaveName.c_str(); } + static const char* getOTOFModulePattern() { return sOTOFModuleName.c_str(); } static const char* getOTOFChipPattern() { return sOTOFChipName.c_str(); } static const char* getOTOFSensorPattern() { return sOTOFSensorName.c_str(); } @@ -81,11 +85,15 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache // Inner TOF static std::string sITOFLayerName; + static std::string sITOFStaveName; + static std::string sITOFModuleName; static std::string sITOFChipName; static std::string sITOFSensorName; // Outer TOF static std::string sOTOFLayerName; + static std::string sOTOFStaveName; + static std::string sOTOFModuleName; static std::string sOTOFChipName; static std::string sOTOFSensorName; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 10d8c5ced94dd..b74fc6d6869dd 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -25,6 +25,9 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper bool enableOuterTOF = true; bool enableForwardTOF = true; bool enableBackwardTOF = true; + std::string detectorPattern = ""; + bool segmentedInnerTOF = false; // If the inner TOF layer is segmented + bool segmentedOuterTOF = false; // If the outer TOF layer is segmented O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx index 8c29127a5e7d6..f7d0eb135a27a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx @@ -23,11 +23,15 @@ std::string GeometryTGeo::sIOTOFVolumeName = "IOTOFV"; // Inner TOF std::string GeometryTGeo::sITOFLayerName = "ITOFLayer"; +std::string GeometryTGeo::sITOFStaveName = "ITOFStave"; +std::string GeometryTGeo::sITOFModuleName = "ITOFModule"; std::string GeometryTGeo::sITOFChipName = "ITOFChip"; std::string GeometryTGeo::sITOFSensorName = "ITOFSensor"; // Outer TOF std::string GeometryTGeo::sOTOFLayerName = "OTOFLayer"; +std::string GeometryTGeo::sOTOFStaveName = "OTOFStave"; +std::string GeometryTGeo::sOTOFModuleName = "OTOFModule"; std::string GeometryTGeo::sOTOFChipName = "OTOFChip"; std::string GeometryTGeo::sOTOFSensorName = "OTOFSensor"; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h index 1f3b2f4fe9fac..f3c4e3ddd6276 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Detector.h @@ -60,7 +60,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false); void configServices(); void createMaterials(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h index b7cc0a05c1c2e..29542810b8021 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h @@ -14,6 +14,8 @@ #include #include +#include +#include namespace o2 { @@ -23,7 +25,8 @@ class Layer { public: Layer() = default; - Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, bool isBarrel = true); + Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, + int layout = kBarrel, int nStaves = 0, float staveSize = 0.0, double staveTiltAngle = 0.0, int modulesPerStave = 0); ~Layer() = default; auto getInnerRadius() const { return mInnerRadius; } @@ -33,9 +36,14 @@ class Layer auto getx2X0() const { return mX2X0; } auto getChipThickness() const { return mChipThickness; } auto getName() const { return mLayerName; } - auto getIsBarrel() const { return mIsBarrel; } + auto getLayout() const { return mLayout; } + auto getSegments() const { return mStaves; } + static constexpr int kBarrel = 0; + static constexpr int kDisk = 1; + static constexpr int kBarrelSegmented = 2; + static constexpr int kDiskSegmented = 3; - virtual void createLayer(TGeoVolume* motherVolume){}; + virtual void createLayer(TGeoVolume* motherVolume) {}; protected: std::string mLayerName; @@ -45,7 +53,11 @@ class Layer float mZOffset{0.f}; // Of use when fwd layers float mX2X0; float mChipThickness; - bool mIsBarrel{true}; + int mLayout{kBarrel}; // Identifier of the type of layer layout (barrel, disk, barrel segmented, disk segmented) + // To be used only in case of the segmented layout, to define the number of staves in phi (for barrel) or in r (for disk) + std::pair mStaves{0, 0.0f}; // Number and size of staves in phi (for barrel) or in r (for disk) in case of segmented layout + int mModulesPerStave{0}; // Number of modules along a stave + double mTiltAngle{0.0}; // Tilt angle in degrees to be applied as a rotation around the local center of the stave }; class ITOFLayer : public Layer @@ -53,6 +65,7 @@ class ITOFLayer : public Layer public: using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; + static std::vector mRegister; }; class OTOFLayer : public Layer @@ -60,6 +73,7 @@ class OTOFLayer : public Layer public: using Layer::Layer; virtual void createLayer(TGeoVolume* motherVolume) override; + static std::vector mRegister; }; class FTOFLayer : public Layer diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index a2bba7cc5fe35..c056df5fd34ca 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -20,8 +20,6 @@ #include "IOTOFSimulation/Detector.h" #include "IOTOFBase/IOTOFBaseParam.h" -using o2::itsmft::Hit; - namespace o2 { namespace iotof @@ -40,7 +38,10 @@ Detector::Detector(bool active) mHits(o2::utils::createSimVector()) { auto& iotofPars = IOTOFBaseParam::Instance(); - configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, iotofPars.enableForwardTOF); + configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, + iotofPars.enableForwardTOF, iotofPars.enableBackwardTOF, + iotofPars.detectorPattern, + iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF); } Detector::~Detector() @@ -56,19 +57,63 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof) +void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented) { - if (itof) { - mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, 19.f, 0.f, 124.f, 0.f, 0.02f, true); // iTOF + + float radiusInnerTof = 19.f; + float radiusOuterTof = 85.f; + float lengthInnerTof = 124.f; + float lengthOuterTof = 680.f; + std::pair radiusRangeDiskTof = {15.f, 100.f}; + float zForwardTof = 370.f; + LOG(info) << "Configuring IOTOF layers with '" << pattern << "' pattern"; + if (pattern == "") { + LOG(info) << "Default pattern"; + } else if (pattern == "v3b") { + ftof = false; + btof = false; + } else if (pattern == "v3b1a") { + lengthOuterTof = 500.f; + zForwardTof = 270.f; + radiusRangeDiskTof = {30.f, 100.f}; + } else if (pattern == "v3b1b") { + lengthOuterTof = 500.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else if (pattern == "v3b2a") { + lengthOuterTof = 440.f; + zForwardTof = 270.f; + radiusRangeDiskTof = {30.f, 120.f}; + } else if (pattern == "v3b2b") { + lengthOuterTof = 440.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else if (pattern == "v3b3") { + lengthOuterTof = 580.f; + zForwardTof = 200.f; + radiusRangeDiskTof = {20.f, 68.f}; + } else { + LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting"; } - if (otof) { - mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, 85.f, 0.f, 680.f, 0.f, 0.02f, true); // oTOF + if (itof) { // iTOF + mITOFLayer = itofSegmented ? ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrelSegmented, + 24, 5.42, 10.0, 10) + : ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrel); + } + if (otof) { // oTOF + mOTOFLayer = otofSegmented ? OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrelSegmented, + 62, 9.74, 5.0, 54) + : OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrel); } if (ftof) { - mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, 15.f, 100.f, 0.f, 370.f, 0.02f, false); // fTOF + mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 0.02f, FTOFLayer::kDisk); // fTOF } if (btof) { - mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, 15.f, 100.f, 0.f, -370.f, 0.02f, false); // bTOF + mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, BTOFLayer::kDisk); // bTOF } } @@ -151,14 +196,18 @@ void Detector::defineSensitiveVolumes() // The names of the IOTOF sensitive volumes have the format: IOTOFLayer(0...mLayers.size()-1) auto& iotofPars = IOTOFBaseParam::Instance(); if (iotofPars.enableInnerTOF) { - v = geoManager->GetVolume(GeometryTGeo::getITOFSensorPattern()); - LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + for (const std::string& itofSensor : ITOFLayer::mRegister) { + v = geoManager->GetVolume(itofSensor.c_str()); + LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + } } if (iotofPars.enableOuterTOF) { - v = geoManager->GetVolume(GeometryTGeo::getOTOFSensorPattern()); - LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); - AddSensitiveVolume(v); + for (const std::string& otofSensor : OTOFLayer::mRegister) { + v = geoManager->GetVolume(otofSensor.c_str()); + LOGP(info, "Adding IOTOF Sensitive Volume {}", v->GetName()); + AddSensitiveVolume(v); + } } if (iotofPars.enableForwardTOF) { v = geoManager->GetVolume(GeometryTGeo::getFTOFSensorPattern()); @@ -214,28 +263,28 @@ bool Detector::ProcessHits(FairVolume* vol) bool startHit = false, stopHit = false; unsigned char status = 0; if (fMC->IsTrackEntering()) { - status |= Hit::kTrackEntering; + status |= o2::itsmft::Hit::kTrackEntering; } if (fMC->IsTrackInside()) { - status |= Hit::kTrackInside; + status |= o2::itsmft::Hit::kTrackInside; } if (fMC->IsTrackExiting()) { - status |= Hit::kTrackExiting; + status |= o2::itsmft::Hit::kTrackExiting; } if (fMC->IsTrackOut()) { - status |= Hit::kTrackOut; + status |= o2::itsmft::Hit::kTrackOut; } if (fMC->IsTrackStop()) { - status |= Hit::kTrackStopped; + status |= o2::itsmft::Hit::kTrackStopped; } if (fMC->IsTrackAlive()) { - status |= Hit::kTrackAlive; + status |= o2::itsmft::Hit::kTrackAlive; } // track is entering or created in the volume - if ((status & Hit::kTrackEntering) || (status & Hit::kTrackInside && !mTrackData.mHitStarted)) { + if ((status & o2::itsmft::Hit::kTrackEntering) || (status & o2::itsmft::Hit::kTrackInside && !mTrackData.mHitStarted)) { startHit = true; - } else if ((status & (Hit::kTrackExiting | Hit::kTrackOut | Hit::kTrackStopped))) { + } else if ((status & (o2::itsmft::Hit::kTrackExiting | o2::itsmft::Hit::kTrackOut | o2::itsmft::Hit::kTrackStopped))) { stopHit = true; } @@ -264,9 +313,9 @@ bool Detector::ProcessHits(FairVolume* vol) fMC->CurrentVolOffID(3, halfstave); fMC->CurrentVolOffID(4, stave); - Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), - mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), - mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); + o2::itsmft::Hit* p = addHit(stack->GetCurrentTrackNumber(), lay, mTrackData.mPositionStart.Vect(), positionStop.Vect(), + mTrackData.mMomentumStart.Vect(), mTrackData.mMomentumStart.E(), positionStop.T(), + mTrackData.mEnergyLoss, mTrackData.mTrkStatusStart, status); // RS: not sure this is needed // Increment number of Detector det points in TParticle diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 53c07d1fa4978..32a24fc46f94c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -14,84 +14,369 @@ #include "Framework/Logger.h" +#include +#include #include #include +#include + +#include +#include namespace o2 { namespace iotof { -Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, bool isBarrel) - : mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mZLength(zLength), mZOffset(zOffset), mX2X0(layerX2X0), mIsBarrel(isBarrel) +Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, + int layout, int nStaves, float staveSize, double staveTiltAngle, int modulesPerStave) + : mLayerName(layerName), + mInnerRadius(rInn), + mOuterRadius(rOut), + mZLength(zLength), + mZOffset(zOffset), + mX2X0(layerX2X0), + mLayout(layout), + mStaves(nStaves, staveSize), + mModulesPerStave(modulesPerStave), + mTiltAngle(staveTiltAngle) { float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; - if (isBarrel) { - mOuterRadius = mInnerRadius + mChipThickness; - } else { - mZLength = mChipThickness; + std::string name = ""; + switch (layout) { + case kBarrel: + case kBarrelSegmented: + name = "barrel"; + mOuterRadius = mInnerRadius + mChipThickness; + break; + case kDisk: + case kDiskSegmented: + name = "forward"; + mZLength = mChipThickness; + break; + default: + LOG(fatal) << "Invalid layout " << layout; + } + // Sanity checks + if (mInnerRadius > mOuterRadius) { + LOG(fatal) << "Invalid layer dimensions: rInner " << mInnerRadius << " cm is larger than rOuter " << mOuterRadius << " cm"; + } + if ((mStaves.first != 0 || mStaves.second != 0.0f) && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mStaves.first << " is set for non-segmented layout " << layout; + } + if ((mStaves.first <= 1 || mStaves.second <= 0.0f) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mStaves.first << " must be positive for segmented layout " << layout; + } + if (mModulesPerStave <= 0 && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of sensors per segment " << mModulesPerStave << " must be positive for segmented layout " << layout; + } + if (std::abs(mTiltAngle) > 0.1 && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is set for non-segmented layout " << layout; + } + if ((mTiltAngle < 0.0 || mTiltAngle > 90.0) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is too large, it must be between 0 and 90 degrees"; } - LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", isBarrel ? std::string("barrel") : std::string("forward"), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); + + LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", name.c_str(), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); } -void ITOFLayer::createLayer(TGeoVolume* motherVolume) +void setLayerStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed - 7); + obj->SetFillColor(kRed - 7); + obj->SetLineWidth(1); + obj->SetTransparency(70); +} +void setStaveStyle(TGeoVolume* obj) { - std::string chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(), - sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); + obj->SetLineColor(kRed - 5); + obj->SetFillColor(kRed - 9); + obj->SetLineWidth(2); + obj->SetTransparency(45); +} +void setModuleStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed - 3); + obj->SetFillColor(kRed - 8); + obj->SetLineWidth(2); + obj->SetTransparency(35); +} +void setChipStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kOrange); + obj->SetFillColor(kOrange - 9); + obj->SetLineWidth(3); + obj->SetTransparency(15); +} +void setSensorStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed); + obj->SetFillColor(kRed - 9); + obj->SetLineWidth(3); + obj->SetTransparency(5); +} - TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); +std::vector ITOFLayer::mRegister; +void ITOFLayer::createLayer(TGeoVolume* motherVolume) +{ + const char* chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(); + const char* sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); + const char* moduleName = o2::iotof::GeometryTGeo::getITOFModulePattern(); + const char* staveName = o2::iotof::GeometryTGeo::getITOFStavePattern(); TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); - LOGP(info, "Media: {} {}", (void*)medSi, (void*)medAir); - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); - layerVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, nullptr); + switch (mLayout) { + case kBarrel: { + TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); + + LOGP(info, "Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); + ITOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + LOGP(info, "Inserting Barrel {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + case kBarrelSegmented: { + // First we create the volume for the whole layer, which will be used as mother volume for the segments + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + setLayerStyle(layerVol); + + // Now we create the volume for a single stave + TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); + TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); + setStaveStyle(staveVol); + + // Now we create the volume for a single module (sensor + chip) + const int modulesPerStaveX = 1; // we assume that each stave is divided in 2 modules along the x direction + const double moduleSizeX = staveSizeX / modulesPerStaveX; // cm + const double moduleSizeY = staveSizeY; // cm + const double moduleSizeZ = staveSizeZ / mModulesPerStave; // cm + TGeoBBox* module = new TGeoBBox(moduleSizeX * 0.5, moduleSizeY * 0.5, moduleSizeZ * 0.5); + TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir); + setModuleStyle(moduleVol); + + // Now we create the volume of the chip, which is the same for all modules + const int chipsPerModuleX = 2; // we assume that each module is divided in 2 chips along the x direction + const int chipsPerModuleZ = 2; // we assume that each module is divided in 2 chips along the z direction + const double chipSizeX = moduleSizeX / chipsPerModuleX; // cm + const double chipSizeY = moduleSizeY; // cm + const double chipSizeZ = moduleSizeZ / chipsPerModuleZ; // cm + TGeoBBox* chip = new TGeoBBox(chipSizeX * 0.5, chipSizeY * 0.5, chipSizeZ * 0.5); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); + setChipStyle(chipVol); + + // Finally we create the volume of the sensor, which is the same for all chips + const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction + const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction + const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm + const double sensorSizeY = chipSizeY; // cm + const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm + TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); + setSensorStyle(sensVol); + ITOFLayer::mRegister.push_back(sensVol->GetName()); + + // Now we build a chip from sensors + for (int i = 0; i < sensorsPerChipX; ++i) { + for (int j = 0; j < sensorsPerChipZ; ++j) { + LOGP(info, "iTOF: Creating sensor {}/{} for chip {}/{}", i + 1, sensorsPerChipX, j + 1, sensorsPerChipZ); + auto* translation = new TGeoTranslation((i + 0.5) * sensorSizeX - 0.5 * chipSizeX, + 0, + (j + 0.5) * sensorSizeZ - 0.5 * chipSizeZ); + chipVol->AddNode(sensVol, 1 + i * sensorsPerChipZ + j, translation); + } + } + + // Now we build a module from chips + for (int i = 0; i < chipsPerModuleX; ++i) { + for (int j = 0; j < chipsPerModuleZ; ++j) { + LOGP(info, "iTOF: Creating chip {}/{} for module {}/{}", i + 1, chipsPerModuleX, j + 1, chipsPerModuleZ); + auto* translation = new TGeoTranslation((i + 0.5) * chipSizeX - 0.5 * moduleSizeX, 0, (j + 0.5) * chipSizeZ - 0.5 * moduleSizeZ); + moduleVol->AddNode(chipVol, 1 + i * chipsPerModuleZ + j, translation); + } + } + + // Now we build a stave from modules + for (int i = 0; i < modulesPerStaveX; ++i) { + for (int j = 0; j < mModulesPerStave; ++j) { + LOGP(info, "iTOF: Creating module {}/{} for stave {}/{}", i + 1, modulesPerStaveX, j + 1, mModulesPerStave); + auto* translation = new TGeoTranslation((i + 0.5) * moduleSizeX - 0.5 * staveSizeX, 0, (j + 0.5) * moduleSizeZ - 0.5 * staveSizeZ); + staveVol->AddNode(moduleVol, 1 + i * mModulesPerStave + j, translation); + } + } + + // We finally put all the staves in the layer + for (int i = 0; i < mStaves.first; ++i) { + LOGP(info, "iTOF: Creating stave {}/{} for layer {}", i + 1, mStaves.first, layerVol->GetName()); + const double phi = TMath::TwoPi() * i / mStaves.first; + const double x = avgRadius * TMath::Cos(phi); + const double y = avgRadius * TMath::Sin(phi); + auto* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + 90 + mTiltAngle, 0, 0); + auto* transformation = new TGeoCombiTrans(x, y, 0, rotation); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(staveVol, 1 + i, transformation); + } + LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + default: + LOG(fatal) << "Invalid layout " << mLayout; + } } +std::vector OTOFLayer::mRegister; void OTOFLayer::createLayer(TGeoVolume* motherVolume) { - std::string chipName = o2::iotof::GeometryTGeo::getOTOFChipPattern(), - sensName = o2::iotof::GeometryTGeo::getOTOFSensorPattern(); - - TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + const char* chipName = o2::iotof::GeometryTGeo::getOTOFChipPattern(); + const char* sensName = o2::iotof::GeometryTGeo::getOTOFSensorPattern(); + const char* moduleName = o2::iotof::GeometryTGeo::getOTOFModulePattern(); + const char* staveName = o2::iotof::GeometryTGeo::getOTOFStavePattern(); TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); + LOGP(info, "Media: {} {}", (void*)medSi, (void*)medAir); - TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); - TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); - TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); - - LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); - layerVol->AddNode(chipVol, 1, nullptr); - - LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); - motherVolume->AddNode(layerVol, 1, nullptr); + switch (mLayout) { + case kBarrel: { + TGeoTube* sensor = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* chip = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); + + LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + OTOFLayer::mRegister.push_back(sensVol->GetName()); + chipVol->AddNode(sensVol, 1, nullptr); + + LOGP(info, "Inserting {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(chipVol, 1, nullptr); + + LOGP(info, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + case kBarrelSegmented: { + // First we create the volume for the whole layer, which will be used as mother volume for the segments + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + setLayerStyle(layerVol); + + // Now we create the volume for a single stave + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); + TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); + setStaveStyle(staveVol); + + // Now we create the volume for a single module (sensor + chip) + const int modulesPerStaveX = 1; // we assume that each stave is divided in 2 modules along the x direction + const double moduleSizeX = staveSizeX / modulesPerStaveX; // cm + const double moduleSizeY = staveSizeY; // cm + const double moduleSizeZ = staveSizeZ / mModulesPerStave; // cm + TGeoBBox* module = new TGeoBBox(moduleSizeX * 0.5, moduleSizeY * 0.5, moduleSizeZ * 0.5); + TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir); + setModuleStyle(moduleVol); + + // Now we create the volume of the chip, which is the same for all modules + const int chipsPerModuleX = 2; // we assume that each module is divided in 2 chips along the x direction + const int chipsPerModuleZ = 2; // we assume that each module is divided in 2 chips along the z direction + const double chipSizeX = moduleSizeX / chipsPerModuleX; // cm + const double chipSizeY = moduleSizeY; // cm + const double chipSizeZ = moduleSizeZ / chipsPerModuleZ; // cm + TGeoBBox* chip = new TGeoBBox(chipSizeX * 0.5, chipSizeY * 0.5, chipSizeZ * 0.5); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); + setChipStyle(chipVol); + + // Finally we create the volume of the sensor, which is the same for all chips + const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction + const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction + const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm + const double sensorSizeY = chipSizeY; // cm + const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm + TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); + setSensorStyle(sensVol); + OTOFLayer::mRegister.push_back(sensVol->GetName()); + + // Now we build a chip from sensors + for (int i = 0; i < sensorsPerChipX; ++i) { + for (int j = 0; j < sensorsPerChipZ; ++j) { + LOGP(info, "oTOF: Creating sensor {}/{} for chip {}/{}", i + 1, sensorsPerChipX, j + 1, sensorsPerChipZ); + auto* translation = new TGeoTranslation((i + 0.5) * sensorSizeX - 0.5 * chipSizeX, + 0, + (j + 0.5) * sensorSizeZ - 0.5 * chipSizeZ); + chipVol->AddNode(sensVol, 1 + i * sensorsPerChipZ + j, translation); + } + } + + // Now we build a module from chips + for (int i = 0; i < chipsPerModuleX; ++i) { + for (int j = 0; j < chipsPerModuleZ; ++j) { + LOGP(info, "oTOF: Creating chip {}/{} for module {}/{}", i + 1, chipsPerModuleX, j + 1, chipsPerModuleZ); + auto* translation = new TGeoTranslation((i + 0.5) * chipSizeX - 0.5 * moduleSizeX, 0, (j + 0.5) * chipSizeZ - 0.5 * moduleSizeZ); + moduleVol->AddNode(chipVol, 1 + i * chipsPerModuleZ + j, translation); + } + } + + // Now we build a stave from modules + for (int i = 0; i < modulesPerStaveX; ++i) { + for (int j = 0; j < mModulesPerStave; ++j) { + LOGP(info, "oTOF: Creating module {}/{} for stave {}/{}", i + 1, modulesPerStaveX, j + 1, mModulesPerStave); + auto* translation = new TGeoTranslation((i + 0.5) * moduleSizeX - 0.5 * staveSizeX, 0, (j + 0.5) * moduleSizeZ - 0.5 * staveSizeZ); + staveVol->AddNode(moduleVol, 1 + i * mModulesPerStave + j, translation); + } + } + + // We finally put all the staves in the layer + for (int i = 0; i < mStaves.first; ++i) { + LOGP(info, "oTOF: Creating stave {}/{} for layer {}", i + 1, mStaves.first, layerVol->GetName()); + const double phi = TMath::TwoPi() * i / mStaves.first; + const double x = avgRadius * TMath::Cos(phi); + const double y = avgRadius * TMath::Sin(phi); + auto* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + 90 + mTiltAngle, 0, 0); + auto* transformation = new TGeoCombiTrans(x, y, 0, rotation); + + LOGP(info, "Inserting Barrel {} in {} ", chipVol->GetName(), layerVol->GetName()); + layerVol->AddNode(staveVol, 1 + i, transformation); + } + LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); + motherVolume->AddNode(layerVol, 1, nullptr); + return; + } + default: + LOG(fatal) << "Invalid layout " << mLayout; + } } void FTOFLayer::createLayer(TGeoVolume* motherVolume) @@ -109,9 +394,9 @@ void FTOFLayer::createLayer(TGeoVolume* motherVolume) TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); chipVol->AddNode(sensVol, 1, nullptr); @@ -141,9 +426,9 @@ void BTOFLayer::createLayer(TGeoVolume* motherVolume) TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); chipVol->AddNode(sensVol, 1, nullptr); diff --git a/Detectors/Upgrades/ALICE3/README.md b/Detectors/Upgrades/ALICE3/README.md index 7e1e1c03718d8..23d45232b71c9 100644 --- a/Detectors/Upgrades/ALICE3/README.md +++ b/Detectors/Upgrades/ALICE3/README.md @@ -21,7 +21,7 @@ The specific modules for Run 5 are enabled by passing their their IDs to the `-m A list of the available DetIDs is reproted in the table below: | Detector ID | Detector description | -|-------------|----------------------------------| +| ----------- | -------------------------------- | | `A3IP` | Beam pipe | | `TRK` | Barrel Tracker | | `TF3` | Time Of Flight detectors | @@ -49,7 +49,7 @@ export ALICE3_MAGFIELD_MACRO=../ALICE3Field.C An exampling macro for a custom magnetic field is stored in `Detectors/Upgrades/macros/ALICE3Field.C`. -### Run a simple simulation for run 5 +### Run a simple simulation for ALICE 3 The simplest command to be run to test the simulation is working is: ```bash @@ -61,13 +61,20 @@ To enable a specific set of modules, e.g. the beampipe and the TOFs one can spec ```bash o2-sim-run5 -n 10 -m A3IP TF3 ``` + +#### Specific detector setups + +Configurables for various sub-detectors are presented in the following Table: + +| Available options | Link to options | +| ----------------- | -------------------------------------------------------------- | +| TKR | [Link to TRK options](./TRK/README.md#specific-detector-setup) | + ### Output of the simulation The simulation will produce a `o2sim_Hits.root` file with a tree with the hits related to that detector. -Currently, hits are produced for: `TRK`, `FT3`, and `TF3`. -More detectors will be included. ## Reconstruction WIP ## Analysis -WIP \ No newline at end of file +WIP diff --git a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt index e623239122658..6e3437c9d841b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(base) add_subdirectory(macros) add_subdirectory(simulation) -add_subdirectory(workflow) \ No newline at end of file +add_subdirectory(reconstruction) +add_subdirectory(workflow) diff --git a/Detectors/Upgrades/ALICE3/TRK/README.md b/Detectors/Upgrades/ALICE3/TRK/README.md index 44937fb3663a6..8b3a7984bb233 100644 --- a/Detectors/Upgrades/ALICE3/TRK/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/README.md @@ -6,5 +6,23 @@ This is top page for the TRK detector documentation. + +## Specific detector setup + + +Configurables for various sub-detectors are presented in the following Table: + +| Subsystem | Available options | Comments | +| ------------------ | ------------------------------------------------------- | ---------------------------------------------------------------- | +| `TRKBase.layoutVD` | `kIRIS4` (default), `kIRISFullCyl`, `kIRIS5`, `kIRIS4a` | [link to definitions](./base/include/TRKBase/TRKBaseParam.h) | +| `TRKBase.layoutML` | `kCylinder`, `kTurboStaves` (default), `kStaggered` | | +| `TRKBase.layoutOT` | `kCylinder`, `kTurboStaves`, `kStaggered` (default) | | + +For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by +```bash +o2-sim-serial-run5 -n 1 -g pythia8hi -m A3IP TRK FT3 TF3 \ + --configKeyValues "TRKBase.layoutVD=kIRISFullCyl;TRKBase.layoutML=kCylinder;TRKBase.layoutOL=kCylinder" +``` + \ No newline at end of file +/doxy --> diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index deec53950cd5f..bb1597f2967e4 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -223,7 +223,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache std::vector mCacheRefAlphaMLOT; /// cache for sensor ref alpha ML and OT eLayout mLayoutML; // Type of segmentation for the middle layers - eLayout mLayoutOL; // Type of segmentation for the outer layers + eLayout mLayoutOT; // Type of segmentation for the outer layers private: static std::unique_ptr sInstance; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 7f2f7f32b79d9..f919839b7ed0a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -31,6 +31,13 @@ enum eLayout { kStaggered, }; +enum eVDLayout { + kIRIS4 = 0, + kIRISFullCyl, + kIRIS5, + kIRIS4a, +}; + struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; float serviceTubeX0 = 0.02f; // X0 Al2O3 @@ -39,10 +46,12 @@ struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { eOverallGeom overallGeom = kDefaultRadii; // Overall geometry option, to be used in Detector::buildTRKMiddleOuterLayers eLayout layoutML = kTurboStaves; // Type of segmentation for the middle layers - eLayout layoutOL = kStaggered; // Type of segmentation for the outer layers + eLayout layoutOT = kStaggered; // Type of segmentation for the outer layers + eVDLayout layoutVD = kIRIS4; // VD detector layout design eLayout getLayoutML() const { return layoutML; } - eLayout getLayoutOL() const { return layoutOL; } + eLayout getLayoutOT() const { return layoutOT; } + eVDLayout getLayoutVD() const { return layoutVD; } O2ParamDef(TRKBaseParam, "TRKBase"); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index b32c89164f18a..b5535af781910 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -77,9 +77,9 @@ void GeometryTGeo::Build(int loadTrans) } mLayoutML = o2::trk::TRKBaseParam::Instance().getLayoutML(); - mLayoutOL = o2::trk::TRKBaseParam::Instance().getLayoutOL(); + mLayoutOT = o2::trk::TRKBaseParam::Instance().getLayoutOT(); - LOG(debug) << "Layout ML: " << mLayoutML << ", Layout OL: " << mLayoutOL; + LOG(debug) << "Layout ML: " << mLayoutML << ", Layout OL: " << mLayoutOT; mNumberOfLayersMLOT = extractNumberOfLayersMLOT(); mNumberOfPetalsVD = extractNumberOfPetalsVD(); @@ -102,7 +102,6 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndexMLOT.resize(mNumberOfLayersMLOT); /// ML and OT are part of TRK as the same detector, without disks for (int i = 0; i < mNumberOfLayersMLOT; i++) { - std::cout << "Layer MLOT: " << i << std::endl; mNumberOfStaves[i] = extractNumberOfStavesMLOT(i); mNumberOfHalfStaves[i] = extractNumberOfHalfStavesMLOT(i); mNumberOfModules[i] = extractNumberOfModulesMLOT(i); @@ -406,7 +405,7 @@ TString GeometryTGeo::getMatrixPath(int index) const // handling cylindrical configuration for ML and/or OT // needed bercause of the different numbering scheme in the geometry for the cylindrical case wrt the staggered and turbo ones if (subDetID == 1) { - if ((layer < 4 && mLayoutML == eLayout::kCylinder) || (layer > 3 && mLayoutOL == eLayout::kCylinder)) { + if ((layer < 4 && mLayoutML == eLayout::kCylinder) || (layer > 3 && mLayoutOT == eLayout::kCylinder)) { stave = 1; mod = 1; chip = 1; @@ -416,15 +415,15 @@ TString GeometryTGeo::getMatrixPath(int index) const // build the path if (subDetID == 0) { // VD if (disk >= 0) { - path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n - path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk); // PETALCASEx_DISKy_1 - // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKChipPattern(), disk); // PETALCASEx_DISKy_TRKChipy_1 + path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n + path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk); // PETALCASEx_DISKy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKChipPattern(), disk); // PETALCASEx_DISKy_TRKChipy_1 path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKSensorPattern(), disk); // PETALCASEx_DISKy_TRKSensory_1 } else if (layer >= 0) { path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer); // PETALCASEx_LAYERy_1 // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKStavePattern(), layer); // PETALCASEx_LAYERy_TRKStavey_1 - // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKChipPattern(), layer); // PETALCASEx_LAYERy_TRKChipy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKChipPattern(), layer); // PETALCASEx_LAYERy_TRKChipy_1 path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKSensorPattern(), layer); // PETALCASEx_LAYERy_TRKSensory_1 } } else if (subDetID == 1) { // MLOT @@ -962,9 +961,9 @@ int GeometryTGeo::extractNumberOfChipsPerPetalVD() const for (int i = 0; i < subNodes->GetEntriesFast(); i++) { auto* subNode = dynamic_cast(subNodes->At(i)); - if (strstr(subNode->GetName(), getTRKSensorPattern()) != nullptr) { + if (strstr(subNode->GetName(), getTRKChipPattern()) != nullptr) { numberOfChips++; - LOGP(debug, "Found sensor in {}: {}", nodeName, subNode->GetName()); + LOGP(debug, "Found chip in {}: {}", nodeName, subNode->GetName()); } } } diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index 379207eb07481..d9908bbfeb1e5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -18,4 +18,14 @@ o2_add_test_root_macro(CheckDigits.C O2::SimulationDataFormat O2::DetectorsBase O2::Steer + LABELS trk COMPILE_ONLY) + +o2_add_test_root_macro(CheckTracksCA.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::ITStracking + O2::SimulationDataFormat + O2::DetectorsBase + O2::TRKBase + O2::TRKSimulation + O2::Steer LABELS trk COMPILE_ONLY) \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C new file mode 100644 index 0000000000000..ae75616b7719c --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C @@ -0,0 +1,345 @@ +// 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 CheckTracksCA.C +/// \brief Quality assurance macro for TRK tracking + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTrack.h" +#include "Steer/MCKinematicsReader.h" +#include "TRKSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "DetectorsBase/GeometryManager.h" + +#endif + +using namespace std; +using namespace o2; + +/// Structure to track particle hit information +struct ParticleHitInfo { + std::bitset<11> layerHits; ///< Which layers have hits (11 layers for TRK) + int nHits = 0; ///< Total number of hits + float pt = 0.0f; ///< Particle pT + + void addHit(int layer) + { + if (!layerHits[layer]) { + layerHits[layer] = true; + nHits++; + } + } + + bool hasConsecutiveLayers(int nConsecutive) const + { + for (int startLayer = 0; startLayer <= 11 - nConsecutive; ++startLayer) { + bool allSet = true; + for (int i = 0; i < nConsecutive; ++i) { + if (!layerHits[startLayer + i]) { + allSet = false; + break; + } + } + if (allSet) { + return true; + } + } + return false; + } +}; + +void CheckTracksCA(std::string tracfile = "o2trac_trk.root", + std::string kinefile = "o2sim_Kine.root", + std::string hitsfile = "o2sim_HitsTRK.root", + std::string outputfile = "trk_qa_output.root") +{ + gStyle->SetOptStat(0); + + std::cout << "=== Starting TRK Track Quality Assurance ===" << std::endl; + std::cout << "Input files:" << std::endl; + std::cout << " Tracks: " << tracfile << std::endl; + std::cout << " Kinematics: " << kinefile << std::endl; + std::cout << " Hits: " << hitsfile << std::endl; + std::cout << " Output: " << outputfile << std::endl; + std::cout << std::endl; + + // MC kinematics reader + o2::steer::MCKinematicsReader kineReader("o2sim", o2::steer::MCKinematicsReader::Mode::kMCKine); + const int nEvents = kineReader.getNEvents(0); + std::cout << "Number of MC events: " << nEvents << std::endl; + + // Open hits file to count hits per particle per layer + TFile* hitsFile = TFile::Open(hitsfile.c_str(), "READ"); + if (!hitsFile || hitsFile->IsZombie()) { + std::cerr << "ERROR: Cannot open hits file: " << hitsfile << std::endl; + return; + } + TTree* hitsTree = hitsFile->Get("o2sim"); + if (!hitsTree) { + std::cerr << "ERROR: Cannot find o2sim tree in hits file" << std::endl; + return; + } + + // Open reconstructed tracks file + TFile* tracFile = TFile::Open(tracfile.c_str(), "READ"); + if (!tracFile || tracFile->IsZombie()) { + std::cerr << "ERROR: Cannot open tracks file: " << tracfile << std::endl; + return; + } + TTree* recTree = tracFile->Get("o2sim"); + if (!recTree) { + std::cerr << "ERROR: Cannot find o2sim tree in tracks file" << std::endl; + return; + } + + // Reconstructed tracks and labels + std::vector* recTracks = nullptr; + std::vector* trkLabels = nullptr; + recTree->SetBranchAddress("TRKTrack", &recTracks); + recTree->SetBranchAddress("TRKTrackMCTruth", &trkLabels); + + std::cout << "Reading tracks from tree..." << std::endl; + + // Analyze hits tree to count hits per particle per layer + std::cout << "Analyzing hits from tree..." << std::endl; + std::unordered_map particleHitMap; + + // Load geometry for layer determination + o2::base::GeometryManager::loadGeometry(); + auto* gman = o2::trk::GeometryTGeo::Instance(); + + // Array to map detector to starting layer + constexpr std::array startLayer{0, 3}; + + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + Long64_t nHitsEntries = hitsTree->GetEntries(); + std::cout << "Processing " << nHitsEntries << " hit entries..." << std::endl; + + for (Long64_t iEntry = 0; iEntry < nHitsEntries; ++iEntry) { + hitsTree->GetEntry(iEntry); + + for (const auto& hit : *trkHit) { + // Skip disk hits (only barrel) + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; + } + + // Determine layer + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + + // Create label for this particle + o2::MCCompLabel label(hit.GetTrackID(), static_cast(iEntry), 0); + + // Add hit to particle's hit map + particleHitMap[label].addHit(layer); + } + } + + std::cout << "Found " << particleHitMap.size() << " unique particles with hits" << std::endl; + + // Store particle info and fill generated histograms + std::unordered_map particlePtMap; + + // Create histograms + constexpr int nLayers = 11; + constexpr int nb = 100; + double xbins[nb + 1], ptcutl = 0.05, ptcuth = 10.; + double a = std::log(ptcuth / ptcutl) / nb; + for (int i = 0; i <= nb; i++) + xbins[i] = ptcutl * std::exp(i * a); + + TH1D genParticlePtHist("genParticlePt", "Generated Particle p_{T} (All Layers); #it{p}_{T} (GeV/#it{c}); Counts", nb, xbins); + TH1D genParticlePt7LayersHist("genParticlePt7Layers", "Generated Particle p_{T} with hits in at least 7 consecutive layers; #it{p}_{T} (GeV/#it{c}); Counts", nb, xbins); + TH1D goodTracks("goodTracks", "Good Tracks; p_{T} (GeV/c); Counts", nb, xbins); + TH1D fakeTracks("fakeTracks", "Fake Tracks; p_{T} (GeV/c); Counts", nb, xbins); + + std::array goodTracksMatching, fakeTracksMatching; + for (int i = 0; i < 5; ++i) { + goodTracksMatching[i] = TH1D(Form("goodTracksMatching_%dLayers", i + 7), + Form("Good Tracks with %d layer hits; p_{T} (GeV/c); Counts", i + 7), + nb, xbins); + fakeTracksMatching[i] = TH1D(Form("fakeTracksMatching_%dLayers", i + 7), + Form("Fake Tracks with %d layer hits; p_{T} (GeV/c); Counts", i + 7), + nb, xbins); + } + + TH1D numberOfClustersPerTrack("numberOfClustersPerTrack", + "Number of clusters per track; N_{clusters}; Counts", + 12, -0.5, 11.5); + + // First pass: identify particles with full hit coverage from kinematics + std::cout << "Analyzing MC particles..." << std::endl; + for (int iEvent = 0; iEvent < nEvents; ++iEvent) { + const auto& mcTracks = kineReader.getTracks(iEvent); + for (size_t iTrack = 0; iTrack < mcTracks.size(); ++iTrack) { + const auto& mcTrack = mcTracks[iTrack]; + if (!mcTrack.isPrimary()) { + continue; + } + + // Create label for this particle + o2::MCCompLabel label(iTrack, iEvent, 0); + float pt = mcTrack.GetPt(); + + // Store particle info + particlePtMap[label] = pt; + + // Check if this particle has hits + auto hitIt = particleHitMap.find(label); + if (hitIt != particleHitMap.end()) { + // Store pT in hit info + hitIt->second.pt = pt; + + // Fill histogram for particles with hits in all 11 layers + if (hitIt->second.nHits == 11) { + genParticlePtHist.Fill(pt); + } + + // Fill histogram for particles with at least 7 consecutive layer hits + if (hitIt->second.hasConsecutiveLayers(7)) { + genParticlePt7LayersHist.Fill(pt); + } + } + } + } + + std::cout << "Generated particles with 11 hits: " << genParticlePtHist.GetEntries() << std::endl; + std::cout << "Generated particles with 7+ consecutive hits: " << genParticlePt7LayersHist.GetEntries() << std::endl; + + // Second pass: analyze reconstructed tracks + std::cout << "Analyzing reconstructed tracks..." << std::endl; + int nROFs = recTree->GetEntries(); + int totalTracks = 0; + int goodTracksCount = 0; + int fakeTracksCount = 0; + + for (int iROF = 0; iROF < nROFs; ++iROF) { + recTree->GetEntry(iROF); + + if (!recTracks || !trkLabels) { + continue; + } + + totalTracks += recTracks->size(); + + for (size_t iTrack = 0; iTrack < recTracks->size(); ++iTrack) { + const auto& track = recTracks->at(iTrack); + const auto& label = trkLabels->at(iTrack); + + if (!label.isSet() || !label.isValid()) { + continue; + } + + int eventID = label.getEventID(); + int trackID = label.getTrackID(); + int nClusters = track.getNumberOfClusters(); + + // Get MC track info + if (eventID < 0 || eventID >= nEvents) { + continue; + } + + const auto& mcTracks = kineReader.getTracks(eventID); + if (trackID < 0 || trackID >= (int)mcTracks.size()) { + continue; + } + + float pt = mcTracks[trackID].GetPt(); + + // Fill histograms + numberOfClustersPerTrack.Fill(nClusters); + + auto key = o2::MCCompLabel(trackID, eventID, 0); + if (particleHitMap.find(key) != particleHitMap.end() && particleHitMap[key].hasConsecutiveLayers(11)) { + if (label.isFake()) { + fakeTracks.Fill(pt); + fakeTracksCount++; + if (nClusters >= 7 && nClusters <= 11) { + fakeTracksMatching[nClusters - 7].Fill(pt); + } + } else { + goodTracks.Fill(pt); + goodTracksCount++; + if (nClusters >= 7 && nClusters <= 11) { + goodTracksMatching[nClusters - 7].Fill(pt); + } + } + } + } + } + + // Create efficiency histograms + std::cout << "Computing efficiencies..." << std::endl; + + std::array efficiencyHistograms; + THStack* efficiencyStack = new THStack("efficiencyStack", + "Tracking Efficiency; #it{p}_{T} (GeV/#it{c}); Efficiency"); + + int colors[5] = {kRed, kBlue, kGreen + 2, kMagenta, kOrange}; + for (int i = 0; i < 5; ++i) { + int nClusters = i + 7; + efficiencyHistograms[i] = TH1D(Form("efficiency_%dClusters", nClusters), + Form("Efficiency for %d cluster tracks; #it{p}_{T} (GeV/#it{c}); Efficiency", nClusters), + nb, xbins); + + efficiencyHistograms[i].Divide(&goodTracksMatching[i], &genParticlePtHist, 1, 1, "B"); + + efficiencyHistograms[i].SetLineColor(colors[i]); + efficiencyHistograms[i].SetFillColor(colors[i]); + efficiencyHistograms[i].SetLineWidth(2); + efficiencyHistograms[i].SetMarkerColor(colors[i]); + efficiencyHistograms[i].SetMarkerStyle(20 + i); + efficiencyStack->Add(&efficiencyHistograms[i]); + } + + // Write output + std::cout << "Writing output to " << outputfile << std::endl; + TFile outFile(outputfile.c_str(), "RECREATE"); + genParticlePtHist.Write(); + goodTracks.Write(); + fakeTracks.Write(); + for (int i = 0; i < 5; ++i) { + goodTracksMatching[i].Write(); + fakeTracksMatching[i].Write(); + efficiencyHistograms[i].Write(); + } + efficiencyStack->Write(); + genParticlePt7LayersHist.Write(); + numberOfClustersPerTrack.Write(); + outFile.Close(); + + // Clean up + hitsFile->Close(); + tracFile->Close(); + delete efficiencyStack; + delete hitsFile; + delete tracFile; +} diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh index 797d1d12af4ab..d0953a342af04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh @@ -2,7 +2,7 @@ nEvents=10 # Simulating -o2-sim-serial-run5 -n $nEvents -g pythia8hi -m TRK --configKeyValues "TRKBase.layoutML=kTurboStaves;TRKBase.layoutOL=kStaggered;">& sim_TRK.log +o2-sim-serial-run5 -n $nEvents -g pythia8hi -m TRK --configKeyValues "TRKBase.layoutML=kTurboStaves;TRKBase.layoutOT=kStaggered;">& sim_TRK.log # Digitizing o2-sim-digitizer-workflow -b >& digiTRK.log diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt new file mode 100644 index 0000000000000..01ddc783d192b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -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. + +o2_add_library(TRKReconstruction + TARGETVARNAME targetName + SOURCES src/TimeFrame.cxx + PUBLIC_LINK_LIBRARIES + O2::ITStracking + O2::GPUCommon + Microsoft.GSL::GSL + O2::CommonConstants + O2::DataFormatsITSMFT + O2::SimulationDataFormat + O2::ITSBase + O2::ITSReconstruction + O2::ITSMFTReconstruction + O2::DataFormatsITS + O2::TRKSimulation + nlohmann_json::nlohmann_json + PRIVATE_LINK_LIBRARIES + O2::Steer + TBB::tbb) + +o2_target_root_dictionary(TRKReconstruction + HEADERS include/TRKReconstruction/TimeFrame.h + LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h new file mode 100644 index 0000000000000..f42a1c897efb6 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -0,0 +1,70 @@ +// 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 TimeFrame.h +/// \brief TRK TimeFrame class derived from ITS TimeFrame +/// + +#ifndef ALICEO2_TRK_TIMEFRAME_H +#define ALICEO2_TRK_TIMEFRAME_H + +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Constants.h" +#include "ITStracking/Configuration.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include +#include +#include +#include + +#include + +class TTree; + +namespace o2 +{ +namespace trk +{ +class Hit; +class GeometryTGeo; + +/// TRK TimeFrame class that extends ITS TimeFrame functionality +/// This allows for customization of tracking algorithms specific to the TRK detector +template +class TimeFrame : public o2::its::TimeFrame +{ + public: + TimeFrame() = default; + ~TimeFrame() override = default; + + /// Override methods if needed for TRK-specific behavior + /// For now, we inherit all functionality from ITS TimeFrame + + /// Process hits from TTree to initialize ROFs + /// \param hitsTree Tree containing TRK hits + /// \param mcHeaderTree Tree containing MC event headers + /// \param nEvents Number of events to process + /// \param gman TRK geometry manager instance + /// \param config Configuration parameters for hit reconstruction + int loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config); + + /// Add primary vertices from MC headers for each ROF + /// \param mcHeaderTree Tree containing MC event headers + /// \param nRofs Number of ROFs (Read-Out Frames) + /// \param nEvents Number of events to process + /// \param inROFpileup Number of events per ROF + void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup); +}; + +} // namespace trk +} // namespace o2 + +#endif // ALICEO2_TRK_TIMEFRAME_H diff --git a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h similarity index 76% rename from DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx rename to Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h index bcf10d74c95ff..09ab598ec626c 100644 --- a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -9,8 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "DataFormatsMCH/DsChannelGroup.h" +#ifdef __CLING__ -std::string o2::mch::DsChannelId() const -{ -} +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::trk::TimeFrame < 11> + ; + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx new file mode 100644 index 0000000000000..610a08450d5ee --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -0,0 +1,194 @@ +// 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 TimeFrame.cxx +/// \brief TRK TimeFrame implementation +/// + +#include "TRKReconstruction/TimeFrame.h" +#include "TRKSimulation/Hit.h" +#include "TRKBase/GeometryTGeo.h" +#include "Framework/Logger.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include +#include +#include +#include + +namespace o2::trk +{ + +template +int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) +{ + constexpr std::array startLayer{0, 3}; + const Long64_t nEvents = hitsTree->GetEntries(); + + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L) | o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; + + // Calculate number of ROFs and initialize data structures + this->mNrof = (nEvents + inROFpileup - 1) / inROFpileup; + + // Reset and prepare ROF data structures + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mMinR[iLayer] = std::numeric_limits::max(); + this->mMaxR[iLayer] = std::numeric_limits::lowest(); + this->mROFramesClusters[iLayer].clear(); + this->mROFramesClusters[iLayer].resize(this->mNrof + 1, 0); + this->mUnsortedClusters[iLayer].clear(); + this->mTrackingFrameInfo[iLayer].clear(); + this->mClusterExternalIndices[iLayer].clear(); + } + + // Pre-count hits to reserve memory efficiently + int totalNHits{0}; + std::array clusterCountPerLayer{}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + for (const auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; // skip non-barrel hits + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + if (layer >= nLayers) { + continue; + } + ++clusterCountPerLayer[layer]; + totalNHits++; + } + } + + // Reserve memory for all layers + for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + this->mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + this->mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } + clearResizeBoundedVector(this->mClusterSize, totalNHits, this->mMemoryPool.get()); + + std::array resolution{0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004}; + if (config["geometry"]["pitch"].size() == nLayers) { + for (int iLayer{0}; iLayer < config["geometry"]["pitch"].size(); ++iLayer) { + LOGP(info, "Setting resolution for layer {} from config", iLayer); + LOGP(info, "Layer {} pitch {} cm", iLayer, config["geometry"]["pitch"][iLayer].get()); + resolution[iLayer] = config["geometry"]["pitch"][iLayer].get() / std::sqrt(12.f); + } + } + LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); + + int hitCounter{0}; + auto labels = new dataformats::MCTruthContainer(); + + int iRof{0}; // Current ROF index + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + hitsTree->GetEntry(iEvent); + + for (auto& hit : *trkHit) { + if (gman->getDisk(hit.GetDetectorID()) != -1) { + continue; // skip non-barrel hits for this test + } + int subDetID = gman->getSubDetID(hit.GetDetectorID()); + const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + + float alpha{0.f}; + o2::math_utils::Point3D gloXYZ; + o2::math_utils::Point3D trkXYZ; + float r{0.f}; + if (layer >= nLayers) { + continue; + } + if (layer >= 3) { + int chipID = hit.GetDetectorID(); + alpha = gman->getSensorRefAlphaMLOT(chipID); + const o2::math_utils::Transform3D& l2g = gman->getMatrixL2G(chipID); + auto locXYZ = l2g ^ (hit.GetPos()); + locXYZ.SetX(locXYZ.X() + gRandom->Gaus(0.0, resolution[layer])); + locXYZ.SetZ(locXYZ.Z() + gRandom->Gaus(0.0, resolution[layer])); + gloXYZ = gman->getMatrixL2G(chipID) * locXYZ; + trkXYZ = gman->getMatrixT2L(chipID - gman->getNumberOfActivePartsVD()) ^ locXYZ; + r = std::hypot(gloXYZ.X(), gloXYZ.Y()); + } else { + const auto& hitPos = hit.GetPos(); + r = std::hypot(hitPos.X(), hitPos.Y()); + alpha = std::atan2(hitPos.Y(), hitPos.X()) + gRandom->Gaus(0.0, resolution[layer] / r); + o2::math_utils::bringTo02Pi(alpha); + gloXYZ.SetX(r * std::cos(alpha)); + gloXYZ.SetY(r * std::sin(alpha)); + gloXYZ.SetZ(hitPos.Z() + gRandom->Gaus(0.0, resolution[layer])); + trkXYZ.SetX(r); + trkXYZ.SetY(0.f); + trkXYZ.SetZ(gloXYZ.Z()); + } + this->mMinR[layer] = std::min(this->mMinR[layer], r); + this->mMaxR[layer] = std::max(this->mMaxR[layer], r); + this->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{resolution[layer] * resolution[layer], 0., resolution[layer] * resolution[layer]}); + /// Rotate to the global frame + this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), this->mUnsortedClusters[layer].size()); + this->addClusterExternalIndexToLayer(layer, hitCounter); + MCCompLabel label{hit.GetTrackID(), static_cast(iEvent), 0}; + labels->addElement(hitCounter, label); + this->mClusterSize[hitCounter] = 1; // For compatibility with cluster-based tracking, set cluster size to 1 for hits + hitCounter++; + } + trkHit->clear(); + + // Update ROF structure when we complete an ROF or reach the last event + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + for (unsigned int iLayer{0}; iLayer < this->mUnsortedClusters.size(); ++iLayer) { + this->mROFramesClusters[iLayer][iRof] = this->mUnsortedClusters[iLayer].size(); // effectively calculating an exclusive sum + } + // Update primary vertices ROF structure + } + this->mClusterLabels = labels; + } + return this->mNrof; +} + +template +void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup) +{ + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + this->mROFramesPV.clear(); + this->mROFramesPV.resize(nRofs + 1, 0); + this->mPrimaryVertices.clear(); + + int iRof{0}; + for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { + mcHeaderTree->GetEntry(iEvent); + o2::its::Vertex vertex; + vertex.setXYZ(mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + vertex.setNContributors(30); + vertex.setChi2(0.f); + LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {})", iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ()); + this->mPrimaryVertices.push_back(vertex); + if ((iEvent + 1) % inROFpileup == 0 || iEvent == nEvents - 1) { + iRof++; + this->mROFramesPV[iRof] = this->mPrimaryVertices.size(); // effectively calculating an exclusive sum + } + } + this->mMultiplicityCutMask.resize(nRofs, true); /// all ROFs are valid with MC primary vertices. +} + +// Explicit template instantiation for TRK with 11 layers +template class TimeFrame<11>; + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h index 658fb823bb596..73c95b04c45e3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h @@ -29,6 +29,12 @@ class ChipDigitsContainer : public o2::itsmft::ChipDigitsContainer using Segmentation = SegmentationChip; + /// Get global ordering key made of readout frame, column and row + static ULong64_t getOrderingKey(UInt_t roframe, UShort_t row, UShort_t col) + { + return (static_cast(roframe) << (8 * sizeof(UInt_t))) + (static_cast(col) << (8 * sizeof(Short_t))) + row; + } + ClassDefNV(ChipDigitsContainer, 1); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 06fd2d9670b67..a2629a101d2d0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -146,10 +146,10 @@ void Detector::buildTRKMiddleOuterLayers() mLayers[3].setLayout(trkPars.layoutML); // Outer tracker - mLayers[4].setLayout(trkPars.layoutOL); - mLayers[5].setLayout(trkPars.layoutOL); - mLayers[6].setLayout(trkPars.layoutOL); - mLayers[7].setLayout(trkPars.layoutOL); + mLayers[4].setLayout(trkPars.layoutOT); + mLayers[5].setLayout(trkPars.layoutOT); + mLayers[6].setLayout(trkPars.layoutOT); + mLayers[7].setLayout(trkPars.layoutOT); } void Detector::configFromFile(std::string fileName) @@ -261,12 +261,32 @@ void Detector::createGeometry() mServices.createServices(vTRK); // Build the VD using the petal builder - // Choose the VD design (here: IRIS4 by default). - // You can wire this to a parameter in TRKBaseParam if desired. - // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); + // Choose the VD design based on TRKBaseParam.layoutVD + auto& trkPars = TRKBaseParam::Instance(); o2::trk::clearVDSensorRegistry(); - o2::trk::createIRISGeometryFullCyl(vTRK); + + switch (trkPars.layoutVD) { + case kIRIS4: + LOG(info) << "Building VD with IRIS4 layout"; + o2::trk::createIRIS4Geometry(vTRK); + break; + case kIRISFullCyl: + LOG(info) << "Building VD with IRIS fully cylindrical layout"; + o2::trk::createIRISGeometryFullCyl(vTRK); + break; + case kIRIS5: + LOG(info) << "Building VD with IRIS5 layout"; + o2::trk::createIRIS5Geometry(vTRK); + break; + case kIRIS4a: + LOG(info) << "Building VD with IRIS4a layout"; + o2::trk::createIRIS4aGeometry(vTRK); + break; + default: + LOG(fatal) << "Unknown VD layout option: " << static_cast(trkPars.layoutVD); + break; + } // Fill sensor names from registry right after geometry creation const auto& regs = o2::trk::vdSensorRegistry(); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 8d30cf9759e40..82b6fbd40af59 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -122,17 +122,12 @@ TGeoVolume* TRKLayer::createChip(std::string type) chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); sensVol = createSensor("cylinder"); - metalVol = createMetalStack("cylinder"); - - TGeoCombiTrans* transSens = new TGeoCombiTrans(); - transSens->SetTranslation(0, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, transSens); + chipVol->AddNode(sensVol, 1, nullptr); - TGeoCombiTrans* transMetal = new TGeoCombiTrans(); - transMetal->SetTranslation(0, mSensorThickness / 2, 0); // TO BE CHECKED !!! + metalVol = createMetalStack("cylinder"); LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); - chipVol->AddNode(metalVol, 1, transMetal); + chipVol->AddNode(metalVol, 1, nullptr); // deadVol = createDeadzone("cylinder"); } else if (type == "flat") { @@ -175,7 +170,9 @@ TGeoVolume* TRKLayer::createModule(std::string type) TGeoVolume* moduleVol; if (type == "cylinder") { - module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + double moduleLength = constants::moduleMLOT::length * mNumberOfModules; + + module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, moduleLength / 2); moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); TGeoVolume* chipVol = createChip("cylinder"); @@ -229,8 +226,10 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) TGeoShape* halfStave; TGeoVolume* halfStaveVol; + double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; + if (type == "cylinder") { - halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); + halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, halfStaveLength / 2); halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); TGeoVolume* moduleVol = createModule("cylinder"); @@ -239,7 +238,6 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) } else if (type == "flat") { double moduleLength = constants::moduleMLOT::length; double halfStaveWidth = constants::OT::halfstave::width; - double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; halfStave = new TGeoBBox(halfStaveWidth / 2, mChipThickness / 2, halfStaveLength / 2); halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); @@ -271,8 +269,10 @@ TGeoVolume* TRKLayer::createStave(std::string type) TGeoShape* stave; TGeoVolume* staveVol; + double staveLength = constants::moduleMLOT::length * mNumberOfModules; + if (type == "cylinder") { - stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); TGeoVolume* moduleVol = createModule("cylinder"); @@ -281,7 +281,6 @@ TGeoVolume* TRKLayer::createStave(std::string type) } else if (type == "flat") { double moduleLength = constants::moduleMLOT::length; double staveWidth = constants::ML::width; - double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mChipThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); @@ -304,7 +303,6 @@ TGeoVolume* TRKLayer::createStave(std::string type) double halfstaveWidth = constants::OT::halfstave::width; double staveWidth = constants::OT::width - overlap; - double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mLogicalVolumeThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); @@ -343,15 +341,16 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) TGeoTube* layer; TGeoVolume* layerVol; + double layerLength = constants::moduleMLOT::length * mNumberOfModules; + if (mLayout == eLayout::kCylinder) { - layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + layer = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, layerLength / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); TGeoVolume* staveVol = createStave("cylinder"); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, 1, nullptr); } else if (mLayout == eLayout::kTurboStaves) { - double layerLength = constants::moduleMLOT::length * mNumberOfModules; double staveWidth = constants::ML::width; // Each stave has two modules (based on the LOI design) if (mInnerRadius > 25) { @@ -390,7 +389,6 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) } else if (mLayout == kStaggered) { double overlapInStave = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true)overlap - double layerLength = constants::moduleMLOT::length * mNumberOfModules; double staveWidth = constants::OT::width - overlapInStave; layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 51eea905c436a..bd27a5bc30f62 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -264,8 +264,8 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Carbon Fiber Cylinder support for the middle tracker float rMinMiddleCarbonSupport = 34.8f; // Arbitrary value float rMaxMiddleCarbonSupport = 35.f; // 2 mm of carbon fiber - const float zLengthMiddleCarbon = 62.f; - TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon); + const float zLengthMiddleCarbon = 129.f; + TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon / 2.); TGeoVolume* middleBarrelCarbonSupportVolume = new TGeoVolume("TRK_MID_CARBONSUPPORT", middleBarrelCarbonSupport, medCFiber); middleBarrelCarbonSupportVolume->SetLineColor(kGray); LOGP(info, "Creating carbon fiber support for Middle Tracker"); @@ -318,28 +318,28 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Middle barrel connection disks const float rMinMiddleBarrelDisk = 5.68f; const float rMaxMiddleBarrelDisk = 35.f; - const float zLengthMiddleBarrel = 62.f; + const float zLengthMiddleBarrel = 64.5f; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { - TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick); - TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick); + TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick / 2.); + TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_SIO2_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskSIO2, medSiO2); TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPE, medPE); middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); middleBarrelConnDiskPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); - auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2 + zLengthMiddleBarrel), rot); - auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2 + zLengthMiddleBarrel), rot); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2. + zLengthMiddleBarrel), rot); motherVolume->AddNode(middleBarrelConnDiskSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarrelConnDiskPEVolume, 1, combiTransPE); - TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick); - TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick); + TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick / 2.); + TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick / 2.); TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_CU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskCu, medCu); TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPEPower, medPE); middleBarrelConnDiskCuVolume->SetLineColor(kGray); middleBarrelConnDiskPEPowerVolume->SetLineColor(kGray); - auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2 + zLengthMiddleBarrel), rot); - auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2 + zLengthMiddleBarrel), rot); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2. + zLengthMiddleBarrel), rot); motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); @@ -357,39 +357,39 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) float rMaxMiddleServicesBarFwd = 74.5f + siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick; for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { // Create fibers: 3.07mm, 50% SiO2, 50% PE - TGeoTube* middleBarFwdFiberSIO2 = new TGeoTube("TRK_MIDBARFWD_FIBER_SIO2sh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, siO2FiberThick); - TGeoTube* middleBarFwdFiberPE = new TGeoTube("TRK_MIDBARFWD_FIBER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, peFiberThick); + TGeoTube* middleBarFwdFiberSIO2 = new TGeoTube("TRK_MIDBARFWD_FIBER_SIO2sh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, siO2FiberThick / 2.); + TGeoTube* middleBarFwdFiberPE = new TGeoTube("TRK_MIDBARFWD_FIBER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, peFiberThick / 2.); TGeoVolume* middleBarFwdFiberSIO2Volume = new TGeoVolume("TRK_MIDBARFWD_FIBER_SIO2", middleBarFwdFiberSIO2, medSiO2); TGeoVolume* middleBarFwdFiberPEVolume = new TGeoVolume("TRK_MIDBARFWD_FIBER_PE", middleBarFwdFiberPE, medPE); middleBarFwdFiberSIO2Volume->SetLineColor(kGray); middleBarFwdFiberPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); - auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2 + zLengthMiddleServices), rot); - auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2 + zLengthMiddleServices), rot); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick / 2. + zLengthMiddleServices), rot); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdFiberSIO2Volume, 1, combiTransSIO2); motherVolume->AddNode(middleBarFwdFiberPEVolume, 1, combiTransPE); // Create powerlines: 10.9mm, 9% Cu, 91% PE - TGeoTube* middleBarFwdPowerCu = new TGeoTube("TRK_MIDBARFWD_POWER_CUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, cuPowerThick); - TGeoTube* middleBarFwdPowerPE = new TGeoTube("TRK_MIDBARFWD_POWER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, pePowerThick); + TGeoTube* middleBarFwdPowerCu = new TGeoTube("TRK_MIDBARFWD_POWER_CUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, cuPowerThick / 2.); + TGeoTube* middleBarFwdPowerPE = new TGeoTube("TRK_MIDBARFWD_POWER_PEsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, pePowerThick / 2.); TGeoVolume* middleBarFwdPowerCuVolume = new TGeoVolume("TRK_MIDBARFWD_POWER_CU", middleBarFwdPowerCu, medCu); TGeoVolume* middleBarFwdPowerPEVolume = new TGeoVolume("TRK_MIDBARFWD_POWER_PE", middleBarFwdPowerPE, medPE); middleBarFwdPowerCuVolume->SetLineColor(kGray); middleBarFwdPowerPEVolume->SetLineColor(kGray); - auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2 + zLengthMiddleServices), rot); - auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2 + zLengthMiddleServices), rot); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick / 2. + zLengthMiddleServices), rot); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdPowerCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarFwdPowerPEVolume, 1, combiTransPEPower); // Create cooling pipes: 4.74mm, 56% PU, 44% H2O - TGeoTube* middleBarFwdCoolingPU = new TGeoTube("TRK_MIDBARFWD_COOLING_PUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, puCoolingThick); - TGeoTube* middleBarFwdCoolingH2O = new TGeoTube("TRK_MIDBARFWD_COOLING_H2Osh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, h2oCoolingThick); + TGeoTube* middleBarFwdCoolingPU = new TGeoTube("TRK_MIDBARFWD_COOLING_PUsh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, puCoolingThick / 2.); + TGeoTube* middleBarFwdCoolingH2O = new TGeoTube("TRK_MIDBARFWD_COOLING_H2Osh", rMinMiddleBarrel, rMaxMiddleServicesBarFwd, h2oCoolingThick / 2.); TGeoVolume* middleBarFwdCoolingPUVolume = new TGeoVolume("TRK_MIDBARFWD_COOLING_PU", middleBarFwdCoolingPU, medPU); TGeoVolume* middleBarFwdCoolingH2OVolume = new TGeoVolume("TRK_MIDBARFWD_COOLING_H2O", middleBarFwdCoolingH2O, medH2O); middleBarFwdCoolingPUVolume->SetLineColor(kGray); middleBarFwdCoolingH2OVolume->SetLineColor(kGray); - auto* combiTransCoolingPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2 + zLengthMiddleServices), rot); - auto* combiTransCoolingH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2 + zLengthMiddleServices), rot); + auto* combiTransCoolingPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2. + zLengthMiddleServices), rot); + auto* combiTransCoolingH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2. + zLengthMiddleServices), rot); motherVolume->AddNode(middleBarFwdCoolingPUVolume, 1, combiTransCoolingPU); motherVolume->AddNode(middleBarFwdCoolingH2OVolume, 1, combiTransCoolingH2O); } @@ -501,4 +501,4 @@ void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) motherVolume->AddNode(outerBarrelCoolingH2OVolume, 1, nullptr); } } // namespace trk -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 6ce04bb8443ef..b06faa38211bb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -749,7 +749,7 @@ static TGeoVolume* buildPetalAssembly(int nPetals, /*fullCylindricalRadialWalls=*/fullCylinders); addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0, fullCylinders); - addDisks(petalAsm, nPetals, petalID, fullCylinders); + // addDisks(petalAsm, nPetals, petalID, fullCylinders); // disks removed according to the v3b layout addColdPlate(petalAsm, nPetals, petalID, /*fullCylinders=*/false); addIRISServiceModulesSegmented(petalAsm, nPetals); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt index e86ed7982c85b..d6c8ea85c2bbd 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt @@ -14,17 +14,23 @@ o2_add_library(TRKWorkflow SOURCES src/DigitReaderSpec.cxx src/DigitWriterSpec.cxx src/TrackerSpec.cxx + src/TrackWriterSpec.cxx src/RecoWorkflow.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2::GPUWorkflow O2::SimConfig O2::DataFormatsITSMFT O2::SimulationDataFormat - O2::DPLUtils) + O2::DPLUtils + O2::TRKBase + O2::TRKSimulation + O2::TRKReconstruction + nlohmann_json::nlohmann_json) o2_add_executable(reco-workflow SOURCES src/trk-reco-workflow.cxx COMPONENT_NAME alice3-trk PUBLIC_LINK_LIBRARIES O2::TRKWorkflow O2::TRKSimulation - O2::ITStracking) \ No newline at end of file + O2::TRKReconstruction + O2::ITStracking) diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md new file mode 100644 index 0000000000000..1cdce15b72726 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md @@ -0,0 +1,130 @@ +# TRK Reconstruction Workflow + +This document describes how to run the TRK (ALICE 3 Tracker) reconstruction workflow and provides examples of configuration files. + +## Overview + +The TRK reconstruction workflow performs track reconstruction from simulated hits, producing reconstructed tracks with MC truth labels. The workflow currently supports the track reconstruction from hits using the Cellular Automaton (CA) algorithm. The ouput is stored to a ROOT file for offline analysis (example of QA macro provided in `macros/test/CheckTracksCA.C`). + +## Quick Start + +### Basic Command + +```bash +o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +### Command Line Options + +- `--tracking-from-hits-config `: Path to tracking configuration JSON file (required) +- `-b`: Batch mode (no GUI) +- `--disable-root-output`: Skip writing tracks to ROOT file +- `--help`: Show all available options + +## Configuration File + +The tracking configuration is provided via a JSON file that specifies: +1. Input file paths +2. Geometry parameters (magnetic field, detector pitch) +3. Tracking algorithm parameters (can specify multiple iterations) + +### Example Configuration (`config_tracker.json`) + +```json +{ + "inputfiles": { + "hits": "o2sim_HitsTRK.root", + "geometry": "o2sim_geometry.root", + "mcHeader": "o2sim_MCHeader.root", + "kinematics": "o2sim_Kine.root" + }, + "geometry": { + "bz": 5.0, + "pitch": [0.001, 0.001, 0.001, 0.001, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004, 0.004] + }, + "trackingparams": [{ + "NLayers": 11, + "DeltaROF": 0, + "LayerZ": [25.1, 25.1, 25.1, 64.2, 64.2, 64.2, 64.2, 64.2, 128.5, 128.5, 128.5], + "LayerRadii": [0.5, 1.2, 2.5, 7.05, 9.05, 12.05, 20.05, 30.05, 45.05, 60.5, 80.05], + "LayerxX0": [0.001, 0.001, 0.001, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01], + "LayerResolution": [0.0003, 0.0003, 0.0003, 0.0003, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012, 0.0012], + "SystErrorY2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "SystErrorZ2": [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], + "ZBins": 256, + "PhiBins": 128, + "nROFsPerIterations": -1, + "UseDiamond": false, + "Diamond": [0.0, 0.0, 0.0], + "AllowSharingFirstCluster": false, + "ClusterSharing": 0, + "MinTrackLength": 7, + "NSigmaCut": 10, + "PVres": 0.01, + "TrackletMinPt": 0.1, + "TrackletsPerClusterLimit": 2.0, + "CellDeltaTanLambdaSigma": 0.007, + "CellsPerClusterLimit": 2.0, + "MaxChi2ClusterAttachment": 60.0, + "MaxChi2NDF": 30.0, + "ReseedIfShorter": 6, + "MinPt": [0.0, 0.0, 0.0, 0.0], + "StartLayerMask": 4095, + "RepeatRefitOut": false, + "ShiftRefToCluster": true, + "FindShortTracks": false, + "PerPrimaryVertexProcessing": false, + "SaveTimeBenchmarks": false, + "DoUPCIteration": false, + "FataliseUponFailure": true, + "UseTrackFollower": true, + "UseTrackFollowerTop": false, + "UseTrackFollowerBot": false, + "UseTrackFollowerMix": true, + "TrackFollowerNSigmaCutZ": 1.0, + "TrackFollowerNSigmaCutPhi": 1.0, + "createArtefactLabels": false, + "PrintMemory": false, + "DropTFUponFailure": false + }] +} +``` +Note that the `trackingparams` field can contain multiple sets of parameters for different iterations of the tracking algorithm. The example above shows a single iteration with 11 layers and it is **not** optimized. + +## Complete Workflow Example + +### 1. Run Simulation + +First, generate simulation data: + +```bash +o2-sim-serial-run5 -n 200 -g pythia8hi -m TRK --configKeyValues "Diamond.width[0]=0.01;Diamond.width[1]=0.01;Diamond.width[2]=5;TRKBase.layoutML=kTurboStaves;TRKBase.layoutOT=kStaggered;" +``` + +This produces, among other files: +- `o2sim_HitsTRK.root` +- `o2sim_geometry.root` +- `o2sim_MCHeader.root` +- `o2sim_Kine.root` +That will be used by the reconstruction as currently we do not have clusters. + +### 2. Run Reconstruction + +Execute the tracking workflow: + +```bash +o2-alice3-trk-reco-workflow --tracking-from-hits-config config_tracker.json -b +``` + +This produces: +- `o2trac_trk.root`: Reconstructed tracks with MC labels + +### 3. Run Quality Assurance + +Analyze the tracking performance: + +```bash +root -l +.L CheckTracksCA.C+ +CheckTracksCA("o2trac_trk.root", "o2sim_Kine.root", "o2sim_HitsTRK.root", "trk_qa_output.root") +``` diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h index 98d4154f11dd8..7046955a20c2e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h @@ -14,6 +14,7 @@ #include "Framework/WorkflowSpec.h" #include "GPUDataTypesConfig.h" +#include namespace o2::trk { @@ -21,6 +22,7 @@ namespace reco_workflow { o2::framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, bool upstreamDigits = false, bool upstreamClusters = false, bool disableRootOutput = false, diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h similarity index 71% rename from Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h rename to Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h index 51dc5a6481eb5..105504e7c9fe6 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h @@ -9,23 +9,23 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// @file ClusterWriterSpec.h +/// @file TrackWriterSpec.h -#ifndef O2_MFT_CLUSTERWRITER_H_ -#define O2_MFT_CLUSTERWRITER_H_ +#ifndef O2_TRK_TRACKWRITER +#define O2_TRK_TRACKWRITER #include "Framework/DataProcessorSpec.h" namespace o2 { -namespace mft +namespace trk { /// create a processor spec -/// write MFT clusters a root file -framework::DataProcessorSpec getClusterWriterSpec(bool useMC); +/// write TRK tracks to ROOT file +o2::framework::DataProcessorSpec getTrackWriterSpec(bool useMC); -} // namespace mft +} // namespace trk } // namespace o2 -#endif /* O2_MFT_CLUSTERWRITER_H */ +#endif /* O2_TRK_TRACKWRITER */ diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h index dac1826e21cf6..33b25737bbc29 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h @@ -19,6 +19,9 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include + +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/TrackingInterface.h" #include "GPUDataTypesConfig.h" @@ -26,6 +29,8 @@ #include "TStopwatch.h" +#include + namespace o2::trk { class TrackerDPL : public framework::Task @@ -33,6 +38,7 @@ class TrackerDPL : public framework::Task public: TrackerDPL(std::shared_ptr gr, bool isMC, + const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; void init(framework::InitContext& ic) final; @@ -43,14 +49,18 @@ class TrackerDPL : public framework::Task private: void updateTimeDependentParams(framework::ProcessingContext& pc); + std::vector createTrackingParamsFromConfig(); // std::unique_ptr mRecChain = nullptr; // std::unique_ptr mChainITS = nullptr; // std::shared_ptr mGGCCDBRequest; // ITSTrackingInterface mITSTrackingInterface; + std::shared_ptr mMemoryPool; + std::shared_ptr mTaskArena; + nlohmann::json mHitRecoConfig; TStopwatch mTimer; }; -framework::DataProcessorSpec getTrackerSpec(bool useMC, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::trk #endif /* O2_TRK_TRACKERDPL */ diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index 09d447a576e48..5f6cbe2f96b04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -11,12 +11,16 @@ #include "TRKWorkflow/RecoWorkflow.h" #include "TRKWorkflow/TrackerSpec.h" +#include "TRKWorkflow/TrackWriterSpec.h" #include "Framework/CCDBParamSpec.h" +#include + namespace o2::trk::reco_workflow { framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, bool upstreamDigits, bool upstreamClusters, bool disableRootOutput, @@ -24,7 +28,12 @@ framework::WorkflowSpec getWorkflow(bool useMC, o2::gpu::gpudatatypes::DeviceType dtype) { framework::WorkflowSpec specs; - specs.emplace_back(o2::trk::getTrackerSpec(useMC, dtype)); + specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); + + if (!disableRootOutput) { + specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + } + return specs; } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx new file mode 100644 index 0000000000000..1606c32a0ea78 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx @@ -0,0 +1,57 @@ +// 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 TrackWriterSpec.cxx + +#include + +#include "TRKWorkflow/TrackWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsITS/TrackITS.h" +#include "SimulationDataFormat/MCCompLabel.h" + +using namespace o2::framework; + +namespace o2 +{ +namespace trk +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using LabelsType = std::vector; +using namespace o2::header; + +DataProcessorSpec getTrackWriterSpec(bool useMC) +{ + // Spectators for logging + auto tracksSize = std::make_shared(0); + auto tracksSizeGetter = [tracksSize](std::vector const& tracks) { + *tracksSize = tracks.size(); + }; + auto logger = [tracksSize]() { + LOG(info) << "TRKTrackWriter pulled " << *tracksSize << " tracks"; + }; + + return MakeRootTreeWriterSpec("trk-track-writer", + "o2trac_trk.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with TRK tracks"}, + BranchDefinition>{InputSpec{"tracks", "TRK", "TRACKS", 0}, + "TRKTrack", + tracksSizeGetter}, + BranchDefinition{InputSpec{"labels", "TRK", "TRACKSMCTR", 0}, + "TRKTrackMCTruth", + (useMC ? 1 : 0), // one branch if mc labels enabled + ""})(); +} + +} // namespace trk +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 868a8acc0fc6e..8fc67f0fa5567 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -10,11 +10,27 @@ // or submit itself to any jurisdiction. #include +#include +#include "DetectorsBase/GeometryManager.h" +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Configuration.h" +#include "Field/MagneticField.h" +#include "Field/MagFieldParam.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "SimulationDataFormat/MCEventHeader.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" +#include "TRKSimulation/Hit.h" +#include "TRKReconstruction/TimeFrame.h" #include "TRKWorkflow/TrackerSpec.h" +#include + +#include +#include namespace o2 { @@ -25,8 +41,14 @@ using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, + const std::string& hitRecoConfigFileName, o2::gpu::gpudatatypes::DeviceType dType) { + if (!hitRecoConfigFileName.empty()) { + std::ifstream configFile(hitRecoConfigFileName); + mHitRecoConfig = nlohmann::json::parse(configFile); + } + // mITSTrackingInterface.setTrackingMode(trMode); } @@ -46,13 +68,291 @@ void TrackerDPL::stop() LOGF(info, "CPU Reconstruction total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } +std::vector TrackerDPL::createTrackingParamsFromConfig() +{ + std::vector trackingParams; + + if (!mHitRecoConfig.contains("trackingparams") || !mHitRecoConfig["trackingparams"].is_array()) { + LOGP(fatal, "No trackingparams field found in configuration or it is not an array. Returning empty vector."); + return trackingParams; + } + + for (const auto& paramConfig : mHitRecoConfig["trackingparams"]) { + o2::its::TrackingParameters params; + + // Parse integer parameters + if (paramConfig.contains("NLayers")) { + params.NLayers = paramConfig["NLayers"].get(); + } + if (paramConfig.contains("DeltaROF")) { + params.DeltaROF = paramConfig["DeltaROF"].get(); + } + if (paramConfig.contains("ZBins")) { + params.ZBins = paramConfig["ZBins"].get(); + } + if (paramConfig.contains("PhiBins")) { + params.PhiBins = paramConfig["PhiBins"].get(); + } + if (paramConfig.contains("nROFsPerIterations")) { + params.nROFsPerIterations = paramConfig["nROFsPerIterations"].get(); + } + if (paramConfig.contains("ClusterSharing")) { + params.ClusterSharing = paramConfig["ClusterSharing"].get(); + } + if (paramConfig.contains("MinTrackLength")) { + params.MinTrackLength = paramConfig["MinTrackLength"].get(); + } + if (paramConfig.contains("ReseedIfShorter")) { + params.ReseedIfShorter = paramConfig["ReseedIfShorter"].get(); + } + if (paramConfig.contains("StartLayerMask")) { + params.StartLayerMask = paramConfig["StartLayerMask"].get(); + } + + // Parse float parameters + if (paramConfig.contains("NSigmaCut")) { + params.NSigmaCut = paramConfig["NSigmaCut"].get(); + } + if (paramConfig.contains("PVres")) { + params.PVres = paramConfig["PVres"].get(); + } + if (paramConfig.contains("TrackletMinPt")) { + params.TrackletMinPt = paramConfig["TrackletMinPt"].get(); + } + if (paramConfig.contains("TrackletsPerClusterLimit")) { + params.TrackletsPerClusterLimit = paramConfig["TrackletsPerClusterLimit"].get(); + } + if (paramConfig.contains("CellDeltaTanLambdaSigma")) { + params.CellDeltaTanLambdaSigma = paramConfig["CellDeltaTanLambdaSigma"].get(); + } + if (paramConfig.contains("CellsPerClusterLimit")) { + params.CellsPerClusterLimit = paramConfig["CellsPerClusterLimit"].get(); + } + if (paramConfig.contains("MaxChi2ClusterAttachment")) { + params.MaxChi2ClusterAttachment = paramConfig["MaxChi2ClusterAttachment"].get(); + } + if (paramConfig.contains("MaxChi2NDF")) { + params.MaxChi2NDF = paramConfig["MaxChi2NDF"].get(); + } + if (paramConfig.contains("TrackFollowerNSigmaCutZ")) { + params.TrackFollowerNSigmaCutZ = paramConfig["TrackFollowerNSigmaCutZ"].get(); + } + if (paramConfig.contains("TrackFollowerNSigmaCutPhi")) { + params.TrackFollowerNSigmaCutPhi = paramConfig["TrackFollowerNSigmaCutPhi"].get(); + } + + // Parse boolean parameters + if (paramConfig.contains("UseDiamond")) { + params.UseDiamond = paramConfig["UseDiamond"].get(); + } + if (paramConfig.contains("AllowSharingFirstCluster")) { + params.AllowSharingFirstCluster = paramConfig["AllowSharingFirstCluster"].get(); + } + if (paramConfig.contains("RepeatRefitOut")) { + params.RepeatRefitOut = paramConfig["RepeatRefitOut"].get(); + } + if (paramConfig.contains("ShiftRefToCluster")) { + params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); + } + if (paramConfig.contains("FindShortTracks")) { + params.FindShortTracks = paramConfig["FindShortTracks"].get(); + } + if (paramConfig.contains("PerPrimaryVertexProcessing")) { + params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); + } + if (paramConfig.contains("SaveTimeBenchmarks")) { + params.SaveTimeBenchmarks = paramConfig["SaveTimeBenchmarks"].get(); + } + if (paramConfig.contains("DoUPCIteration")) { + params.DoUPCIteration = paramConfig["DoUPCIteration"].get(); + } + if (paramConfig.contains("FataliseUponFailure")) { + params.FataliseUponFailure = paramConfig["FataliseUponFailure"].get(); + } + if (paramConfig.contains("UseTrackFollower")) { + params.UseTrackFollower = paramConfig["UseTrackFollower"].get(); + } + if (paramConfig.contains("UseTrackFollowerTop")) { + params.UseTrackFollowerTop = paramConfig["UseTrackFollowerTop"].get(); + } + if (paramConfig.contains("UseTrackFollowerBot")) { + params.UseTrackFollowerBot = paramConfig["UseTrackFollowerBot"].get(); + } + if (paramConfig.contains("UseTrackFollowerMix")) { + params.UseTrackFollowerMix = paramConfig["UseTrackFollowerMix"].get(); + } + if (paramConfig.contains("createArtefactLabels")) { + params.createArtefactLabels = paramConfig["createArtefactLabels"].get(); + } + if (paramConfig.contains("PrintMemory")) { + params.PrintMemory = paramConfig["PrintMemory"].get(); + } + if (paramConfig.contains("DropTFUponFailure")) { + params.DropTFUponFailure = paramConfig["DropTFUponFailure"].get(); + } + + // Parse vector parameters + if (paramConfig.contains("LayerZ")) { + params.LayerZ = paramConfig["LayerZ"].get>(); + } + if (paramConfig.contains("LayerRadii")) { + params.LayerRadii = paramConfig["LayerRadii"].get>(); + } + if (paramConfig.contains("LayerxX0")) { + params.LayerxX0 = paramConfig["LayerxX0"].get>(); + } + if (paramConfig.contains("LayerResolution")) { + params.LayerResolution = paramConfig["LayerResolution"].get>(); + } + if (paramConfig.contains("SystErrorY2")) { + params.SystErrorY2 = paramConfig["SystErrorY2"].get>(); + } + if (paramConfig.contains("SystErrorZ2")) { + params.SystErrorZ2 = paramConfig["SystErrorZ2"].get>(); + } + if (paramConfig.contains("MinPt")) { + params.MinPt = paramConfig["MinPt"].get>(); + } + + // Parse Diamond array + if (paramConfig.contains("Diamond") && paramConfig["Diamond"].is_array() && paramConfig["Diamond"].size() == 3) { + params.Diamond[0] = paramConfig["Diamond"][0].get(); + params.Diamond[1] = paramConfig["Diamond"][1].get(); + params.Diamond[2] = paramConfig["Diamond"][2].get(); + } + + // Parse size_t parameter + if (paramConfig.contains("MaxMemory")) { + params.MaxMemory = paramConfig["MaxMemory"].get(); + } + + // Parse CorrType enum + if (paramConfig.contains("CorrType")) { + int corrTypeInt = paramConfig["CorrType"].get(); + params.CorrType = static_cast::MatCorrType>(corrTypeInt); + } + + trackingParams.push_back(params); + } + + LOGP(info, "Loaded {} tracking parameter sets from configuration", trackingParams.size()); + return trackingParams; +} + void TrackerDPL::run(ProcessingContext& pc) { auto cput = mTimer.CpuTime(); auto realt = mTimer.RealTime(); mTimer.Start(false); - // mITSTrackingInterface.updateTimeDependentParams(pc); - // mITSTrackingInterface.run(pc); + + if (!mHitRecoConfig.empty()) { + TFile hitsFile(mHitRecoConfig["inputfiles"]["hits"].get().c_str(), "READ"); + TFile mcHeaderFile(mHitRecoConfig["inputfiles"]["mcHeader"].get().c_str(), "READ"); + TTree* hitsTree = hitsFile.Get("o2sim"); + std::vector* trkHit = nullptr; + hitsTree->SetBranchAddress("TRKHit", &trkHit); + + TTree* mcHeaderTree = mcHeaderFile.Get("o2sim"); + auto mcheader = new o2::dataformats::MCEventHeader; + mcHeaderTree->SetBranchAddress("MCEventHeader.", &mcheader); + + o2::base::GeometryManager::loadGeometry(mHitRecoConfig["inputfiles"]["geometry"].get().c_str(), false, true); + auto* gman = o2::trk::GeometryTGeo::Instance(); + + const Long64_t nEvents{hitsTree->GetEntries()}; + LOGP(info, "Starting reconstruction from hits for {} events", nEvents); + + if (mMemoryPool.get() == nullptr) { + mMemoryPool = std::make_shared(); + } + if (mTaskArena.get() == nullptr) { + mTaskArena = std::make_shared(1); /// TODO: make it configurable + } + + o2::trk::TimeFrame<11> timeFrame; + o2::its::TrackerTraits<11> itsTrackerTraits; + o2::its::Tracker<11> itsTracker(&itsTrackerTraits); + timeFrame.setMemoryPool(mMemoryPool); + itsTrackerTraits.setMemoryPool(mMemoryPool); + itsTrackerTraits.setNThreads(mTaskArena->max_concurrency(), mTaskArena); + itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); + itsTracker.adoptTimeFrame(timeFrame); + itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); + auto field = new field::MagneticField("ALICE3Mag", "ALICE 3 Magnetic Field", mHitRecoConfig["geometry"]["bz"].get() / 5.f, 0.0, o2::field::MagFieldParam::k5kGUniform); + TGeoGlobalMagField::Instance()->SetField(field); + TGeoGlobalMagField::Instance()->Lock(); + + int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); + + const int inROFpileup{mHitRecoConfig.contains("inROFpileup") ? mHitRecoConfig["inROFpileup"].get() : 1}; + + // Add primary vertices from MC headers for each ROF + timeFrame.getPrimaryVerticesFromMC(mcHeaderTree, nRofs, nEvents, inROFpileup); + // Create tracking parameters from config and set them in the time frame + auto trackingParams = createTrackingParamsFromConfig(); + + itsTrackerTraits.updateTrackingParameters(trackingParams); + + const auto trackingLoopStart = std::chrono::steady_clock::now(); + for (size_t iter{0}; iter < trackingParams.size(); ++iter) { + LOGP(info, "{}", trackingParams[iter].asString()); + timeFrame.initialise(iter, trackingParams[iter], 11, false); + itsTrackerTraits.computeLayerTracklets(iter, -1, -1); + LOGP(info, "Number of tracklets in iteration {}: {}", iter, timeFrame.getNumberOfTracklets()); + itsTrackerTraits.computeLayerCells(iter); + LOGP(info, "Number of cells in iteration {}: {}", iter, timeFrame.getNumberOfCells()); + itsTrackerTraits.findCellsNeighbours(iter); + LOGP(info, "Number of cell neighbours in iteration {}: {}", iter, timeFrame.getNumberOfNeighbours()); + itsTrackerTraits.findRoads(iter); + LOGP(info, "Number of roads in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); + itsTrackerTraits.extendTracks(iter); + } + const auto trackingLoopElapsedMs = std::chrono::duration_cast(std::chrono::steady_clock::now() - trackingLoopStart).count(); + LOGP(info, "Tracking iterations block took {} ms", trackingLoopElapsedMs); + + itsTracker.computeTracksMClabels(); + + // Stream tracks and their MC labels to the output + // Collect all tracks and labels from all ROFs + std::vector allTracks; + std::vector allLabels; + + int totalTracks = 0; + int goodTracks = 0; + int fakeTracks = 0; + + for (int iRof = 0; iRof < nRofs; ++iRof) { + const auto& rofTracks = timeFrame.getTracks(iRof); + const auto& rofLabels = timeFrame.getTracksLabel(iRof); + + allTracks.insert(allTracks.end(), rofTracks.begin(), rofTracks.end()); + allLabels.insert(allLabels.end(), rofLabels.begin(), rofLabels.end()); + + totalTracks += rofTracks.size(); + for (const auto& label : rofLabels) { + if (label.isFake()) { + fakeTracks++; + } else { + goodTracks++; + } + } + } + + LOGP(info, "=== Tracking Summary ==="); + LOGP(info, "Total tracks reconstructed: {}", totalTracks); + LOGP(info, "Good tracks: {} ({:.1f}%)", goodTracks, totalTracks > 0 ? 100.0 * goodTracks / totalTracks : 0); + LOGP(info, "Fake tracks: {} ({:.1f}%)", fakeTracks, totalTracks > 0 ? 100.0 * fakeTracks / totalTracks : 0); + + // Stream tracks and labels to DPL output + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKS", 0}, allTracks); + pc.outputs().snapshot(o2::framework::Output{"TRK", "TRACKSMCTR", 0}, allLabels); + + LOGP(info, "Tracks and MC labels streamed to output"); + + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(framework::QuitRequest::Me); + } + mTimer.Stop(); LOGP(info, "CPU Reconstruction time for this TF {} s (cpu), {} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); } @@ -67,16 +367,11 @@ void TrackerDPL::endOfStream(EndOfStreamContext& ec) LOGF(info, "TRK CA-Tracker total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::gpudatatypes::DeviceType dType) +DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o2::gpu::gpudatatypes::DeviceType dType) { std::vector inputs; - - // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); - // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); - // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); - - // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + std::vector outputs; + outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); auto ggRequest = std::make_shared(false, // orbitResetTime false, // GRPECS=true false, // GRPLHCIF @@ -85,8 +380,29 @@ DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::gpudatatypes::DeviceType d o2::base::GRPGeomRequest::None, // geometry, but ignored until it will be put in the CCDB inputs, true); - std::vector outputs; - outputs.emplace_back("TRK", "TRACKS", 0, Lifetime::Timeframe); + + if (!hitRecoConfig.empty()) { + outputs.emplace_back("TRK", "TRACKSMCTR", 0, Lifetime::Timeframe); + return DataProcessorSpec{ + "trk-hits-tracker", + {}, + outputs, + AlgorithmSpec{adaptFromTask(ggRequest, + useMC, + hitRecoConfig, + dType)}, + Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}}}}; + } + + inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); + + // inputs.emplace_back("compClusters", "TRK", "COMPCLUSTERS", 0, Lifetime::Timeframe); + // inputs.emplace_back("patterns", "TRK", "PATTERNS", 0, Lifetime::Timeframe); + // inputs.emplace_back("ROframes", "TRK", "CLUSTERSROF", 0, Lifetime::Timeframe); + + // inputs.emplace_back("itscldict", "TRK", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); + // inputs.emplace_back("itsalppar", "TRK", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "VERTICES", 0, Lifetime::Timeframe); @@ -108,6 +424,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, o2::gpu::gpudatatypes::DeviceType d outputs, AlgorithmSpec{adaptFromTask(ggRequest, useMC, + hitRecoConfig, dType)}, Options{}}; } diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx index 8f44b01da1c9c..166e6f65b4b2b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx @@ -52,6 +52,7 @@ void customize(std::vector& workflowOptions) {"clusters-from-upstream", VariantType::Bool, false, {"clusters will be provided from upstream, skip clusterizer"}}, {"disable-root-output", VariantType::Bool, false, {"do not write output root files"}}, {"disable-mc", VariantType::Bool, false, {"disable MC propagation even if available"}}, + {"tracking-from-hits-config", VariantType::String, "", {"JSON file with tracking from hits configuration"}}, {"disable-tracking", VariantType::Bool, false, {"disable tracking step"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"use-gpu-workflow", VariantType::Bool, false, {"use GPU workflow (default: false)"}}, @@ -66,6 +67,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); + auto hitRecoConfig = configcontext.options().get("tracking-from-hits-config"); auto useGpuWF = configcontext.options().get("use-gpu-workflow"); auto gpuDevice = static_cast(configcontext.options().get("gpu-device")); auto extDigits = configcontext.options().get("digits-from-upstream"); @@ -76,5 +78,5 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // write the configuration used for the reco workflow o2::conf::ConfigurableParam::writeINI("o2itsrecoflow_configuration.ini"); - return o2::trk::reco_workflow::getWorkflow(useMC, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); + return o2::trk::reco_workflow::getWorkflow(useMC, hitRecoConfig, extDigits, extClusters, disableRootOutput, useGpuWF, gpuDevice); } diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index a7422c55e72b8..937fa8d2e982c 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -104,7 +104,7 @@ namespace carbonfoam // TODO: Waiting for the further information from WP5(Corrado) constexpr double HringLength{6.0 * mm}; // from blueprint constexpr double longeronsWidth{2.0 * mm}; // what is the height of the longerons? -constexpr double longeronsLength{segment::length - 2 * HringLength}; // 263mm from blueprint; overrriden to be consitent +constexpr double longeronsLength{segment::length - (2 * HringLength)}; // 263mm from blueprint; overrriden to be consitent constexpr double edgeBetwChipAndFoam{1.0 * mm}; // from blueprint but not used cause forms are already overlapping constexpr double gapBetwHringsLongerons{0.05 * mm}; // from blueprint constexpr std::array nHoles{11, 11, 11}; // how many holes for each layer? diff --git a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C index 32d5bad87ce21..5bc053c516079 100644 --- a/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C +++ b/Detectors/Upgrades/ITS3/macros/test/CheckChipResponseFile.C @@ -22,6 +22,7 @@ #include #include +#include "CCDB/BasicCCDBManager.h" #define ENABLE_UPGRADES #include "ITSMFTSimulation/AlpideSimResponse.h" #include "ITS3Simulation/ChipSimResponse.h" @@ -37,16 +38,12 @@ double cm2um(double cm) { return cm * 1e+4; } std::unique_ptr mAlpSimResp0, mAlpSimResp1, mAptSimResp1; -std::unique_ptr loadResponse(const std::string& fileName, const std::string& respName) +std::unique_ptr loadResponse(const std::string& path) { - TFile* f = TFile::Open(fileName.data()); - if (!f) { - std::cerr << fileName << " not found" << std::endl; - return nullptr; - } - auto base = f->Get(respName.c_str()); + auto& cdb = o2::ccdb::BasicCCDBManager::instance(); + o2::itsmft::AlpideSimResponse* base = cdb.get(path); if (!base) { - std::cerr << respName << " not found in " << fileName << std::endl; + std::cerr << path << " not found in " << '\n'; return nullptr; } return std::make_unique(base); @@ -54,24 +51,24 @@ std::unique_ptr loadResponse(const std::string& fileN void LoadRespFunc() { - std::string AptsFile = "$(O2_ROOT)/share/Detectors/Upgrades/ITS3/data/ITS3ChipResponseData/APTSResponseData.root"; - std::string AlpideFile = "$(O2_ROOT)/share/Detectors/ITSMFT/data/AlpideResponseData/AlpideResponseData.root"; + auto& cdb = o2::ccdb::BasicCCDBManager::instance(); + cdb.setURL("https://alice-ccdb.cern.ch/"); std::cout << "=====================\n"; LOGP(info, "ALPIDE Vbb=0V response"); - mAlpSimResp0 = loadResponse(AlpideFile, "response0"); // Vbb=0V + mAlpSimResp0 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbb0"); // Vbb=0V mAlpSimResp0->computeCentreFromData(); mAlpSimResp0->print(); LOGP(info, "Response Centre {}", mAlpSimResp0->getRespCentreDep()); std::cout << "=====================\n"; LOGP(info, "ALPIDE Vbb=-3V response"); - mAlpSimResp1 = loadResponse(AlpideFile, "response1"); // Vbb=-3V + mAlpSimResp1 = loadResponse("ITSMFT/Calib/ALPIDEResponseVbbM3"); // Vbb=-3V mAlpSimResp1->computeCentreFromData(); mAlpSimResp1->print(); LOGP(info, "Response Centre {}", mAlpSimResp1->getRespCentreDep()); std::cout << "=====================\n"; LOGP(info, "APTS response"); - mAptSimResp1 = loadResponse(AptsFile, "response1"); // APTS + mAptSimResp1 = loadResponse("IT3/Calib/APTSResponse"); // APTS mAptSimResp1->computeCentreFromData(); mAptSimResp1->print(); LOGP(info, "Response Centre {}", mAptSimResp1->getRespCentreDep()); diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h index 80565df55d154..3e230cee474bd 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h @@ -40,17 +40,19 @@ class DescriptorInnerBarrelITS3 : public o2::its::DescriptorInnerBarrel void createLayer(int idLayer, TGeoVolume* dest); void createServices(TGeoVolume* dest); void configure() {} + void addAlignableVolumesLayer(int idLayer, int wrapperLayerId, TString& parentPath, int& lastUID) const; protected: - int mNumLayers{constants::nLayers}; - // wrapper volume properties static constexpr double mTolerance{1e-3}; static constexpr double mWrapperMinRadiusITS3{constants::radiiInner[0] - mTolerance}; static constexpr double mWrapperMaxRadiusITS3{constants::services::radiusOuter + mTolerance}; - static constexpr double mWrapperZSpanITS3{constants::services::length * 2 + mTolerance}; // z length is divided in half + static constexpr double mWrapperZSpanITS3{(constants::services::length * 2) + mTolerance}; // z length is divided in half private: + void addAlignableVolumesHalfBarrel(int idLayer, int iHB, TString& parentPath, int& lastUID) const; + void addAlignableVolumesChips(int idLayer, int iHalfBarrel, TString& parentPath, int& lastUID) const; + std::array, constants::nLayers> mIBLayers; std::unique_ptr mServices; diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h index fd9195f9ee228..f45a4469ae2b8 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/ITS3Layer.h @@ -26,7 +26,7 @@ namespace o2::its3 { /// This class defines the geometry for the ITS3 IB layers. -class ITS3Layer +class ITS3Layer final { // The hierarchy will be the following: // ITS2 -> ITS3 @@ -76,7 +76,6 @@ class ITS3Layer void buildPartial(TGeoVolume* motherVolume, TGeoMatrix* mat = nullptr, BuildLevel level = BuildLevel::kAll, bool createMaterials = false); private: - bool mBuilt{false}; TGeoMedium* mSilicon{nullptr}; TGeoMedium* mAir{nullptr}; TGeoMedium* mCarbon{nullptr}; @@ -91,7 +90,7 @@ class ITS3Layer void createSegment(); void createChip(); void createCarbonForm(); - TGeoCompositeShape* getHringShape(TGeoTubeSeg* Hring); + TGeoCompositeShape* getHringShape(TGeoTubeSeg* Hring) const; void createLayerImpl(); uint8_t mNLayer{0}; // Layer number diff --git a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx index 04f244284d5b6..42644fbfe0c38 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx @@ -10,6 +10,8 @@ // or submit itself to any jurisdiction. #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" +#include "ITSBase/GeometryTGeo.h" +#include "Framework/Logger.h" using namespace o2::its3; @@ -26,3 +28,45 @@ void DescriptorInnerBarrelITS3::createServices(TGeoVolume* dest) mServices = std::make_unique(); mServices->createCYSSAssembly(dest); } + +void DescriptorInnerBarrelITS3::addAlignableVolumesLayer(int idLayer, int wrapperLayerId, TString& parentPath, int& lastUID) const +{ + TString wrpV = wrapperLayerId != -1 ? Form("%s%d_1", its::GeometryTGeo::getITSWrapVolPattern(), wrapperLayerId) : ""; + TString path = Form("%s/%s/%s%d_0", parentPath.Data(), wrpV.Data(), its::GeometryTGeo::getITS3LayerPattern(), idLayer); + TString sname = its::GeometryTGeo::composeSymNameLayer(idLayer, true); + + for (int iHalfBarrel{0}; iHalfBarrel < 2; ++iHalfBarrel) { + addAlignableVolumesHalfBarrel(idLayer, iHalfBarrel, path, lastUID); + } +} + +void DescriptorInnerBarrelITS3::addAlignableVolumesHalfBarrel(int idLayer, int iHB, TString& parentPath, int& lastUID) const +{ + // for ITS3 smallest alignable volume is the half-barrel (e.g., the carbon-form composite structure with the sensors) + TString path = Form("%s/%s%d_%d", parentPath.Data(), its::GeometryTGeo::getITS3HalfBarrelPattern(), idLayer, iHB); + TString sname = its::GeometryTGeo::composeSymNameHalfBarrel(idLayer, iHB, true); + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(fatal) << "Unable to set alignable entry ! " << sname << " : " << path; + } + addAlignableVolumesChips(idLayer, iHB, path, lastUID); +} + +void DescriptorInnerBarrelITS3::addAlignableVolumesChips(int idLayer, int iHB, TString& parentPath, int& lastUID) const +{ + for (int seg{0}; seg < constants::nSegments[idLayer]; ++seg) { + for (int rsu{0}; rsu < constants::segment::nRSUs; ++rsu) { + for (int tile{0}; tile < constants::rsu::nTiles; ++tile) { + TString path = parentPath; + path += Form("/%s_0/", its::GeometryTGeo::getITS3ChipPattern(idLayer)); + path += Form("%s_%d/", its::GeometryTGeo::getITS3SegmentPattern(idLayer), seg); + path += Form("%s_%d/", its::GeometryTGeo::getITS3RSUPattern(idLayer), rsu); + path += Form("%s_%d/", its::GeometryTGeo::getITS3TilePattern(idLayer), tile); + TString sname = its::GeometryTGeo::composeSymNameChip(idLayer, iHB, 0, seg, rsu, tile, true); + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(fatal) << "Unable to set alignable entry ! " << sname << " : " << path; + } + ++lastUID; + } + } + } +} diff --git a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index e0be011096450..c0f8fdc19d03b 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx @@ -67,11 +67,11 @@ void ITS3Layer::createLayer(TGeoVolume* motherVolume) // Create one layer of ITS3 and attach it to the motherVolume. getMaterials(); createLayerImpl(); - mBuilt = true; if (motherVolume == nullptr) { return; } + // Add it to motherVolume auto* trans = new TGeoTranslation(0, 0, -constants::segment::lengthSensitive / 2.); motherVolume->AddNode(mLayer, 0, trans); @@ -122,8 +122,8 @@ void ITS3Layer::createTile() mTile->AddNode(mPixelArray, 0, phiRotPixelArray); // Biasing - double biasPhi1 = constants::pixelarray::width / mR * o2m::Rad2Deg + readoutPhi2; - double biasPhi2 = biasing::width / mR * o2m::Rad2Deg + biasPhi1; + double biasPhi1 = (constants::pixelarray::width / mR * o2m::Rad2Deg) + readoutPhi2; + double biasPhi2 = (biasing::width / mR * o2m::Rad2Deg) + biasPhi1; auto biasing = new TGeoTubeSeg(mRmin, mRmax, biasing::length / 2, biasPhi1, biasPhi2); auto biasingVol = new TGeoVolume(Form("biasing%d", mNLayer), biasing, mSilicon); biasingVol->SetLineColor(biasing::color); @@ -131,9 +131,9 @@ void ITS3Layer::createTile() mTile->AddNode(biasingVol, 0); // Power Switches are on the side right side of the pixel array and biasing. - auto zMovePowerSwitches = new TGeoTranslation(0, 0, +powerswitches::length / 2. + constants::pixelarray::length / 2.); + auto zMovePowerSwitches = new TGeoTranslation(0, 0, (+powerswitches::length / 2.) + (constants::pixelarray::length / 2.)); double powerPhi1 = readoutPhi2; - double powerPhi2 = powerswitches::width / mR * o2m::Rad2Deg + powerPhi1; + double powerPhi2 = (powerswitches::width / mR * o2m::Rad2Deg) + powerPhi1; auto powerSwitches = new TGeoTubeSeg(mRmin, mRmax, powerswitches::length / 2, powerPhi1, powerPhi2); auto powerSwitchesVol = new TGeoVolume(Form("powerswitches%d", mNLayer), powerSwitches, mSilicon); powerSwitchesVol->SetLineColor(powerswitches::color); @@ -166,7 +166,7 @@ void ITS3Layer::createRSU() // Lower Left auto zMoveLL1 = new TGeoTranslation(0, 0, constants::tile::length); auto zMoveLL2 = new TGeoTranslation(0, 0, constants::tile::length * 2.); - auto zMoveLLDB = new TGeoTranslation(0, 0, -databackbone::length / 2. - constants::pixelarray::length / 2.); + auto zMoveLLDB = new TGeoTranslation(0, 0, (-databackbone::length / 2.) - (constants::pixelarray::length / 2.)); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, nullptr); mRSU->AddNode(mTile, nCopyRSU++, zMoveLL1); @@ -175,9 +175,9 @@ void ITS3Layer::createRSU() // Lower Right auto zMoveLR0 = new TGeoTranslation(0, 0, +length / 2.); - auto zMoveLR1 = new TGeoTranslation(0, 0, constants::tile::length + length / 2.); - auto zMoveLR2 = new TGeoTranslation(0, 0, constants::tile::length * 2. + length / 2.); - auto zMoveLRDB = new TGeoTranslation(0, 0, -databackbone::length / 2. + length / 2. - constants::pixelarray::length / 2.); + auto zMoveLR1 = new TGeoTranslation(0, 0, constants::tile::length + (length / 2.)); + auto zMoveLR2 = new TGeoTranslation(0, 0, (constants::tile::length * 2.) + (length / 2.)); + auto zMoveLRDB = new TGeoTranslation(0, 0, (-databackbone::length / 2.) + (length / 2.) - (constants::pixelarray::length / 2.)); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, zMoveLR0); mRSU->AddNode(mTile, nCopyRSU++, zMoveLR1); @@ -192,7 +192,7 @@ void ITS3Layer::createRSU() // Upper Left auto zMoveUL1 = new TGeoCombiTrans(0, 0, constants::tile::length, rot); auto zMoveUL2 = new TGeoCombiTrans(0, 0, constants::tile::length * 2., rot); - auto zMoveULDB = new TGeoCombiTrans(0, 0, -databackbone::length / 2. - constants::pixelarray::length / 2., rot); + auto zMoveULDB = new TGeoCombiTrans(0, 0, (-databackbone::length / 2.) - (constants::pixelarray::length / 2.), rot); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, rot); mRSU->AddNode(mTile, nCopyRSU++, zMoveUL1); @@ -201,9 +201,9 @@ void ITS3Layer::createRSU() // Upper Right auto zMoveUR0 = new TGeoCombiTrans(0, 0, +length / 2., rot); - auto zMoveUR1 = new TGeoCombiTrans(0, 0, constants::tile::length + length / 2., rot); - auto zMoveUR2 = new TGeoCombiTrans(0, 0, constants::tile::length * 2. + length / 2., rot); - auto zMoveURDB = new TGeoCombiTrans(0, 0, -databackbone::length / 2. + length / 2. - constants::pixelarray::length / 2., rot); + auto zMoveUR1 = new TGeoCombiTrans(0, 0, constants::tile::length + (length / 2.), rot); + auto zMoveUR2 = new TGeoCombiTrans(0, 0, (constants::tile::length * 2.) + (length / 2.), rot); + auto zMoveURDB = new TGeoCombiTrans(0, 0, (-databackbone::length / 2.) + (length / 2.) - (constants::pixelarray::length / 2.), rot); // Lets attach the tiles to the QS. mRSU->AddNode(mTile, nCopyRSU++, zMoveUR0); mRSU->AddNode(mTile, nCopyRSU++, zMoveUR1); @@ -225,9 +225,9 @@ void ITS3Layer::createSegment() mSegment = new TGeoVolumeAssembly(its3TGeo::getITS3SegmentPattern(mNLayer)); mSegment->VisibleDaughters(); - for (size_t i{0}; i < nRSUs; ++i) { - auto zMove = new TGeoTranslation(0, 0, +i * constants::rsu::length + constants::rsu::databackbone::length + constants::pixelarray::length / 2.); - mSegment->AddNode(mRSU, i, zMove); + for (unsigned int i{0}; i < nRSUs; ++i) { + auto zMove = new TGeoTranslation(0, 0, (i * constants::rsu::length) + constants::rsu::databackbone::length + (constants::pixelarray::length / 2.)); + mSegment->AddNode(mRSU, (int)i, zMove); } // LEC @@ -242,7 +242,7 @@ void ITS3Layer::createSegment() mSegment->AddNode(lecVol, 0, zMoveLEC); // REC; reuses lecPhi1,2 - auto zMoveREC = new TGeoTranslation(0, 0, nRSUs * constants::rsu::length + rec::length / 2.); + auto zMoveREC = new TGeoTranslation(0, 0, (nRSUs * constants::rsu::length) + (rec::length / 2.)); auto rec = new TGeoTubeSeg(mRmin, mRmax, rec::length / 2., lecPhi1, lecPhi2); auto recVol = new TGeoVolume(Form("rec%d", mNLayer), rec, mSilicon); @@ -266,11 +266,11 @@ void ITS3Layer::createChip() auto phiOffset = constants::segment::width / mR * o2m::Rad2Deg; for (unsigned int i{0}; i < constants::nSegments[mNLayer]; ++i) { auto rot = new TGeoRotation(Form("its3PhiSegmentOffset_%d_%d", mNLayer, i), 0, 0, phiOffset * i); - mChip->AddNode(mSegment, i, rot); + mChip->AddNode(mSegment, (int)i, rot); } // Add metal stack positioned radially outward - auto zMoveMetal = new TGeoTranslation(0, 0, constants::metalstack::length / 2. - constants::segment::lec::length); + auto zMoveMetal = new TGeoTranslation(0, 0, (constants::metalstack::length / 2.) - constants::segment::lec::length); auto metal = new TGeoTubeSeg(mRmax, mRmax + constants::metalstack::thickness, constants::metalstack::length / 2., 0, constants::nSegments[mNLayer] * phiOffset); auto metalVol = new TGeoVolume(Form("metal%d", mNLayer), metal, mCopper); metalVol->SetLineColor(constants::metalstack::color); @@ -296,7 +296,7 @@ void ITS3Layer::createCarbonForm() dRadius = constants::carbonfoam::thicknessOuterFoam; // TODO: lack of carbon foam radius for layer 2, use 0.7 cm as a temporary value } double phiSta = edgeBetwChipAndFoam / (0.5 * constants::radii[mNLayer + 1] + constants::radii[mNLayer]) * o2m::Rad2Deg; - double phiEnd = (constants::nSegments[mNLayer] * constants::segment::width) / constants::radii[mNLayer] * o2m::Rad2Deg - phiSta; + double phiEnd = ((constants::nSegments[mNLayer] * constants::segment::width) / constants::radii[mNLayer] * o2m::Rad2Deg) - phiSta; double phiLongeronsCover = longeronsWidth / (0.5 * constants::radii[mNLayer + 1] + constants::radii[mNLayer]) * o2m::Rad2Deg; // H-rings foam @@ -308,35 +308,37 @@ void ITS3Layer::createCarbonForm() HringCVol->SetLineColor(color); auto HringAVol = new TGeoVolume(Form("hringA%d", mNLayer), HringAWithHoles, mCarbon); HringAVol->SetLineColor(color); - auto zMoveHringC = new TGeoTranslation(0, 0, -constants::segment::lec::length + HringLength / 2.); - auto zMoveHringA = new TGeoTranslation(0, 0, -constants::segment::lec::length + HringLength / 2. + constants::segment::length - HringLength); + auto zMoveHringC = new TGeoTranslation(0, 0, -constants::segment::lec::length + (HringLength / 2.)); + auto zMoveHringA = new TGeoTranslation(0, 0, -constants::segment::lec::length + (HringLength / 2.) + constants::segment::length - HringLength); // Longerons are made by same material + // added separately to make navigation faster [[maybe_unused]] auto longeronR = new TGeoTubeSeg(Form("longeronR%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiSta, phiSta + phiLongeronsCover); [[maybe_unused]] auto longeronL = new TGeoTubeSeg(Form("longeronL%d", mNLayer), mRmax, mRmax + dRadius, longeronsLength / 2., phiEnd - phiLongeronsCover, phiEnd); - TString nameLongerons = Form("longeronR%d + longeronL%d", mNLayer, mNLayer); - auto longerons = new TGeoCompositeShape(nameLongerons); - auto longeronsVol = new TGeoVolume(Form("longerons%d", mNLayer), longerons, mCarbon); - longeronsVol->SetLineColor(color); - auto zMoveLongerons = new TGeoTranslation(0, 0, -constants::segment::lec::length + constants::segment::length / 2.); + auto longeronRVol = new TGeoVolume(Form("longeronR%d", mNLayer), longeronR, mCarbon); + longeronRVol->SetLineColor(color); + auto longeronLVol = new TGeoVolume(Form("longeronL%d", mNLayer), longeronL, mCarbon); + longeronLVol->SetLineColor(color); + auto zMoveLongerons = new TGeoTranslation(0, 0, -constants::segment::lec::length + (constants::segment::length / 2.)); mCarbonForm->AddNode(HringCVol, 0, zMoveHringC); mCarbonForm->AddNode(HringAVol, 0, zMoveHringA); - mCarbonForm->AddNode(longeronsVol, 0, zMoveLongerons); + mCarbonForm->AddNode(longeronRVol, 0, zMoveLongerons); + mCarbonForm->AddNode(longeronLVol, 0, zMoveLongerons); mCarbonForm->AddNode(mChip, 0); } -TGeoCompositeShape* ITS3Layer::getHringShape(TGeoTubeSeg* Hring) +TGeoCompositeShape* ITS3Layer::getHringShape(TGeoTubeSeg* Hring) const { // Function to dig holes in H-rings using namespace constants::carbonfoam; double stepPhiHoles = (Hring->GetPhi2() - Hring->GetPhi1()) / (nHoles[mNLayer]); - double phiHolesSta = Hring->GetPhi1() + stepPhiHoles / 2.; + double phiHolesSta = Hring->GetPhi1() + (stepPhiHoles / 2.); double radiusHring = 0.5 * (Hring->GetRmin() + Hring->GetRmax()); TGeoCompositeShape* HringWithHoles = nullptr; TString nameAllHoles = ""; for (int iHoles = 0; iHoles < nHoles[mNLayer]; iHoles++) { - double phiHole = phiHolesSta + stepPhiHoles * iHoles; + double phiHole = phiHolesSta + (stepPhiHoles * iHoles); TString nameHole = Form("hole_%d_%d", iHoles, mNLayer); [[maybe_unused]] auto hole = new TGeoTube(nameHole, 0, radiusHoles[mNLayer], 3 * Hring->GetDz()); // move hole to the hring radius @@ -376,9 +378,7 @@ void ITS3Layer::createLayerImpl() void ITS3Layer::buildPartial(TGeoVolume* motherVolume, TGeoMatrix* mat, BuildLevel level, bool createMaterials) { - if (!mBuilt) { - getMaterials(createMaterials); - } + getMaterials(createMaterials); switch (level) { case BuildLevel::kPixelArray: createPixelArray(); diff --git a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx index 004c3f6097167..60fe4fabfe481 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx @@ -12,7 +12,7 @@ #include "ITS3Workflow/RecoWorkflow.h" #include "ITS3Workflow/ClustererSpec.h" #include "ITS3Workflow/TrackerSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" #include "ITS3Workflow/DigitReaderSpec.h" #include "GPUWorkflow/GPUWorkflowSpec.h" @@ -40,7 +40,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } if (trmode != its::TrackingMode::Off) { diff --git a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h b/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h deleted file mode 100644 index d5bc6631575af..0000000000000 --- a/Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h +++ /dev/null @@ -1,1297 +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 FwdDCAFitterN.h -/// \brief Defintions for N-prongs secondary vertex fit -/// \author ruben.shahoyan@cern.ch, adapted from central barrel to fwd rapidities by Rita Sadek, rita.sadek@cern.ch -/// For the formulae derivation see /afs/cern.ch/user/s/shahoian/public/O2/DCAFitter/DCAFitterN.pdf - -#ifndef _ALICEO2_DCA_FWDFITTERN_ -#define _ALICEO2_DCA_FWDFITTERN_ -#include -#include "MathUtils/Cartesian.h" -#include "ReconstructionDataFormats/TrackFwd.h" -#include "ReconstructionDataFormats/Track.h" -#include "ReconstructionDataFormats/HelixHelper.h" -#include -#include "DetectorsBase/Propagator.h" -#include "DetectorsBase/GeometryManager.h" - -namespace o2 -{ -namespace vertexing -{ - -///__________________________________________________________________________________ -///< Fwd Inverse cov matrix (augmented by a dummy Z error) of the point defined by the track -struct FwdTrackCovI { - float sxx, syy, sxy, szz; - - FwdTrackCovI(const o2::track::TrackParCovFwd& trc, float zerrFactor = 1.) { set(trc, zerrFactor); } - FwdTrackCovI() = default; - void set(const o2::track::TrackParCovFwd& trc, float zerrFactor = 1) - { - float cxx = trc.getSigma2X(), cyy = trc.getSigma2Y(), cxy = trc.getSigmaXY(), czz = cyy * zerrFactor; - float detXY = cxx * cyy - cxy * cxy; - if (detXY > 0.) { - auto detXYI = 1. / detXY; - sxx = cyy * detXYI; - syy = cxx * detXYI; - sxy = -cxy * detXYI; - szz = 1. / czz; - } else { - throw std::runtime_error("invalid track covariance"); - } - } -}; - -///__________________________________________________________________________ -///< Fwd derivative (up to 2) of the TrackParam position over its running param Z -struct FwdTrackDeriv { - float dxdz, dydz, d2xdz2, d2ydz2; - FwdTrackDeriv() = default; - FwdTrackDeriv(const o2::track::TrackParFwd& trc, float bz) { set(trc, bz); } - void set(const o2::track::TrackParFwd& trc, float bz) - { - float snp = trc.getSnp(), csp = std::sqrt((1. - snp) * (1. + snp)), cspI = 1. / csp, crv2c = trc.getCurvature(bz), tgl = trc.getTanl(), tglI = 1. / tgl; - if (crv2c == 0.) { - crv2c = (trc.getCharge()) * 0.3 * bz * (-1e-3); - } - - dxdz = csp * tglI; - dydz = snp * tglI; - d2xdz2 = crv2c * snp * tglI * tglI; - d2ydz2 = -crv2c * csp * tglI * tglI; - } -}; - -template -class FwdDCAFitterN -{ - static constexpr double NMin = 2; - static constexpr double NMax = 4; - static constexpr double NInv = 1. / N; - static constexpr int MAXHYP = 2; - static constexpr float ZerrFactor = 5.; // factor for conversion of track covXX to dummy covZZ - using Track = o2::track::TrackParCovFwd; - using TrackAuxPar = o2::track::TrackAuxPar; - using CrossInfo = o2::track::CrossInfo; - using Vec3D = ROOT::Math::SVector; - using VecND = ROOT::Math::SVector; - using MatSym3D = ROOT::Math::SMatrix>; - using MatStd3D = ROOT::Math::SMatrix>; - using MatSymND = ROOT::Math::SMatrix>; - using MatStdND = ROOT::Math::SMatrix>; - using SMatrix55 = ROOT::Math::SMatrix>; - using TrackCoefVtx = MatStd3D; - using ArrTrack = std::array; // container for prongs (tracks) at single vertex cand. - using ArrTrackCovI = std::array; // container for inv.cov.matrices at single vertex cand. - using ArrTrCoef = std::array; // container of TrackCoefVtx coefficients at single vertex cand. - using ArrTrDer = std::array; // container of Track 1st and 2nd derivative over their Z param - using ArrTrPos = std::array; // container of Track positions - - public: - static constexpr int getNProngs() { return N; } - - FwdDCAFitterN() = default; - FwdDCAFitterN(float bz, bool useAbsDCA, bool prop2DCA) : mBz(bz), mUseAbsDCA(useAbsDCA), mPropagateToPCA(prop2DCA) - { - static_assert(N >= NMin && N <= NMax, "N prongs outside of allowed range"); - } - - //========================================================================= - ///< return PCA candidate, by default best on is provided (no check for the index validity) - const Vec3D& getPCACandidate(int cand = 0) const { return mPCA[mOrder[cand]]; } - const auto getPCACandidatePos(int cand = 0) const - { - const auto& vd = mPCA[mOrder[cand]]; - return std::array{float(vd[0]), float(vd[1]), float(vd[2])}; - } - - ///< return Chi2 at PCA candidate (no check for its validity) - float getChi2AtPCACandidate(int cand = 0) const { return mChi2[mOrder[cand]]; } - - ///< prepare copies of tracks at the V0 candidate (no check for the candidate validity) - /// must be called before getTrack(i,cand) query - bool FwdpropagateTracksToVertex(int cand = 0); - - ///< check if propagation of tracks to candidate vertex was done - bool isPropagateTracksToVertexDone(int cand = 0) const { return mTrPropDone[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) - { - if (!mTrPropDone[mOrder[cand]]) { - throw std::runtime_error("propagateTracksToVertex was not called yet"); - } - return mCandTr[mOrder[cand]][i]; - } - - ///< calculate on the fly track param (no cov mat) at candidate - o2::track::TrackParFwd FwdgetTrackParamAtPCA(int i, int cand = 0) const; - - MatSym3D calcPCACovMatrix(int cand = 0) const; - - std::array calcPCACovMatrixFlat(int cand = 0) const - { - auto m = calcPCACovMatrix(cand); - return {float(m(0, 0)), float(m(1, 0)), float(m(1, 1)), float(m(2, 0)), float(m(2, 1)), float(m(2, 2))}; - } - - const Track* getOrigTrackPtr(int i) const { return mOrigTrPtr[i]; } - - ///< return number of iterations during minimization (no check for its validity) - int getNIterations(int cand = 0) const { return mNIters[mOrder[cand]]; } - void setPropagateToPCA(bool v = true) { mPropagateToPCA = v; } - void setMaxIter(int n = 60) { mMaxIter = n > 2 ? n : 2; } - void setMaxR(float r = 200.) { mMaxR2 = r * r; } - void setMaxDXIni(float d = 4.) { mMaxDXIni = d; } - void setMaxChi2(float chi2 = 999.) { mMaxChi2 = chi2; } - void setBz(float bz) { mBz = std::abs(bz) > o2::constants::math::Almost0 ? bz : 0.f; } - void setMinParamChange(float x = 1e-3) { mMinParamChange = x > 1e-4 ? x : 1.e-4; } - void setMinRelChi2Change(float r = 0.9) { mMinRelChi2Change = r > 0.1 ? r : 999.; } - void setUseAbsDCA(bool v) { mUseAbsDCA = v; } - void setMatLUT(const o2::base::MatLayerCylSet* m) - { - mMatLUT = m; - mUseMatBudget = true; - } - void setTGeoMat(bool v = true) { mTGeoFallBackAllowed = v; } - void setMaxDistance2ToMerge(float v) { mMaxDist2ToMergeSeeds = v; } - - int getNCandidates() const { return mCurHyp; } - int getMaxIter() const { return mMaxIter; } - float getMaxR() const { return std::sqrt(mMaxR2); } - float getMaxDXIni() const { return mMaxDXIni; } - float getMaxChi2() const { return mMaxChi2; } - float getMinParamChange() const { return mMinParamChange; } - float getBz() const { return mBz; } - double getK(double b) const { return std::abs(o2::constants::math::B2C * b); } - double getHz(double b) const { return std::copysign(1, b); } - - float getMaxDistance2ToMerge() const { return mMaxDist2ToMergeSeeds; } - bool getUseAbsDCA() const { return mUseAbsDCA; } - bool getPropagateToPCA() const { return mPropagateToPCA; } - - template - int process(const Tr&... args); - void print() const; - - protected: - bool FwdcalcPCACoefs(); - bool FwdcalcInverseWeight(); - void FwdcalcResidDerivatives(); - void FwdcalcResidDerivativesNoErr(); - void FwdcalcChi2Derivatives(); - void FwdcalcChi2DerivativesNoErr(); - void FwdcalcPCA(); - void FwdcalcPCANoErr(); - void FwdcalcTrackResiduals(); - void calcTrackDerivatives(); - float findZatXY(int cand = 0); - void findZatXY_mid(int cand = 0); - void findZatXY_lineApprox(int cand = 0); - void findZatXY_quad(int cand = 0); - void findZatXY_linear(int cand = 0); - double FwdcalcChi2() const; - double FwdcalcChi2NoErr() const; - bool FwdcorrectTracks(const VecND& corrZ); - bool minimizeChi2(); - bool minimizeChi2NoErr(); - bool roughDXCut() const; - bool closerToAlternative() const; - static double getAbsMax(const VecND& v); - bool propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const; - - ///< track param positions at V0 candidate (no check for the candidate validity) - const Vec3D& getTrackPos(int i, int cand = 0) const { return mTrPos[mOrder[cand]][i]; } - - ///< track Z-param at V0 candidate (no check for the candidate validity) - float getTrackZ(int i, int cand = 0) const { return getTrackPos(i, cand)[2]; } - - MatStd3D getTrackRotMatrix(int i) const // generate 3D matrix for track rotation to global frame - // no rotation for fwd: mat=I - { - MatStd3D mat; - mat(0, 0) = 1; - mat(1, 1) = 1; - mat(2, 2) = 1; - return mat; - } - - MatSym3D getTrackCovMatrix(int i, int cand = 0) const // generate covariance matrix of track position, adding fake Z error - { - const auto& trc = mCandTr[mOrder[cand]][i]; - MatSym3D mat; - mat(0, 0) = trc.getSigma2X(); - mat(1, 1) = trc.getSigma2Y(); - mat(1, 0) = trc.getSigmaXY(); - mat(2, 2) = trc.getSigma2Y() * ZerrFactor; - return mat; - } - - void assign(int) {} - template - void assign(int i, const T& t, const Tr&... args) - { - static_assert(std::is_convertible(), "Wrong track type"); - mOrigTrPtr[i] = &t; - assign(i + 1, args...); - } - - void clear() - { - mCurHyp = 0; - mAllowAltPreference = true; - } - - static void setTrackPos(Vec3D& pnt, const Track& tr) - { - pnt[0] = tr.getX(); - pnt[1] = tr.getY(); - pnt[2] = tr.getZ(); - } - - private: - // vectors of 1st derivatives of track local residuals over Z parameters - std::array, N> mDResidDz; - // vectors of 1nd derivatives of track local residuals over Z parameters - std::array, N> mD2ResidDz2; - VecND mDChi2Dz; // 1st derivatives of chi2 over tracks Z params - MatSymND mD2Chi2Dz2; // 2nd derivatives of chi2 over tracks Z params (symmetric matrix) - - std::array mOrigTrPtr; - std::array mTrAux; // Aux track info for each track at each cand. vertex - CrossInfo mCrossings; // info on track crossing - - std::array mTrcEInv; // errors for each track at each cand. vertex - std::array mCandTr; // tracks at each cond. vertex (Note: Errors are at seed XY point) - std::array mTrCFVT; // TrackCoefVtx for each track at each cand. vertex - std::array mTrDer; // Track derivativse - std::array mTrPos; // Track positions - std::array mTrRes; // Track residuals - 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 - MatSym3D mWeightInv; // inverse weight of single track, [sum{M^T E M}]^-1 in EQ.T - std::array mOrder{0}; - int mCurHyp = 0; - int mCrossIDCur = 0; - int mCrossIDAlt = -1; - bool mAllowAltPreference = true; // if the fit converges to alternative PCA seed, abandon the current one - bool mUseAbsDCA = false; // use abs. distance minimization rather than chi2 - bool mPropagateToPCA = true; // create tracks version propagated to PCA - bool mUseMatBudget = false; // include MCS effects in track propagation - bool mTGeoFallBackAllowed = true; // use TGeo for precise estimate of mat. budget - int mMaxIter = 60; // max number of iterations - float mBz = 0; // bz field, to be set by user - float mMaxR2 = 200. * 200.; // reject PCA's above this radius - float mMaxDXIni = 4.; // reject (if>0) PCA candidate if tracks DZ exceeds threshold - float mMinParamChange = 1e-5; // stop iterations if largest change of any X is smaller than this - float mMinRelChi2Change = 0.98; // stop iterations is chi2/chi2old > this - float mMaxChi2 = 100; // abs cut on chi2 or abs distance - float mMaxDist2ToMergeSeeds = 1.; // merge 2 seeds to their average if their distance^2 is below the threshold - const o2::base::MatLayerCylSet* mMatLUT = nullptr; // use to compute material budget to include MCS effects - - ClassDefNV(FwdDCAFitterN, 1); -}; - -///_________________________________________________________________________ -template -template -int FwdDCAFitterN::process(const Tr&... args) -{ - - static_assert(sizeof...(args) == N, "incorrect number of input tracks"); - assign(0, args...); - clear(); - - for (int i = 0; i < N; i++) { - mTrAux[i].set(*mOrigTrPtr[i], mBz); - } - - if (!mCrossings.set(mTrAux[0], *mOrigTrPtr[0], mTrAux[1], *mOrigTrPtr[1])) { // even for N>2 it should be enough to test just 1 loop - return 0; // no crossing - } - - if (mCrossings.nDCA == MAXHYP) { // if there are 2 candidates - auto dst2 = (mCrossings.xDCA[0] - mCrossings.xDCA[1]) * (mCrossings.xDCA[0] - mCrossings.xDCA[1]) + - (mCrossings.yDCA[0] - mCrossings.yDCA[1]) * (mCrossings.yDCA[0] - mCrossings.yDCA[1]); - - if (dst2 < mMaxDist2ToMergeSeeds) { - mCrossings.nDCA = 1; - mCrossings.xDCA[0] = 0.5 * (mCrossings.xDCA[0] + mCrossings.xDCA[1]); - mCrossings.yDCA[0] = 0.5 * (mCrossings.yDCA[0] + mCrossings.yDCA[1]); - } - } - - // check all crossings - for (int ic = 0; ic < mCrossings.nDCA; ic++) { - // check if radius is acceptable - if (mCrossings.xDCA[ic] * mCrossings.xDCA[ic] + mCrossings.yDCA[ic] * mCrossings.yDCA[ic] > mMaxR2) { - continue; - } - - mCrossIDCur = ic; - mCrossIDAlt = (mCrossings.nDCA == 2 && mAllowAltPreference) ? 1 - ic : -1; // works for max 2 crossings - mNIters[mCurHyp] = 0; - mTrPropDone[mCurHyp] = false; - mChi2[mCurHyp] = -1.; - - findZatXY_mid(mCurHyp); - - if (mUseAbsDCA ? minimizeChi2NoErr() : minimizeChi2()) { - mOrder[mCurHyp] = mCurHyp; - if (mPropagateToPCA && !FwdpropagateTracksToVertex(mCurHyp)) { - continue; - } - mCurHyp++; - } - } - - for (int i = mCurHyp; i--;) { // order in quality - for (int j = i; j--;) { - if (mChi2[mOrder[i]] < mChi2[mOrder[j]]) { - std::swap(mOrder[i], mOrder[j]); - } - } - } - - return mCurHyp; -} - -//__________________________________________________________________________ -template -bool FwdDCAFitterN::FwdcalcPCACoefs() -{ - //< calculate Ti matrices for global vertex decomposition to V = sum_{0 -bool FwdDCAFitterN::FwdcalcInverseWeight() -{ - //< calculate [sum_{0 -void FwdDCAFitterN::FwdcalcResidDerivatives() -{ - //< calculate matrix of derivatives for weighted chi2: residual i vs parameter Z of track j - MatStd3D matMT; - for (int i = N; i--;) { // residual being differentiated - // const auto& taux = mTrAux[i]; - for (int j = N; j--;) { // track over which we differentiate - const auto& matT = mTrCFVT[mCurHyp][j]; // coefficient matrix for track J - const auto& trDz = mTrDer[mCurHyp][j]; // track point derivs over track Z param - auto& dr1 = mDResidDz[i][j]; - auto& dr2 = mD2ResidDz2[i][j]; - // calculate M_i^transverse * T_j , M_i^transverse=I -> MT=T - matMT[0][0] = matT[0][0]; - matMT[0][1] = matT[0][1]; - matMT[0][2] = matT[0][2]; - matMT[1][0] = matT[1][0]; - matMT[1][1] = matT[1][1]; - matMT[1][2] = matT[1][2]; - matMT[2][0] = matT[2][0]; - matMT[2][1] = matT[2][1]; - matMT[2][2] = matT[2][2]; - - // calculate DResid_i/Dz_j = (delta_ij - M_i^tr * T_j) * DTrack_k/Dz_k - dr1[0] = -(matMT[0][0] * trDz.dxdz + matMT[0][1] * trDz.dydz + matMT[0][2]); - dr1[1] = -(matMT[1][0] * trDz.dxdz + matMT[1][1] * trDz.dydz + matMT[1][2]); - dr1[2] = -(matMT[2][0] * trDz.dxdz + matMT[2][1] * trDz.dydz + matMT[2][2]); - - // calculate D2Resid_I/(Dz_J Dz_K) = (delta_ijk - M_i^tr * T_j * delta_jk) * D2Track_k/dz_k^2 - dr2[0] = -(matMT[0][1] * trDz.d2ydz2 + matMT[0][0] * trDz.d2xdz2); - dr2[1] = -(matMT[1][1] * trDz.d2ydz2 + matMT[1][0] * trDz.d2xdz2); - dr2[2] = -(matMT[2][1] * trDz.d2ydz2 + matMT[2][0] * trDz.d2xdz2); - - if (i == j) { - dr1[0] += trDz.dxdz; - dr1[1] += trDz.dydz; - dr1[2] += 1.; - - dr2[0] += trDz.d2xdz2; - dr2[1] += trDz.d2ydz2; - } - } // track over which we differentiate - } // residual being differentiated -} - -//__________________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcResidDerivativesNoErr() -{ - //< calculate matrix of derivatives for absolute distance chi2: residual i vs parameter Z of track j - constexpr double NInv1 = 1. - NInv; // profit from Rii = I/Ninv - for (int i = N; i--;) { // residual being differentiated - const auto& trDzi = mTrDer[mCurHyp][i]; // track point derivs over track Z param - auto& dr1ii = mDResidDz[i][i]; - auto& dr2ii = mD2ResidDz2[i][i]; - - dr1ii[0] = NInv1 * trDzi.dxdz; - dr1ii[1] = NInv1 * trDzi.dydz; - dr1ii[2] = NInv1; - - dr2ii[0] = NInv1 * trDzi.d2xdz2; - dr2ii[1] = NInv1 * trDzi.d2ydz2; - dr2ii[2] = 0; - - for (int j = i; j--;) { // track over which we differentiate - auto& dr1ij = mDResidDz[i][j]; - auto& dr1ji = mDResidDz[j][i]; - const auto& trDzj = mTrDer[mCurHyp][j]; // track point derivs over track Z param - - // calculate DResid_i/Dz_j = (delta_ij - R_ij) * DTrack_j/Dz_j for j -void FwdDCAFitterN::FwdcalcChi2Derivatives() -{ - //< calculate 1st and 2nd derivatives of wighted DCA (chi2) over track parameters Z - std::array, N> covIDrDz; // tempory vectors of covI_j * dres_j/dz_i - - // chi2 1st derivative - for (int i = N; i--;) { - auto& dchi1 = mDChi2Dz[i]; // DChi2/Dz_i = sum_j { res_j * covI_j * Dres_j/Dz_i } - dchi1 = 0; - for (int j = N; j--;) { - const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j - const auto& covI = mTrcEInv[mCurHyp][j]; // inverse cov matrix of track j - const auto& dr1 = mDResidDz[j][i]; // vector of j-th residuals 1st derivative over Z param of track i - auto& cidr = covIDrDz[i][j]; // vector covI_j * dres_j/dz_i, save for 2nd derivative calculation - cidr[0] = covI.sxx * dr1[0] + covI.sxy * dr1[1]; - cidr[1] = covI.sxy * dr1[0] + covI.syy * dr1[1]; - cidr[2] = covI.szz * dr1[2]; - - dchi1 += ROOT::Math::Dot(res, cidr); - } - } - - // chi2 2nd derivative - for (int i = N; i--;) { - for (int j = i + 1; j--;) { // symmetric matrix - auto& dchi2 = mD2Chi2Dz2[i][j]; // D2Chi2/Dz_i/Dz_j = sum_k { Dres_k/Dz_j * covI_k * Dres_k/Dz_i + res_k * covI_k * D2res_k/Dz_i/Dz_j } - dchi2 = 0; - for (int k = N; k--;) { - const auto& dr1j = mDResidDz[k][j]; // vector of k-th residuals 1st derivative over Z param of track j - const auto& cidrkj = covIDrDz[i][k]; // vector covI_k * dres_k/dz_i - dchi2 += ROOT::Math::Dot(dr1j, cidrkj); - if (k == j) { - const auto& res = mTrRes[mCurHyp][k]; // vector of residuals of track k - const auto& covI = mTrcEInv[mCurHyp][k]; // inverse cov matrix of track k - const auto& dr2ij = mD2ResidDz2[k][j]; // vector of k-th residuals 2nd derivative over Z params of track j - dchi2 += res[0] * (covI.sxx * dr2ij[0] + covI.sxy * dr2ij[1]) + res[1] * (covI.sxy * dr2ij[0] + covI.syy * dr2ij[1]) + res[2] * covI.szz * dr2ij[2]; - } - } - } - } -} - -//__________________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcChi2DerivativesNoErr() -{ - //< calculate 1st and 2nd derivatives of abs DCA (chi2) over track parameters Z - for (int i = N; i--;) { - auto& dchi1 = mDChi2Dz[i]; // DChi2/Dz_i = sum_j { res_j * Dres_j/Dz_i } - dchi1 = 0; // chi2 1st derivative - for (int j = N; j--;) { - const auto& res = mTrRes[mCurHyp][j]; // vector of residuals of track j - const auto& dr1 = mDResidDz[j][i]; // vector of j-th residuals 1st derivative over Z param of track i - dchi1 += ROOT::Math::Dot(res, dr1); - if (i >= j) { // symmetrix matrix - // chi2 2nd derivative - auto& dchi2 = mD2Chi2Dz2[i][j]; // D2Chi2/Dz_i/Dz_j = sum_k { Dres_k/Dz_j * covI_k * Dres_k/Dz_i + res_k * covI_k * D2res_k/Dz_i/Dz_j } - dchi2 = ROOT::Math::Dot(mTrRes[mCurHyp][i], mD2ResidDz2[i][j]); - for (int k = N; k--;) { - dchi2 += ROOT::Math::Dot(mDResidDz[k][i], mDResidDz[k][j]); - } - } - } - } -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcPCA() -{ - // calculate point of closest approach for N prongs - // calculating V = sum (Ti*Pi) - mPCA[mCurHyp] = mTrCFVT[mCurHyp][N - 1] * mTrPos[mCurHyp][N - 1]; - for (int i = N - 1; i--;) { - mPCA[mCurHyp] += mTrCFVT[mCurHyp][i] * mTrPos[mCurHyp][i]; - } -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcPCANoErr() -{ - // calculate point of closest approach for N prongs w/o errors - auto& pca = mPCA[mCurHyp]; - - pca[0] = mTrPos[mCurHyp][N - 1][0]; - pca[1] = mTrPos[mCurHyp][N - 1][1]; - pca[2] = mTrPos[mCurHyp][N - 1][2]; - - for (int i = N - 1; i--;) { - pca[0] += mTrPos[mCurHyp][i][0]; - pca[1] += mTrPos[mCurHyp][i][1]; - pca[2] += mTrPos[mCurHyp][i][2]; - } - pca[0] *= NInv; - pca[1] *= NInv; - pca[2] *= NInv; -} - -//___________________________________________________________________ -template -ROOT::Math::SMatrix> FwdDCAFitterN::calcPCACovMatrix(int cand) const -{ - // calculate covariance matrix for the point of closest approach - MatSym3D covm; - for (int i = N; i--;) { - covm += ROOT::Math::Similarity(mUseAbsDCA ? getTrackRotMatrix(i) : mTrCFVT[mOrder[cand]][i], getTrackCovMatrix(i, cand)); - } - return covm; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::FwdcalcTrackResiduals() -{ - // calculate residuals, res = Pi - V - Vec3D vtxLoc; - for (int i = N; i--;) { - mTrRes[mCurHyp][i] = mTrPos[mCurHyp][i]; - vtxLoc = mPCA[mCurHyp]; - mTrRes[mCurHyp][i] -= vtxLoc; - } -} - -//___________________________________________________________________ -template -inline void FwdDCAFitterN::calcTrackDerivatives() -{ - // calculate track derivatives over Z param - for (int i = N; i--;) { - mTrDer[mCurHyp][i].set(mCandTr[mCurHyp][i], mBz); - } -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::FwdcalcChi2() const -{ - // calculate current chi2 - double chi2 = 0; - for (int i = N; i--;) { - const auto& res = mTrRes[mCurHyp][i]; - const auto& covI = mTrcEInv[mCurHyp][i]; - chi2 += res[0] * res[0] * covI.sxx + res[1] * res[1] * covI.syy + res[2] * res[2] * covI.szz + 2. * res[0] * res[1] * covI.sxy; - } - return chi2; -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::FwdcalcChi2NoErr() const -{ - // calculate current chi2 of abs. distance minimization - double chi2 = 0; - for (int i = N; i--;) { - const auto& res = mTrRes[mCurHyp][i]; - chi2 += res[0] * res[0] + res[1] * res[1] + res[2] * res[2]; - } - return chi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::FwdcorrectTracks(const VecND& corrZ) -{ - // propagate tracks to updated Z - for (int i = N; i--;) { - const auto& trDer = mTrDer[mCurHyp][i]; - auto dz2h = 0.5 * corrZ[i] * corrZ[i]; - mTrPos[mCurHyp][i][0] -= trDer.dxdz * corrZ[i] - dz2h * trDer.d2xdz2; - mTrPos[mCurHyp][i][1] -= trDer.dydz * corrZ[i] - dz2h * trDer.d2ydz2; - mTrPos[mCurHyp][i][2] -= corrZ[i]; - } - - return true; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::FwdpropagateTracksToVertex(int icand) -{ - // propagate on z axis to vertex - int ord = mOrder[icand]; - if (mTrPropDone[ord]) { - return true; - } - const Vec3D& pca = mPCA[ord]; - std::array covMatrixPCA = calcPCACovMatrixFlat(ord); - std::array cov = {covMatrixPCA[0], covMatrixPCA[2]}; - for (int i = N; i--;) { - mCandTr[ord][i] = *mOrigTrPtr[i]; // fetch the track again, as mCandTr might have been propagated w/o errors - auto& trc = mCandTr[ord][i]; - const std::array p = {(float)pca[0], (float)pca[1], (float)pca[2]}; - if (!propagateToVtx(trc, p, cov)) { - return false; - } - } - - mTrPropDone[ord] = true; - return true; -} - -//___________________________________________________________________ -template -float FwdDCAFitterN::findZatXY(int mCurHyp) // Between 2 tracks -{ - - double step = 0.001; // initial step - double startPoint = 20.; // first MFT disk - - double z[2] = {startPoint, startPoint}; - double newX[2], newY[2]; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double dstXY[2][3] = {{999., 999., 999.}, {999., 999., 999.}}; - - double Z[2]; - double finalZ[2]; - - double newDstXY; - - for (int i = 0; i < 2; i++) { - - while (z[i] > -10) { - - mCandTr[mCurHyp][i].propagateParamToZquadratic(z[i], mBz); - newX[i] = mCandTr[mCurHyp][i].getX(); - newY[i] = mCandTr[mCurHyp][i].getY(); - - newDstXY = std::sqrt((newX[i] - X) * (newX[i] - X) + - (newY[i] - Y) * (newY[i] - Y)); - - // Update points - dstXY[i][0] = dstXY[i][1]; - dstXY[i][1] = dstXY[i][2]; - dstXY[i][2] = newDstXY; - - if (dstXY[i][2] > dstXY[i][1] && dstXY[i][1] < dstXY[i][0]) { - finalZ[i] = z[i] + step; - break; - } - - z[i] -= step; - } - } - - float rez = 0.5 * (finalZ[0] + finalZ[1]); - return rez; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_mid(int mCurHyp) -{ - // look into dXY of T0 - T1 between 2 points(0,40cm); the one with the highest dXY is moved to mid - - double startPoint = -40.; - double endPoint = 50.; - double midPoint = 0.5 * (startPoint + endPoint); - - double z[2][2] = {{startPoint, endPoint}, {startPoint, endPoint}}; // z for tracks 0/1 on starting poing and endpoint - - double DeltaZ = std::abs(endPoint - startPoint); - - double newX[2][2]; - double newY[2][2]; - - double epsilon = 0.0001; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double finalZ; - - double dstXY[2]; // 0 -> distance btwn both tracks at startPoint - - while (DeltaZ > epsilon) { - - midPoint = 0.5 * (startPoint + endPoint); - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateParamToZquadratic(startPoint, mBz); - newX[i][0] = mCandTr[mCurHyp][i].getX(); - newY[i][0] = mCandTr[mCurHyp][i].getY(); - - mCandTr[mCurHyp][i].propagateParamToZquadratic(endPoint, mBz); - newX[i][1] = mCandTr[mCurHyp][i].getX(); - newY[i][1] = mCandTr[mCurHyp][i].getY(); - } - - dstXY[0] = (newX[0][0] - newX[1][0]) * (newX[0][0] - newX[1][0]) + - (newY[0][0] - newY[1][0]) * (newY[0][0] - newY[1][0]); - - dstXY[1] = (newX[0][1] - newX[1][1]) * (newX[0][1] - newX[1][1]) + - (newY[0][1] - newY[1][1]) * (newY[0][1] - newY[1][1]); - - DeltaZ = std::abs(endPoint - startPoint); - - if (DeltaZ < epsilon) { - finalZ = 0.5 * (startPoint + endPoint); - break; - } - - // chose new start and end Point according to the smallest D_XY - if (dstXY[1] > dstXY[0]) { - endPoint = midPoint; - } else { - startPoint = midPoint; - } - } - - mPCA[mCurHyp][2] = finalZ; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_lineApprox(int mCurHyp) -{ - // approx method: z=(b-b')/(a'-a) -> tracks to lines with y0,1=az0,1+b for each track (in YZ and XZ plane) - - double startPoint = 1.; - double endPoint = 50.; // first disk - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double y[2][2]; // Y00: y track 0 at point 0; Y01: y track 0 at point 1 - double z[2][2]; - double x[2][2]; - - double aYZ[2]; - double bYZ[2]; - - double aXZ[2]; - double bXZ[2]; - - double finalZ; - - // find points of the tracks = 2 straight lines - for (int i = 0; i < 2; i++) { - - mCandTr[mCurHyp][i].propagateToZquadratic(startPoint, mBz); - // mCandTr[mCurHyp][i].propagateToZlinear(startPoint); - z[i][0] = startPoint; - y[i][0] = mCandTr[mCurHyp][i].getY(); - x[i][0] = mCandTr[mCurHyp][i].getX(); - - mCandTr[mCurHyp][i].propagateToZquadratic(endPoint, mBz); - // mCandTr[mCurHyp][i].propagateToZlinear(endPoint); - z[i][1] = endPoint; - y[i][1] = mCandTr[mCurHyp][i].getY(); - x[i][1] = mCandTr[mCurHyp][i].getX(); - - bYZ[i] = (y[i][1] - y[i][0] * z[i][1] / z[i][0]) / (1 - z[i][1] / z[i][0]); - aYZ[i] = (y[i][0] - bYZ[i]) / z[i][0]; - - bXZ[i] = (x[i][1] - x[i][0] * z[i][1] / z[i][0]) / (1 - z[i][1] / z[i][0]); - aXZ[i] = (x[i][0] - bXZ[i]) / z[i][0]; - } - - // z seed: equ. for intersection of these lines - finalZ = 0.5 * ((bYZ[0] - bYZ[1]) / (aYZ[1] - aYZ[0]) + (bXZ[0] - bXZ[1]) / (aXZ[1] - aXZ[0])); - - mPCA[mCurHyp][2] = finalZ; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_quad(int mCurHyp) -{ - double startPoint = 0.; - double endPoint = 40.; // first disk - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double x[2]; - double y[2]; - double sinPhi0[2]; - double cosPhi0[2]; - double tanL0[2]; - double qpt0[2]; - - double k[2]; // B2C *abs(mBz) - double Hz[2]; // mBz/abs(mBz) - - double Ax[2], Bx[2], Cx[2]; - double Ay[2], By[2], Cy[2]; - - double deltaX[2], deltaY[2]; - - bool posX[2], nulX[2], negX[2]; - double z1X[2], z2X[2], z12X[2]; - - bool posY[2], nulY[2], negY[2]; - double z1Y[2], z2Y[2], z12Y[2]; - - double finalZ[2]; - - // find all variables for 2 tracks at z0 = startPoint - // set A, B, C variables for x/y equation for 2 tracks - // calculate Deltax/y for both and roots - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateToZquadratic(startPoint, mBz); - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - sinPhi0[i] = mCandTr[mCurHyp][i].getSnp(); - cosPhi0[i] = std::sqrt((1. - sinPhi0[i]) * (1. + sinPhi0[i])); - tanL0[i] = mCandTr[mCurHyp][i].getTanl(); - qpt0[i] = mCandTr[mCurHyp][i].getInvQPt(); - k[i] = getK(mBz); - Hz[i] = getHz(mBz); - - Ax[i] = qpt0[i] * Hz[i] * k[i] * sinPhi0[i] / (2 * tanL0[i] * tanL0[i]); - Bx[i] = cosPhi0[i] / tanL0[i]; - Cx[i] = x[i] - X; - - Ay[i] = -qpt0[i] * Hz[i] * k[i] * cosPhi0[i] / (2 * tanL0[i] * tanL0[i]); - By[i] = sinPhi0[i] / tanL0[i]; - Cy[i] = y[i] - Y; // - - deltaX[i] = Bx[i] * Bx[i] - 4 * Ax[i] * Cx[i]; - deltaY[i] = By[i] * By[i] - 4 * Ay[i] * Cy[i]; - - if (deltaX[i] > 0) { - posX[i] = true; - z1X[i] = (-Bx[i] - std::sqrt(deltaX[i])) / (2 * Ax[i]); - z2X[i] = (-Bx[i] + std::sqrt(deltaX[i])) / (2 * Ax[i]); - } else if (deltaX[i] == 0) { - nulX[i] = true; - z12X[i] = -Bx[i] / (2 * Ax[i]); - } else { - negX[i] = true; - z12X[i] = 0; - } // discard - - if (deltaY[i] > 0) { - posY[i] = true; - z1Y[i] = (-By[i] - std::sqrt(deltaY[i])) / (2 * Ay[i]); - z2Y[i] = (-By[i] + std::sqrt(deltaY[i])) / (2 * Ay[i]); - } else if (deltaX[i] == 0) { - nulY[i] = true; - z12Y[i] = -By[i] / (2 * Ay[i]); - } else { - negY[i] = true; - z12Y[i] = 0; - } - - // find the z located in an acceptable interval - if (posX[i]) { - if (z1X[i] < endPoint && z1X[i] > startPoint) { - z12X[i] = z1X[i]; - } else { - z12X[i] = z2X[i]; - } - } - - if (posY[i]) { - if (z1Y[i] < endPoint && z1Y[i] > startPoint) { - z12Y[i] = z1Y[i]; - } else { - z12Y[i] = z2Y[i]; - } - } - - finalZ[i] = 0.5 * (z12X[i] + z12Y[i]); - } - - mPCA[mCurHyp][2] = 0.5 * (finalZ[0] + finalZ[1]); -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::findZatXY_linear(int mCurHyp) -{ - - double startPoint = 0.; - - double X = mPCA[mCurHyp][0]; // X seed - double Y = mPCA[mCurHyp][1]; // Y seed - - mCandTr[mCurHyp][0] = *mOrigTrPtr[0]; - mCandTr[mCurHyp][1] = *mOrigTrPtr[1]; - - double x[2]; - double y[2]; - double sinPhi0[2]; - double cosPhi0[2]; - double tanL0[2]; - - double Ax[2], Bx[2]; - double Ay[2], By[2]; - - double z12X[2]; - double z12Y[2]; - - double finalZ[2]; - - // find all variables for 2 tracks at z0 = startPoint - // set A, B variables for x/y equation for 2 tracks - // calculate root - - for (int i = 0; i < 2; i++) { - mCandTr[mCurHyp][i].propagateToZlinear(startPoint); - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - sinPhi0[i] = mCandTr[mCurHyp][i].getSnp(); - cosPhi0[i] = std::sqrt((1. - sinPhi0[i]) * (1. + sinPhi0[i])); - tanL0[i] = mCandTr[mCurHyp][i].getTanl(); - - Ax[i] = cosPhi0[i] / tanL0[i]; - Bx[i] = x[i] - X; - - Ay[i] = sinPhi0[i] / tanL0[i]; - By[i] = y[i] - Y; - - z12X[i] = -Bx[i] / Ax[i]; - z12Y[i] = -By[i] / Ay[i]; - - finalZ[i] = 0.5 * (z12X[i] + z12Y[i]); - } - - mPCA[mCurHyp][2] = 0.5 * (finalZ[0] + finalZ[1]); -} - -//___________________________________________________________________ -template -inline o2::track::TrackParFwd FwdDCAFitterN::FwdgetTrackParamAtPCA(int i, int icand) const -{ - // propagate tracks param only to current vertex (if not already done) - int ord = mOrder[icand]; - o2::track::TrackParFwd trc(mCandTr[ord][i]); - if (!mTrPropDone[ord]) { - auto z = mPCA[ord][2]; - trc.propagateParamToZquadratic(z, mBz); - } - - return {trc}; -} - -//___________________________________________________________________ -template -inline double FwdDCAFitterN::getAbsMax(const VecND& v) -{ - double mx = -1; - for (int i = N; i--;) { - auto vai = std::abs(v[i]); - if (mx < vai) { - mx = vai; - } - } - return mx; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::minimizeChi2() -{ - // find best chi2 (weighted DCA) of N tracks in the vicinity of the seed PCA - double x[2], y[2]; - double sumX = 0.; - double sumY = 0.; - - for (int i = N; i--;) { - mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; - auto z = mPCA[mCurHyp][2]; - - mCandTr[mCurHyp][i].propagateToZquadratic(z, mBz); - - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - mTrcEInv[mCurHyp][i].set(mCandTr[mCurHyp][i], ZerrFactor); // prepare inverse cov.matrices at starting point - - sumX = sumX + x[i]; - sumY = sumY + y[i]; - } - - mPCA[mCurHyp][0] = sumX / N; - mPCA[mCurHyp][1] = sumY / N; - - if (mMaxDXIni > 0 && !roughDXCut()) { // apply rough cut on tracks X difference - return false; - } - - if (!FwdcalcPCACoefs()) { // prepare tracks contribution matrices to the global PCA - return false; - } - FwdcalcPCA(); // current PCA - FwdcalcTrackResiduals(); // current track residuals - float chi2Upd, chi2 = FwdcalcChi2(); - do { - calcTrackDerivatives(); // current track derivatives (1st and 2nd) - FwdcalcResidDerivatives(); // current residals derivatives (1st and 2nd) - FwdcalcChi2Derivatives(); // current chi2 derivatives (1st and 2nd) to proceed for dz calculation - - // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 - if (!mD2Chi2Dz2.Invert()) { - return false; - } - - VecND dz = mD2Chi2Dz2 * mDChi2Dz; - - if (!FwdcorrectTracks(dz)) { // calculate new Pi (mTrPos) following Newton-Rapson iteration - return false; - } - - FwdcalcPCA(); // updated mPCA (new V coordinates with new mTrPos (Pi)) - if (mCrossIDAlt >= 0 && closerToAlternative()) { - mAllowAltPreference = false; - return false; - } - - FwdcalcTrackResiduals(); // updated residuals - chi2Upd = FwdcalcChi2(); // updated chi2 - - if (getAbsMax(dz) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { - chi2 = chi2Upd; - break; // converged - } - - chi2 = chi2Upd; - } while (++mNIters[mCurHyp] < mMaxIter); - - mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::minimizeChi2NoErr() -{ - // find best chi2 (absolute DCA) of N tracks in the vicinity of the PCA seed - double x[2], y[2]; - double sumX = 0.; - double sumY = 0.; - - for (int i = N; i--;) { - - mCandTr[mCurHyp][i] = *mOrigTrPtr[i]; - - auto z = mPCA[mCurHyp][2]; - mCandTr[mCurHyp][i].propagateParamToZquadratic(z, mBz); - - x[i] = mCandTr[mCurHyp][i].getX(); - y[i] = mCandTr[mCurHyp][i].getY(); - - mPCA[mCurHyp][2] = z; - - setTrackPos(mTrPos[mCurHyp][i], mCandTr[mCurHyp][i]); // prepare positions - - sumX = sumX + x[i]; - sumY = sumY + y[i]; - } - - mPCA[mCurHyp][0] = sumX / N; - mPCA[mCurHyp][1] = sumY / N; - - if (mMaxDXIni > 0 && !roughDXCut()) { // apply rough cut on tracks Z difference - return false; - } - - FwdcalcPCANoErr(); // current PCA - FwdcalcTrackResiduals(); // current track residuals - float chi2Upd, chi2 = FwdcalcChi2NoErr(); - do { - calcTrackDerivatives(); // current track derivatives (1st and 2nd) - FwdcalcResidDerivativesNoErr(); // current residals derivatives (1st and 2nd) - FwdcalcChi2DerivativesNoErr(); // current chi2 derivatives (1st and 2nd) - - // do Newton-Rapson iteration with corrections = - dchi2/d{x0..xN} * [ d^2chi2/d{x0..xN}^2 ]^-1 - if (!mD2Chi2Dz2.Invert()) { - return false; - } - VecND dz = mD2Chi2Dz2 * mDChi2Dz; - - if (!FwdcorrectTracks(dz)) { - return false; - } - FwdcalcPCANoErr(); // updated PCA - if (mCrossIDAlt >= 0 && closerToAlternative()) { - mAllowAltPreference = false; - return false; - } - FwdcalcTrackResiduals(); // updated residuals - chi2Upd = FwdcalcChi2NoErr(); // updated chi2 - if (getAbsMax(dz) < mMinParamChange || chi2Upd > chi2 * mMinRelChi2Change) { - chi2 = chi2Upd; - break; // converged - } - chi2 = chi2Upd; - } while (++mNIters[mCurHyp] < mMaxIter); - // - mChi2[mCurHyp] = chi2 * NInv; - return mChi2[mCurHyp] < mMaxChi2; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::roughDXCut() const -{ - // apply rough cut on DX between the tracks in the seed point - - bool accept = true; - for (int i = N; accept && i--;) { - for (int j = i; j--;) { - if (std::abs(mCandTr[mCurHyp][i].getX() - mCandTr[mCurHyp][j].getX()) > mMaxDXIni) { - accept = false; - break; - } - } - } - return accept; -} - -//___________________________________________________________________ -template -bool FwdDCAFitterN::closerToAlternative() const -{ - // check if the point current PCA point is closer to the seeding XY point being tested or to alternative see (if any) - auto dxCur = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDCur], dyCur = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDCur]; - auto dxAlt = mPCA[mCurHyp][0] - mCrossings.xDCA[mCrossIDAlt], dyAlt = mPCA[mCurHyp][1] - mCrossings.yDCA[mCrossIDAlt]; - return dxCur * dxCur + dyCur * dyCur > dxAlt * dxAlt + dyAlt * dyAlt; -} - -//___________________________________________________________________ -template -void FwdDCAFitterN::print() const -{ - LOG(info) << N << "-prong vertex fitter in " << (mUseAbsDCA ? "abs." : "weighted") << " distance minimization mode"; - LOG(info) << "Bz: " << mBz << " MaxIter: " << mMaxIter << " MaxChi2: " << mMaxChi2; - LOG(info) << "Stopping condition: Max.param change < " << mMinParamChange << " Rel.Chi2 change > " << mMinRelChi2Change; - LOG(info) << "Discard candidates for : Rvtx > " << getMaxR() << " DZ between tracks > " << mMaxDXIni; -} -//___________________________________________________________________ -template -inline bool FwdDCAFitterN::propagateToVtx(o2::track::TrackParCovFwd& t, const std::array& p, const std::array& cov) const -{ - // propagate track to vertex including MCS effects if material budget included, simple propagation to Z otherwise - float x2x0 = 0; - if (mUseMatBudget) { - auto mb = mMatLUT->getMatBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); - x2x0 = (float)mb.meanX2X0; - return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); - } else if (mTGeoFallBackAllowed) { - auto geoMan = o2::base::GeometryManager::meanMaterialBudget(t.getX(), t.getY(), t.getZ(), p[0], p[1], p[2]); - x2x0 = (float)geoMan.meanX2X0; - return t.propagateToVtxhelixWithMCS(p[2], {p[0], p[1]}, cov, mBz, x2x0); - } else { - t.propagateToZhelix(p[2], mBz); - return true; - } -} - -using FwdDCAFitter2 = FwdDCAFitterN<2, o2::track::TrackParCovFwd>; -using FwdDCAFitter3 = FwdDCAFitterN<3, o2::track::TrackParCovFwd>; - -} // namespace vertexing -} // namespace o2 -#endif // _ALICEO2_DCA_FWDFITTERN_ diff --git a/Detectors/Vertexing/src/FwdDCAFitterN.cxx b/Detectors/Vertexing/src/FwdDCAFitterN.cxx deleted file mode 100644 index f7176aa5039fd..0000000000000 --- a/Detectors/Vertexing/src/FwdDCAFitterN.cxx +++ /dev/null @@ -1,33 +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 DCAFitterN.cxx -/// \brief Defintions for N-prongs secondary vertex fit -/// \author ruben.shahoyan@cern.ch, adapted from central barrel to fwd rapidities by Rita Sadek, rita.sadek@cern.ch - -#include "DetectorsVertexing/FwdDCAFitterN.h" - -namespace o2 -{ -namespace vertexing -{ - -void __test_instance__() -{ - FwdDCAFitter2 ft2; - FwdDCAFitter3 ft3; - o2::track::TrackParCovFwd tr; - ft2.process(tr, tr); - ft3.process(tr, tr, tr); -} - -} // namespace vertexing -} // namespace o2 diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h index a299431ef17fc..ec80c16af329e 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace zdc class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::ZDC, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::ZDC, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx index 9ab0e10098f43..6f73fa121244f 100644 --- a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, - ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/gconfig/g4Config.C b/Detectors/gconfig/g4Config.C index 8f74c0105dbf5..c2b1fbd433e4b 100644 --- a/Detectors/gconfig/g4Config.C +++ b/Detectors/gconfig/g4Config.C @@ -100,7 +100,16 @@ void Config() auto& g4Params = ::o2::conf::G4Params::Instance(); auto& physicsSetup = g4Params.getPhysicsConfigString(); std::cout << "PhysicsSetup wanted " << physicsSetup << "\n"; - auto runConfiguration = new TG4RunConfiguration("geomRoot", physicsSetup, "stepLimiter+specialCuts", + std::string geomNavStr; + if (g4Params.navmode == o2::conf::EG4Nav::kTGeo) { + geomNavStr = "geomRoot"; + } else if (g4Params.navmode == o2::conf::EG4Nav::kG4) { + geomNavStr = "geomVMC+RootToGeant4"; + } else { + LOG(fatal) << "Unsupported geometry navigation mode"; + } + + auto runConfiguration = new TG4RunConfiguration(geomNavStr, physicsSetup, "stepLimiter+specialCuts", specialStacking, mtMode); /// avoid the use of G4BACKTRACE (it seems to inferfere with process logic in o2-sim) setenv("G4BACKTRACE", "none", 1); diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 7357167a3fcd8..e6a8db1077136 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -125,6 +125,7 @@ o2_add_library(Framework src/RootArrowFilesystem.cxx src/SendingPolicy.cxx src/ServiceRegistry.cxx + src/ServiceRegistryRef.cxx src/ServiceSpec.cxx src/SimpleResourceManager.cxx src/SimpleRawDeviceService.cxx diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 4fd35e0dc5065..7586d6a6d3c63 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -405,15 +405,15 @@ class Table; /// Type-checking index column binding struct Binding { void const* ptr = nullptr; - size_t hash = 0; - std::span refs; + uint32_t hash = 0; + // std::span refs; template void bind(T const* table) { ptr = table; hash = o2::framework::TypeIdHelpers::uniqueId(); - refs = std::span{T::originals}; + // refs = std::span{T::originals}; } template @@ -1293,6 +1293,9 @@ struct ArrowHelpers { template concept is_iterator = framework::base_of_template || framework::specialization_of_template; +template +concept is_table_or_iterator = is_table || is_iterator; + template concept with_originals = requires { T::originals.size(); @@ -2724,7 +2727,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2734,10 +2737,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getIterators(); \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto& i : *mColumnIterator) { \ + result.emplace_back(t->rawIteratorAt(i)); \ + } \ + return result; \ } \ \ - template \ + template \ auto filtered_##_Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2747,35 +2755,15 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - return getFilteredIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ - auto result = std::vector(); \ - for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ - } \ - return result; \ - } \ - \ - template \ - std::vector getFilteredIterators() const \ - { \ - if constexpr (o2::soa::is_filtered_table) { \ - auto result = std::vector(); \ - for (auto const& i : *mColumnIterator) { \ - auto pos = mBinding.get()->isInSelectedRows(i); \ - if (pos > 0) { \ - result.emplace_back(mBinding.get()->iteratorAt(pos)); \ - } \ + auto result = std::vector(); \ + result.reserve((*mColumnIterator).size()); \ + for (auto const& i : *mColumnIterator) { \ + auto pos = t->isInSelectedRows(i); \ + if (pos > 0) { \ + result.emplace_back(t->iteratorAt(pos)); \ } \ - return result; \ - } else { \ - static_assert(o2::framework::always_static_assert_v, "T is not a Filtered type"); \ } \ - return {}; \ + return result; \ } \ \ auto _Getter_() const \ @@ -3090,15 +3078,9 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, "self"); \ } \ - return getIterators(); \ - } \ - \ - template \ - auto getIterators() const \ - { \ auto result = std::vector(); \ for (auto& i : *mColumnIterator) { \ - result.push_back(mBinding.get()->rawIteratorAt(i)); \ + result.push_back(t->rawIteratorAt(i)); \ } \ return result; \ } \ diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index fd41a079c6570..121ce7f4b4a77 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -534,12 +534,6 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling -template -bool preInitializeCache(InitContext&, T&) -{ - return false; -} - template bool initializeCache(ProcessingContext&, T&) { diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index eb98d55cc24b2..fbd523c7b0c37 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -22,7 +22,6 @@ #include "Framework/EndOfStreamContext.h" #include "Framework/GroupSlicer.h" #include "Framework/StructToTuple.h" -#include "Framework/Traits.h" #include "Framework/TypeIdHelpers.h" #include "Framework/ArrowTableSlicingCache.h" #include "Framework/AnalysisDataModel.h" @@ -66,17 +65,20 @@ static constexpr bool is_enumeration_v> = true; template concept is_enumeration = is_enumeration_v>; +template +concept is_table_iterator_or_enumeration = soa::is_table_or_iterator || is_enumeration; + // Helper struct which builds a DataProcessorSpec from // the contents of an AnalysisTask... namespace { struct AnalysisDataProcessorBuilder { - template + template static void addGroupingCandidates(Cache& bk, Cache& bku, bool enabled) { - [&bk, &bku, enabled](framework::pack) mutable { + [](framework::pack, Cache& bk, Cache& bku, bool enabled) { auto key = std::string{"fIndex"} + o2::framework::cutString(soa::getLabelFromType>()); - ([&bk, &bku, &key, enabled]() mutable { + ([](Cache& bk, Cache& bku, bool enabled, std::string const& key) { if constexpr (soa::relatedByIndex, std::decay_t>()) { Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { @@ -85,9 +87,9 @@ struct AnalysisDataProcessorBuilder { framework::updatePairList(bk, e); } } - }(), + }(bk, bku, enabled, key), ...); - }(framework::pack{}); + }(framework::pack{}, bk, bku, enabled); } template @@ -171,8 +173,8 @@ struct AnalysisDataProcessorBuilder { return true; } /// 1. enumeration (must be the only argument) - template - static void inputsFromArgs(R (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) { std::vector inputMetadata; // FIXME: for the moment we do not support begin, end and step. @@ -180,37 +182,37 @@ struct AnalysisDataProcessorBuilder { } /// 2. 1st argument is an iterator - template - static void inputsFromArgs(R (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) + template + static void inputsFromArgs(void (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) requires(std::is_lvalue_reference_v && (std::is_lvalue_reference_v && ...)) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions::parent_t, Args...>(hash, name, value, inputs, eInfos); } /// 3. generic case - template - static void inputsFromArgs(R (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) + template + static void inputsFromArgs(void (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) requires(std::is_lvalue_reference_v && ...) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); addInputsAndExpressions(hash, name, value, inputs, eInfos); } /// 1. enumeration (no grouping) - template - static void cacheFromArgs(R (C::*)(A), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A), bool, Cache&, Cache&) { } /// 2. iterator (the only grouping case) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) { addGroupingCandidates(bk, bku, value); } /// 3. generic case (no grouping) - template - static void cacheFromArgs(R (C::*)(A, Args...), bool, Cache&, Cache&) + template + static void cacheFromArgs(void (C::*)(A, Args...), bool, Cache&, Cache&) { } @@ -285,51 +287,53 @@ struct AnalysisDataProcessorBuilder { } } - template - static auto bindGroupingTable(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindGroupingTable(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as) { - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return extract, 0>(record, infos, hash); } - template - static auto bindAssociatedTables(InputRecord& record, R (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindAssociatedTables(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) requires(!std::same_as && sizeof...(Args) > 0) { constexpr auto p = pack{}; - constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); + constexpr auto hash = o2::framework::TypeIdHelpers::uniqueId(); return std::make_tuple(extract, has_type_at_v(p) + 1>(record, infos, hash)...); } - template + template static void overwriteInternalIndices(std::tuple& dest, std::tuple const& src) { (std::get(dest).bindInternalIndicesTo(&std::get(src)), ...); } - template - static void invokeProcess(Task& task, InputRecord& inputs, R (C::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) + template + static void invokeProcess(Task& task, InputRecord& inputs, void (Task::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) { using G = std::decay_t; auto groupingTable = AnalysisDataProcessorBuilder::bindGroupingTable(inputs, processingFunction, infos); + constexpr const int numElements = nested_brace_constructible_size>() / 10; + // set filtered tables for partitions with grouping - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::setPartition(element, groupingTable); analysis_task_parsers::bindInternalIndicesPartition(element, &groupingTable); return true; }, - task); + task); if constexpr (sizeof...(Associated) == 0) { // single argument to process - homogeneous_apply_refs([&groupingTable](auto& element) { + homogeneous_apply_refs_sized([&groupingTable](auto& element) { analysis_task_parsers::bindExternalIndicesPartition(element, &groupingTable); analysis_task_parsers::setGroupedCombination(element, groupingTable); return true; }, - task); + task); if constexpr (soa::is_iterator) { for (auto& element : groupingTable) { std::invoke(processingFunction, task, *element); @@ -347,7 +351,7 @@ struct AnalysisDataProcessorBuilder { // pre-bind self indices std::apply( [&task](auto&... t) mutable { - (homogeneous_apply_refs( + (homogeneous_apply_refs_sized( [&t](auto& p) { analysis_task_parsers::bindInternalIndicesPartition(p, &t); return true; @@ -359,12 +363,12 @@ struct AnalysisDataProcessorBuilder { auto binder = [&task, &groupingTable, &associatedTables](auto& x) mutable { x.bindExternalIndices(&groupingTable, &std::get>(associatedTables)...); - homogeneous_apply_refs([&x](auto& t) mutable { + homogeneous_apply_refs_sized([&x](auto& t) mutable { analysis_task_parsers::setPartition(t, x); analysis_task_parsers::bindExternalIndicesPartition(t, &x); return true; }, - task); + task); }; groupingTable.bindExternalIndices(&std::get>(associatedTables)...); @@ -376,11 +380,11 @@ struct AnalysisDataProcessorBuilder { associatedTables); // GroupedCombinations bound separately, as they should be set once for all associated tables - homogeneous_apply_refs([&groupingTable, &associatedTables](auto& t) { + homogeneous_apply_refs_sized([&groupingTable, &associatedTables](auto& t) { analysis_task_parsers::setGroupedCombination(t, groupingTable, associatedTables); return true; }, - task); + task); overwriteInternalIndices(associatedTables, associatedTables); if constexpr (soa::is_iterator>) { auto slicer = GroupSlicer(groupingTable, associatedTables, slices); @@ -394,28 +398,28 @@ struct AnalysisDataProcessorBuilder { associatedSlices); // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, slice.groupingElement(), associatedSlices); } } else { // bind partitions and grouping table - homogeneous_apply_refs([&groupingTable](auto& x) { + homogeneous_apply_refs_sized([&groupingTable](auto& x) { analysis_task_parsers::bindExternalIndicesPartition(x, &groupingTable); return true; }, - task); + task); invokeProcessWithArgs(task, processingFunction, groupingTable, associatedTables); } } } - template + template static void invokeProcessWithArgs(C& task, T processingFunction, G g, std::tuple& at) { std::invoke(processingFunction, task, g, std::get(at)...); @@ -523,16 +527,18 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector options; std::vector expressionInfos; + constexpr const int numElements = nested_brace_constructible_size>() / 10; + /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs_sized([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs - homogeneous_apply_refs([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); /// parse process functions defined by corresponding configurables if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::inputsFromArgs(&T::process, "default", true, inputs, expressionInfos); } - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [name = name_str, &expressionInfos, &inputs](auto& x) mutable { // this pushes (argumentIndex, processHash, schemaPtr, nullptr) into expressionInfos for arguments that are Filtered/filtered_iterators return AnalysisDataProcessorBuilder::requestInputsFromArgs(x, name, inputs, expressionInfos); @@ -541,39 +547,39 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) // request base tables for spawnable extended tables and indices to be built // this checks for duplications - homogeneous_apply_refs([&inputs](auto& element) { + homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::requestInputs(inputs, element); }, - *task.get()); + *task.get()); // no static way to check if the task defines any processing, we can only make sure it subscribes to at least something if (inputs.empty() == true) { LOG(warn) << "Task " << name_str << " has no inputs"; } - homogeneous_apply_refs([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); + homogeneous_apply_refs_sized([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); auto requiredServices = CommonServices::defaultServices(); auto arrowServices = CommonServices::arrowServices(); requiredServices.insert(requiredServices.end(), arrowServices.begin(), arrowServices.end()); - homogeneous_apply_refs([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); + homogeneous_apply_refs_sized([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); auto algo = AlgorithmSpec::InitCallback{[task = task, expressionInfos](InitContext& ic) mutable { Cache bindingsKeys; Cache bindingsKeysUnsorted; // add preslice declarations to slicing cache definition - homogeneous_apply_refs([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); + homogeneous_apply_refs_sized([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); - homogeneous_apply_refs([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareOption(ic, element); }, *task.get()); + homogeneous_apply_refs_sized([&ic](auto&& element) { return analysis_task_parsers::prepareService(ic, element); }, *task.get()); auto& callbacks = ic.services().get(); auto eoscb = [task](EndOfStreamContext& eosContext) { - homogeneous_apply_refs([&eosContext](auto& element) { + homogeneous_apply_refs_sized([&eosContext](auto& element) { analysis_task_parsers::postRunService(eosContext, element); analysis_task_parsers::postRunOutput(eosContext, element); return true; }, - *task.get()); + *task.get()); eosContext.services().get().readyToQuit(QuitRequest::Me); }; @@ -585,84 +591,75 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } /// update configurables in filters and partitions - homogeneous_apply_refs( + homogeneous_apply_refs_sized( [&ic](auto& element) -> bool { return analysis_task_parsers::updatePlaceholders(ic, element); }, *task.get()); /// create expression trees for filters gandiva trees matched to schemas and store the pointers into expressionInfos - homogeneous_apply_refs([&expressionInfos](auto& element) { + homogeneous_apply_refs_sized([&expressionInfos](auto& element) { return analysis_task_parsers::createExpressionTrees(expressionInfos, element); }, - *task.get()); + *task.get()); /// parse process functions to enable requested grouping caches - note that at this state process configurables have their final values if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::cacheFromArgs(&T::process, true, bindingsKeys, bindingsKeysUnsorted); } - homogeneous_apply_refs( - [&bindingsKeys, &bindingsKeysUnsorted](auto& x) mutable { + homogeneous_apply_refs_sized( + [&bindingsKeys, &bindingsKeysUnsorted](auto& x) { return AnalysisDataProcessorBuilder::requestCacheFromArgs(x, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); ic.services().get().setCaches(std::move(bindingsKeys)); ic.services().get().setCachesUnsorted(std::move(bindingsKeysUnsorted)); - // initialize global caches - homogeneous_apply_refs([&ic](auto& element) { - return analysis_task_parsers::preInitializeCache(ic, element); - }, - *(task.get())); return [task, expressionInfos](ProcessingContext& pc) mutable { // load the ccdb object from their cache - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); // reset partitions once per dataframe - homogeneous_apply_refs([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); + homogeneous_apply_refs_sized([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); // reset selections for the next dataframe - for (auto& info : expressionInfos) { - info.resetSelection = true; - } + std::ranges::for_each(expressionInfos, [](auto& info) { info.resetSelection = true; }); // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&slices](auto& element) { + homogeneous_apply_refs_sized([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, - *(task.get())); + *(task.get())); // initialize local caches - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); // prepare outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); // execute run() if constexpr (requires { task->run(pc); }) { task->run(pc); } // execute process() - if constexpr (requires { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); }) { + if constexpr (requires { &T::process; }) { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); } // execute optional process() - homogeneous_apply_refs( - [&pc, &expressionInfos, &task, &slices](auto& x) mutable { - if constexpr (base_of_template>) { + homogeneous_apply_refs_sized( + [&pc, &expressionInfos, &task, &slices](auto& x) { + if constexpr (is_process_configurable) { if (x.value == true) { AnalysisDataProcessorBuilder::invokeProcess(*task.get(), pc.inputs(), x.process, expressionInfos, slices); return true; } + return false; } return false; }, *task.get()); // prepare delayed outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); // finalize outputs - homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); + homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); }; }}; return { name, - // FIXME: For the moment we hardcode this. We could build - // this list from the list of methods actually implemented in the - // task itself. inputs, outputs, algo, diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index 7f77e4a96f76f..09ea8b7ea6b61 100644 --- a/Framework/Core/include/Framework/CompletionPolicyHelpers.h +++ b/Framework/Core/include/Framework/CompletionPolicyHelpers.h @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -11,7 +11,6 @@ #ifndef O2_FRAMEWORK_COMPLETIONPOLICYHELPERS_H_ #define O2_FRAMEWORK_COMPLETIONPOLICYHELPERS_H_ -#include "Framework/ChannelSpec.h" #include "Framework/CompletionPolicy.h" #include "Headers/DataHeader.h" @@ -54,6 +53,12 @@ struct CompletionPolicyHelpers { } static CompletionPolicy consumeWhenAny(std::string matchName); + // Consume all the data captured until the oldest possible timeframe + // in input indicates that nothing else can be added to this timeslice. + // Useful in case of wildcards which multiplex multiple subspecs on the + // same input. + static CompletionPolicy consumeWhenPastOldestPossibleTimeframe(const char* name, CompletionPolicy::Matcher matcher); + /// When any of the parts of the record have been received, consume them. static CompletionPolicy consumeWhenAnyWithAllConditions(const char* name, CompletionPolicy::Matcher matcher); /// Default matcher applies for all devices diff --git a/Framework/Core/include/Framework/Configurable.h b/Framework/Core/include/Framework/Configurable.h index 3ef90a0eee166..0931884da1ff7 100644 --- a/Framework/Core/include/Framework/Configurable.h +++ b/Framework/Core/include/Framework/Configurable.h @@ -84,10 +84,10 @@ template using MutableConfigurable = Configurable>; template -concept is_configurable = requires(T& t) { - typename T::type; +concept is_configurable = requires(T t) { requires std::same_as; - &T::operator typename T::type; + requires std::same_as; + requires std::same_as::type, decltype(t.value)>; }; using ConfigurableAxis = Configurable, ConfigParamKind::kAxisSpec, ConfigurablePolicyConst, ConfigParamKind::kAxisSpec>>; @@ -99,19 +99,18 @@ concept is_configurable_axis = is_configurable&& T::kind == ConfigParamKind::kAxisSpec; }; -template +template struct ProcessConfigurable : Configurable { - ProcessConfigurable(R (T::*process_)(As...), std::string const& name_, bool&& value_, std::string const& help_) + ProcessConfigurable(void (T::*process_)(As...), std::string const& name_, bool&& value_, std::string const& help_) : process{process_}, Configurable(name_, std::forward(value_), help_) { } - R(T::*process) - (As...); + void (T::*process)(As...); }; template -concept is_process_configurable = is_configurable && requires(T& t) { t.process; }; +concept is_process_configurable = is_configurable && requires(T t) { t.process; }; #define PROCESS_SWITCH(_Class_, _Name_, _Help_, _Default_) \ decltype(o2::framework::ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}) do##_Name_ = o2::framework::ProcessConfigurable{&_Class_ ::_Name_, #_Name_, _Default_, _Help_}; diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h new file mode 100644 index 0000000000000..b7a334454bb6e --- /dev/null +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -0,0 +1,239 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_DATASPECVIEWS_H_ +#define O2_FRAMEWORK_DATASPECVIEWS_H_ + +#include +#include +#include "DomainInfoHeader.h" +#include "SourceInfoHeader.h" +#include "Headers/DataHeader.h" +#include + +namespace o2::framework +{ + +struct count_payloads { + // ends the pipeline, returns the container + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, count_payloads self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += header->splitPayloadParts; + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts ? header->splitPayloadParts : 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return count; + } +}; + +struct count_parts { + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, count_parts self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + auto* sih = o2::header::get(r[mi]->GetData()); + auto* dih = o2::header::get(r[mi]->GetData()); + if (!header && !sih && !dih) { + throw std::runtime_error("Header information not found"); + } + // We skip oldest possible timeframe / end of stream and not consider it + // as actual parts. + if (dih || sih) { + count += 1; + mi += 2; + } else if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += 1; + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return count; + } +}; + +struct DataRefIndices { + size_t headerIdx; + size_t payloadIdx; +}; + +struct get_pair { + size_t pairId; + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_pair self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + size_t diff = self.pairId - count; + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + count += header->splitPayloadParts; + if (self.pairId < count) { + return {mi, mi + 1 + diff}; + } + mi += header->splitPayloadParts + 1; + } else { + count += header->splitPayloadParts ? header->splitPayloadParts : 1; + if (self.pairId < count) { + return {mi, mi + 2 * diff + 1}; + } + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + throw std::runtime_error("Payload not found"); + } +}; + +struct get_dataref_indices { + size_t part; + size_t subPart; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_dataref_indices self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + if (self.part == count) { + return {mi, mi + 1 + self.subPart}; + } + count += 1; + mi += header->splitPayloadParts + 1; + } else { + if (self.part == count) { + return {mi, mi + 2 * self.subPart + 1}; + } + count += 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + throw std::runtime_error("Payload not found"); + } +}; + +struct get_header { + size_t id; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend fair::mq::MessagePtr& operator|(R&& r, get_header self) + { + return r[(r | get_dataref_indices{self.id, 0}).headerIdx]; + } +}; + +struct get_payload { + size_t part; + size_t subPart; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend fair::mq::MessagePtr& operator|(R&& r, get_payload self) + { + return r[(r | get_dataref_indices{self.part, self.subPart}).payloadIdx]; + } +}; + +struct get_num_payloads { + size_t id; + // ends the pipeline, returns the number of parts + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend size_t operator|(R&& r, get_num_payloads self) + { + size_t count = 0; + size_t mi = 0; + while (mi < r.size()) { + auto* header = o2::header::get(r[mi]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (self.id == count) { + if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + return header->splitPayloadParts; + } else { + return 1; + } + } + if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + count += 1; + mi += header->splitPayloadParts + 1; + } else { + count += 1; + mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + } + } + return 0; + } +}; + +struct MessageSet; + +struct MessageStore { + std::span sets; + size_t inputsPerSlot = 0; +}; + +struct inputs_for_slot { + TimesliceSlot slot; + template + requires requires(R r) { std::ranges::random_access_range; } + friend std::span operator|(R&& r, inputs_for_slot self) + { + return std::span(r.sets[self.slot.index * r.inputsPerSlot]); + } +}; + +struct messages_for_input { + size_t inputIdx; + template + requires std::ranges::random_access_range + friend std::span operator|(R&& r, messages_for_input self) + { + return r[self.inputIdx].messages; + } +}; + +// FIXME: we should use special index classes in place of size_t +// FIXME: we need something to substitute a range in the store with another + +} // namespace o2::framework + +#endif // O2_FRAMEWORK_DATASPECVIEWS_H_ diff --git a/Framework/Core/include/Framework/DataProcessingStats.h b/Framework/Core/include/Framework/DataProcessingStats.h index e32523c9abb08..a1f5c0eec5568 100644 --- a/Framework/Core/include/Framework/DataProcessingStats.h +++ b/Framework/Core/include/Framework/DataProcessingStats.h @@ -69,6 +69,9 @@ enum struct ProcessingStatsId : short { RESOURCES_MISSING, RESOURCES_INSUFFICIENT, RESOURCES_SATISFACTORY, + CCDB_CACHE_HIT, + CCDB_CACHE_MISS, + CCDB_CACHE_FAILURE, AVAILABLE_MANAGED_SHM_BASE = 512, }; diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index 1e010fc12f3d4..e5a2aecea1de4 100644 --- a/Framework/Core/include/Framework/DataRelayer.h +++ b/Framework/Core/include/Framework/DataRelayer.h @@ -102,7 +102,8 @@ class DataRelayer DataRelayer(CompletionPolicy const&, std::vector const& routes, TimesliceIndex&, - ServiceRegistryRef); + ServiceRegistryRef, + int); /// This invokes the appropriate `InputRoute::danglingChecker` on every /// entry in the cache and if it returns true, it creates a new diff --git a/Framework/Core/include/Framework/DefaultsHelpers.h b/Framework/Core/include/Framework/DefaultsHelpers.h index 16d41d03baa7f..68e64cc42a90e 100644 --- a/Framework/Core/include/Framework/DefaultsHelpers.h +++ b/Framework/Core/include/Framework/DefaultsHelpers.h @@ -12,16 +12,24 @@ #ifndef O2_FRAMEWORK_DEFAULTHELPERS_H_ #define O2_FRAMEWORK_DEFAULTHELPERS_H_ +namespace fair::mq +{ +class ProgOptions; +} + namespace o2::framework { enum struct DeploymentMode; +struct DeviceConfig; struct DefaultsHelpers { static DeploymentMode deploymentMode(); /// @true if running online static bool onlineDeploymentMode(); /// get max number of timeslices in the queue - static unsigned int pipelineLength(); + static unsigned int pipelineLength(unsigned int minLength); + static unsigned int pipelineLength(const fair::mq::ProgOptions& options); + static unsigned int pipelineLength(const DeviceConfig& dc); }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/ServiceRegistryRef.h b/Framework/Core/include/Framework/ServiceRegistryRef.h index 910d4e726c080..85aad6d70e93b 100644 --- a/Framework/Core/include/Framework/ServiceRegistryRef.h +++ b/Framework/Core/include/Framework/ServiceRegistryRef.h @@ -112,6 +112,8 @@ class ServiceRegistryRef mRegistry.unlock(mSalt); } + static ServiceRegistryRef *globalDeviceRef(ServiceRegistryRef *ref = nullptr); + private: ServiceRegistry& mRegistry; ServiceRegistry::Salt mSalt; diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 31cddc9803d69..c5cc021a53478 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -564,7 +564,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() if (dc.options.count("timeframes-rate-limit") && dc.options["timeframes-rate-limit"].defaulted() == false) { config->maxTimeframes = std::stoll(dc.options["timeframes-rate-limit"].as()); } else { - config->maxTimeframes = readers * DefaultsHelpers::pipelineLength(); + config->maxTimeframes = readers * DefaultsHelpers::pipelineLength(dc); } static bool once = false; // Until we guarantee this is called only once... @@ -581,7 +581,6 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() auto spawner = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-spawner"); }); auto analysisCCDB = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-ccdb"); }); auto builder = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-index-builder"); }); - auto reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); auto writer = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-writer"); }); auto& dec = ctx.services().get(); dec.requestedAODs.clear(); @@ -659,6 +658,9 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() workflow.erase(writer); } + // removing writer would invalidate the reader iterator if it was created before + auto reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); + if (reader != workflow.end()) { // If reader and/or builder were adjusted, remove unneeded outputs // update currently requested AODs diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index f786d99fd2c0d..06bc7969ebf1e 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -414,11 +414,13 @@ o2::framework::ServiceSpec CommonServices::dataRelayer() .name = "datarelayer", .init = [](ServiceRegistryRef services, DeviceState&, fair::mq::ProgOptions& options) -> ServiceHandle { auto& spec = services.get(); + int pipelineLength = DefaultsHelpers::pipelineLength(options); return ServiceHandle{TypeIdHelpers::uniqueId(), new DataRelayer(spec.completionPolicy, spec.inputs, services.get(), - services)}; + services, + pipelineLength)}; }, .configure = noConfiguration(), .kind = ServiceKind::Serial}; @@ -1150,6 +1152,30 @@ o2::framework::ServiceSpec CommonServices::dataProcessingStats() .scope = Scope::DPL, .minPublishInterval = 0, .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-hit", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_HIT), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-miss", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_MISS), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, + .sendInitialValue = true}, + MetricSpec{.name = "ccdb-cache-failure", + .enabled = true, + .metricId = static_cast(ProcessingStatsId::CCDB_CACHE_FAILURE), + .kind = Kind::UInt64, + .scope = Scope::DPL, + .minPublishInterval = 1000, + .maxRefreshLatency = 10000, .sendInitialValue = true}}; for (auto& metric : metrics) { diff --git a/Framework/Core/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index 67c726b7f4368..2b49b8dfa9acd 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -11,6 +11,7 @@ #include "Framework/CompletionPolicyHelpers.h" #include "Framework/CompletionPolicy.h" +#include "Framework/DataProcessingHeader.h" #include "Framework/InputSpan.h" #include "Framework/DeviceSpec.h" #include "Framework/CompilerBuiltins.h" @@ -263,6 +264,32 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyZeroCount(const char* na return CompletionPolicy{name, matcher, callback, false}; } +CompletionPolicy CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe(const char* name, CompletionPolicy::Matcher matcher) +{ + auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { + size_t currentTimeslice = -1; + for (auto& input : inputs) { + if (input.header == nullptr) { + continue; + } + o2::framework::DataProcessingHeader const* dph = o2::header::get(input.header); + if (dph && !TimingInfo::timesliceIsTimer(dph->startTime)) { + currentTimeslice = dph->startTime; + break; + } + } + + auto& timesliceIndex = ref.get(); + auto oldestPossibleTimeslice = timesliceIndex.getOldestPossibleInput().timeslice.value; + + if (currentTimeslice >= oldestPossibleTimeslice) { + return CompletionPolicy::CompletionOp::Retry; + } + return CompletionPolicy::CompletionOp::Consume; + }; + return CompletionPolicy{name, matcher, callback, false}; +} + CompletionPolicy CompletionPolicyHelpers::consumeWhenAny(const char* name, CompletionPolicy::Matcher matcher) { auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index ccfb58db7559a..da04a23e81c0c 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1483,7 +1483,7 @@ void DataProcessingDevice::doPrepare(ServiceRegistryRef ref) auto& infos = state.inputChannelInfos; if (context.balancingInputs) { - static int pipelineLength = DefaultsHelpers::pipelineLength(); + static int pipelineLength = DefaultsHelpers::pipelineLength(*ref.get().device()->fConfig); static uint64_t ahead = getenv("DPL_MAX_CHANNEL_AHEAD") ? std::atoll(getenv("DPL_MAX_CHANNEL_AHEAD")) : std::max(8, std::min(pipelineLength - 48, pipelineLength / 2)); auto newEnd = std::remove_if(pollOrder.begin(), pollOrder.end(), [&infos, limitNew = currentOldest.value + ahead](int a) -> bool { return infos[a].oldestForChannel.value > limitNew; @@ -2259,12 +2259,14 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return false; } - auto postUpdateStats = [ref](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart, uint64_t tStartMilli) { + int pipelineLength = DefaultsHelpers::pipelineLength(*ref.get().device()->fConfig); + + auto postUpdateStats = [ref, pipelineLength](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t tStart, uint64_t tStartMilli) { auto& stats = ref.get(); auto& states = ref.get(); std::atomic_thread_fence(std::memory_order_release); char relayerSlotState[1024]; - int written = snprintf(relayerSlotState, 1024, "%d ", DefaultsHelpers::pipelineLength()); + int written = snprintf(relayerSlotState, 1024, "%d ", pipelineLength); char* buffer = relayerSlotState + written; for (size_t ai = 0; ai != record.size(); ai++) { buffer[ai] = record.isValid(ai) ? '3' : '0'; @@ -2291,11 +2293,11 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v count++; }; - auto preUpdateStats = [ref](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t) { + auto preUpdateStats = [ref, pipelineLength](DataRelayer::RecordAction const& action, InputRecord const& record, uint64_t) { auto& states = ref.get(); std::atomic_thread_fence(std::memory_order_release); char relayerSlotState[1024]; - snprintf(relayerSlotState, 1024, "%d ", DefaultsHelpers::pipelineLength()); + snprintf(relayerSlotState, 1024, "%d ", pipelineLength); char* buffer = strchr(relayerSlotState, ' ') + 1; for (size_t ai = 0; ai != record.size(); ai++) { buffer[ai] = record.isValid(ai) ? '2' : '0'; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 05b64b6ed1dad..cece5b343659f 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -37,6 +37,7 @@ #include "Framework/DataProcessingStates.h" #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" +#include "Framework/RawDeviceService.h" #include "Headers/DataHeaderHelpers.h" #include "Framework/Formatters.h" @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +72,8 @@ constexpr int INVALID_INPUT = -1; DataRelayer::DataRelayer(const CompletionPolicy& policy, std::vector const& routes, TimesliceIndex& index, - ServiceRegistryRef services) + ServiceRegistryRef services, + int pipelineLength) : mContext{services}, mTimesliceIndex{index}, mCompletionPolicy{policy}, @@ -81,7 +84,17 @@ DataRelayer::DataRelayer(const CompletionPolicy& policy, std::scoped_lock lock(mMutex); if (policy.configureRelayer == nullptr) { - static int pipelineLength = DefaultsHelpers::pipelineLength(); + if (pipelineLength == -1) { + auto getPipelineLengthHelper = [&services]() { + try { + return DefaultsHelpers::pipelineLength(*services.get().device()->fConfig); + } catch (...) { + return DefaultsHelpers::pipelineLength(0); + } + }; + static int detectedPipelineLength = getPipelineLengthHelper(); + pipelineLength = detectedPipelineLength; + } setPipelineLength(pipelineLength); } else { policy.configureRelayer(*this); diff --git a/Framework/Core/src/DefaultsHelpers.cxx b/Framework/Core/src/DefaultsHelpers.cxx index 4dcc734216f0c..5fd1ed29e7af6 100644 --- a/Framework/Core/src/DefaultsHelpers.cxx +++ b/Framework/Core/src/DefaultsHelpers.cxx @@ -11,6 +11,9 @@ #include "Framework/DefaultsHelpers.h" #include "Framework/DataTakingContext.h" +#include "Framework/DeviceConfig.h" +#include + #include #include #include @@ -18,23 +21,35 @@ namespace o2::framework { -unsigned int DefaultsHelpers::pipelineLength() +unsigned int DefaultsHelpers::pipelineLength(unsigned int minLength) { static bool override = getenv("DPL_DEFAULT_PIPELINE_LENGTH"); if (override) { static unsigned int retval = atoi(getenv("DPL_DEFAULT_PIPELINE_LENGTH")); - return retval; + return std::max(minLength, retval); } DeploymentMode deploymentMode = DefaultsHelpers::deploymentMode(); // just some reasonable numers // The number should really be tuned at runtime for each processor. if (deploymentMode == DeploymentMode::OnlineDDS || deploymentMode == DeploymentMode::OnlineECS || deploymentMode == DeploymentMode::FST) { - return 512; + return std::max(minLength, 512u); } else { - return 64; + return std::max(minLength, 64u); } } +unsigned int DefaultsHelpers::pipelineLength(const DeviceConfig& dc) +{ + static unsigned int minLength = dc.options.count("timeframes-rate-limit") ? std::max(0, atoi(dc.options["timeframes-rate-limit"].as().c_str())) : 0; + return pipelineLength(minLength); +} + +unsigned int DefaultsHelpers::pipelineLength(const fair::mq::ProgOptions& options) +{ + static unsigned int minLength = options.Count("timeframes-rate-limit") ? std::max(0, atoi(options.GetValue("timeframes-rate-limit").c_str())) : 0; + return pipelineLength(minLength); +} + static DeploymentMode getDeploymentMode_internal() { char* explicitMode = getenv("O2_DPL_DEPLOYMENT_MODE"); diff --git a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx b/Framework/Core/src/ServiceRegistryRef.cxx similarity index 56% rename from DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx rename to Framework/Core/src/ServiceRegistryRef.cxx index 8affa29259f7a..70728ad37eda7 100644 --- a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx +++ b/Framework/Core/src/ServiceRegistryRef.cxx @@ -1,4 +1,4 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// Copyright 2019-2026 CERN and copyright holders of ALICE O2. // See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. // All rights not expressly granted are reserved. // @@ -9,11 +9,17 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -/// \file EMCALChannelData.cxx -/// \brief Class to store the data format for calibraton of the EMCal -#include "DataFormatsEMCAL/EMCALChannelData.h" +#include "Framework/ServiceRegistryRef.h" +namespace o2::framework { -using namespace o2::dataformats; +ServiceRegistryRef *ServiceRegistryRef::globalDeviceRef(ServiceRegistryRef *ref) { + static ServiceRegistryRef *globalRef = nullptr; + if (!globalRef) { + globalRef = ref; + } + // We return a copy, so that it can be cache + return globalRef; +} -ClassImp(o2::dataformats::EMCALChannelData; +} diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 166f26878c363..815fce47544d0 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -817,7 +817,8 @@ void spawnDevice(uv_loop_t* loop, .sendInitialValue = true, }); - for (size_t i = 0; i < DefaultsHelpers::pipelineLength(); ++i) { + unsigned int pipelineLength = DefaultsHelpers::pipelineLength(DeviceConfig{varmap}); + for (size_t i = 0; i < pipelineLength; ++i) { allStates.back().registerState(DataProcessingStates::StateSpec{ .name = fmt::format("matcher_variables/{}", i), .stateId = static_cast((short)(ProcessingStateId::CONTEXT_VARIABLES_BASE) + i), @@ -826,7 +827,7 @@ void spawnDevice(uv_loop_t* loop, }); } - for (size_t i = 0; i < DefaultsHelpers::pipelineLength(); ++i) { + for (size_t i = 0; i < pipelineLength; ++i) { allStates.back().registerState(DataProcessingStates::StateSpec{ .name = fmt::format("data_relayer/{}", i), .stateId = static_cast((short)(ProcessingStateId::DATA_RELAYER_BASE) + i), @@ -1247,6 +1248,7 @@ std::vector getDumpableMetrics() dumpableMetrics.emplace_back("^total-timeframes.*"); dumpableMetrics.emplace_back("^device_state.*"); dumpableMetrics.emplace_back("^total_wall_time_ms$"); + dumpableMetrics.emplace_back("^ccdb-.*$"); return dumpableMetrics; } @@ -1428,6 +1430,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, // We initialise this in the driver, because different drivers might have // different versions of the service ServiceRegistry serviceRegistry; + ServiceRegistryRef::globalDeviceRef(new ServiceRegistryRef{serviceRegistry, ServiceRegistry::globalDeviceSalt()}); if ((driverConfig.batch == false || getenv("DPL_DRIVER_REMOTE_GUI") != nullptr) && frameworkId.empty()) { debugGUI = initDebugGUI(); diff --git a/Framework/Core/test/benchmark_DataRelayer.cxx b/Framework/Core/test/benchmark_DataRelayer.cxx index dcff3930dbaad..3c3d2294fdd7e 100644 --- a/Framework/Core/test/benchmark_DataRelayer.cxx +++ b/Framework/Core/test/benchmark_DataRelayer.cxx @@ -65,7 +65,7 @@ static void BM_RelaySingleSlot(benchmark::State& state) TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -118,7 +118,7 @@ static void BM_RelayMultipleSlots(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -177,7 +177,7 @@ static void BM_RelayMultipleRoutes(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -254,7 +254,7 @@ static void BM_RelaySplitParts(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -314,7 +314,7 @@ static void BM_RelayMultiplePayloads(benchmark::State& state) auto policy = CompletionPolicyHelpers::consumeWhenAny(); ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // DataHeader matching the one provided in the input diff --git a/Framework/Core/test/test_Concepts.cxx b/Framework/Core/test/test_Concepts.cxx index ea94c4dfffe5a..982c748e701e4 100644 --- a/Framework/Core/test/test_Concepts.cxx +++ b/Framework/Core/test/test_Concepts.cxx @@ -174,6 +174,7 @@ TEST_CASE("IdentificationConcepts") REQUIRE(is_configurable_axis); REQUIRE(is_process_configurable); + REQUIRE(is_process_configurable); struct : ConfigurableGroup { Configurable c{"", 1, ""}; diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 7d5a3ded88e16..8957e361cb8a2 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -83,7 +83,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -133,7 +133,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -195,7 +195,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAll(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); @@ -276,7 +276,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAll(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(3); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); @@ -359,7 +359,7 @@ TEST_CASE("DataRelayer") std::vector infos{1}; TimesliceIndex index{1, infos}; ref.registerService(ServiceRegistryHelpers::handleForService(&index)); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(2); @@ -437,7 +437,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(2); @@ -509,7 +509,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(3); @@ -568,7 +568,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(1); @@ -629,7 +629,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::processWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); // Only two messages to fill the cache. relayer.setPipelineLength(1); @@ -698,7 +698,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); DataHeader dh{"CLUSTERS", "TPC", 0}; @@ -752,7 +752,7 @@ TEST_CASE("DataRelayer") ref.registerService(ServiceRegistryHelpers::handleForService(&index)); auto policy = CompletionPolicyHelpers::consumeWhenAny(); - DataRelayer relayer(policy, inputs, index, {registry}); + DataRelayer relayer(policy, inputs, index, {registry}, -1); relayer.setPipelineLength(4); auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); diff --git a/Framework/Foundation/include/Framework/StructToTuple.h b/Framework/Foundation/include/Framework/StructToTuple.h index 5748329f6a50d..1c7aa62260bd3 100644 --- a/Framework/Foundation/include/Framework/StructToTuple.h +++ b/Framework/Foundation/include/Framework/StructToTuple.h @@ -174,9 +174,9 @@ consteval int nested_brace_constructible_size() return brace_constructible_size() - nesting; } -template () / 10> +template () / 10, typename L> requires(D == 9) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -194,9 +194,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 8) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -214,9 +214,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 7) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -234,9 +234,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 6) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -254,9 +254,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 5) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -274,9 +274,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 4) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -294,9 +294,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 3) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -314,9 +314,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 2) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -334,9 +334,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 1) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -354,9 +354,9 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10> +template () / 10, typename L> requires(D == 0) -auto homogeneous_apply_refs(L l, T&& object) +constexpr auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -373,6 +373,12 @@ auto homogeneous_apply_refs(L l, T&& object) // clang-format on } +template +constexpr auto homogeneous_apply_refs_sized(L l, T&& object) +{ + return homogeneous_apply_refs(l, object); +} + } // namespace o2::framework #endif // O2_FRAMEWORK_STRUCTTOTUPLE_H_ diff --git a/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx b/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx index 841f4a8f2b9bd..bdc08ad45ea24 100644 --- a/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx +++ b/Framework/TestWorkflows/src/o2ParallelWorkflow.cxx @@ -9,7 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "Framework/ConcreteDataMatcher.h" #include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/Logger.h" #include #include @@ -29,13 +34,16 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"3-layer-pipelining", VariantType::Int, 1, {timeHelp}}); } +void customize(std::vector& policies) +{ + policies = { + CompletionPolicyHelpers::consumeWhenPastOldestPossibleTimeframe("merger-policy", [](auto const&) -> bool { return true; })}; +} + #include "Framework/runDataProcessing.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ParallelContext.h" -#include "Framework/ControlService.h" - -#include "Framework/Logger.h" #include @@ -43,22 +51,24 @@ using DataHeader = o2::header::DataHeader; DataProcessorSpec templateProcessor() { - return DataProcessorSpec{"some-processor", { - InputSpec{"x", "TST", "A", 0, Lifetime::Timeframe}, - }, - { + return DataProcessorSpec{.name = "some-processor", + .inputs = { + InputSpec{"x", "TST", "A", 0, Lifetime::Timeframe}, + }, + .outputs = { OutputSpec{"TST", "P", 0, Lifetime::Timeframe}, }, // The producer is stateful, we use a static for the state in this // particular case, but a Singleton or a captured new object would // work as well. - AlgorithmSpec{[](InitContext& setup) { + .algorithm = AlgorithmSpec{[](InitContext& setup) { srand(setup.services().get().index1D()); return [](ProcessingContext& ctx) { // Create a single output. size_t index = ctx.services().get().index1D(); - auto& aData = ctx.outputs().make( + auto& i = ctx.outputs().make( Output{"TST", "P", static_cast(index)}, 1); + i[0] = index; std::this_thread::sleep_for(std::chrono::seconds(rand() % 5)); }; }}}; @@ -86,34 +96,43 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) outputSpecs.emplace_back("TST", "A", ssi); } - workflow.push_back(DataProcessorSpec{"reader", {}, outputSpecs, AlgorithmSpec{[jobs](InitContext& initCtx) { - return [jobs](ProcessingContext& ctx) { - for (size_t ji = 0; ji < jobs; ++ji) { - ctx.outputs().make(Output{"TST", "A", static_cast(ji)}, - 1); - } - }; - }}}); + workflow.push_back(DataProcessorSpec{ + .name = "reader", + .outputs = outputSpecs, + .algorithm = AlgorithmSpec{[jobs](InitContext& initCtx) { + return [jobs](ProcessingContext& ctx) { + static int count = 0; + for (size_t ji = 0; ji < jobs; ++ji) { + int& i = ctx.outputs().make(Output{"TST", "A", static_cast(ji)}); + i = count * 100 + ji; + } + count++; + }; + }}}); workflow.push_back(timePipeline(DataProcessorSpec{ - "merger", - mergeInputs(InputSpec{"x", "TST", "P"}, - jobs, - [](InputSpec& input, size_t index) { - DataSpecUtils::updateMatchingSubspec(input, index); - }), - {OutputSpec{{"out"}, "TST", "M"}}, - AlgorithmSpec{[](InitContext& setup) { + .name = "merger", + .inputs = {InputSpec{"all", ConcreteDataTypeMatcher{"TST", "P"}}}, + .outputs = {OutputSpec{{"out"}, "TST", "M"}}, + .algorithm = AlgorithmSpec{[](InitContext& setup) { return [](ProcessingContext& ctx) { + LOGP(info, "Run"); + for (const auto& input : o2::framework::InputRecordWalker(ctx.inputs())) { + if (input.header == nullptr) { + LOGP(error, "Missing header"); + continue; + } + int record = *(int*)input.payload; + LOGP(info, "Record {}", record); + } ctx.outputs().make(OutputRef("out", 0), 1); }; }}}, stages)); workflow.push_back(DataProcessorSpec{ - "writer", - {InputSpec{"x", "TST", "M"}}, - {}, - AlgorithmSpec{[](InitContext& setup) { + .name = "writer", + .inputs = {InputSpec{"x", "TST", "M"}}, + .algorithm = AlgorithmSpec{[](InitContext& setup) { return [](ProcessingContext& ctx) { }; }}}); diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx index dba7e680d0b2c..c4e1775e445c3 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx @@ -74,6 +74,13 @@ int32_t GPUReconstructionCUDA::genRTC(std::string& filename, uint32_t& nCompile) } fclose(fp); } + if constexpr (std::string_view("CUDA") == "HIP") { // Check if we are RTC-compiling for HIP + if (GetProcessingSettings().hipOverrideAMDEUSperCU > 0) { + mParDevice->par_AMD_EUS_PER_CU = GetProcessingSettings().hipOverrideAMDEUSperCU; + } else if (mParDevice->par_AMD_EUS_PER_CU <= 0) { + GPUFatal("AMD_EUS_PER_CU not set in the parameters provided for the AMD GPU, you can override this via --PROChipOverrideAMDEUSperCU [n]"); + } + } const std::string launchBounds = o2::gpu::internal::GPUDefParametersExport(*mParDevice, true, mParDevice->par_AMD_EUS_PER_CU ? (mParDevice->par_AMD_EUS_PER_CU * mWarpSize) : 0) + "#define GPUCA_WARP_SIZE " + std::to_string(mWarpSize) + "\n"; if (GetProcessingSettings().rtctech.printLaunchBounds || GetProcessingSettings().debugLevel >= 3) { diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx index cd1717faf178d..51a896c2baf6a 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx @@ -43,10 +43,10 @@ int32_t TPCClusterDecompressor::decompress(const CompressedClustersFlat* cluster int32_t TPCClusterDecompressor::decompress(const CompressedClusters* clustersCompressed, o2::tpc::ClusterNativeAccess& clustersNative, std::function allocator, const GPUParam& param, bool deterministicRec) { if (clustersCompressed->nTracks && clustersCompressed->solenoidBz != -1e6f && clustersCompressed->solenoidBz != param.bzkG) { - throw std::runtime_error("Configured solenoid Bz does not match value used for track model encoding"); + throw std::runtime_error("Configured solenoid Bz " + std::to_string(param.bzkG) + " does not match value used for track model encoding " + std::to_string(clustersCompressed->solenoidBz)); } if (clustersCompressed->nTracks && clustersCompressed->maxTimeBin != -1e6 && clustersCompressed->maxTimeBin != param.continuousMaxTimeBin) { - throw std::runtime_error("Configured max time bin does not match value used for track model encoding"); + throw std::runtime_error("Configured max time bin " + std::to_string(param.continuousMaxTimeBin) + " does not match value used for track model encoding " + std::to_string(clustersCompressed->maxTimeBin)); } std::vector clusters[NSECTORS][GPUCA_ROW_COUNT]; std::atomic_flag locks[NSECTORS][GPUCA_ROW_COUNT]; diff --git a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h index ff8abdc1a491e..067a11817d7ac 100644 --- a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h +++ b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h @@ -43,7 +43,9 @@ struct GPUMemorySizeScalers { double tpcClustersPerPeak = 0.9; double tpcStartHitsPerHit = 0.08; double tpcTrackletsPerStartHit = 0.8; + double tpcTrackletsPerStartHitLowField = 0.85; double tpcTrackletHitsPerHit = 5; + double tpcTrackletHitsPerHitLowField = 7; double tpcSectorTracksPerHit = 0.02; double tpcSectorTrackHitsPerHit = 0.8; double tpcSectorTrackHitsPerHitWithRejection = 1.0; @@ -80,8 +82,8 @@ struct GPUMemorySizeScalers { inline size_t NTPCClusters(size_t tpcDigits, bool perSector = false) { return getValue(perSector ? tpcMaxSectorClusters : tpcMaxClusters, (conservative ? 1.0 : tpcClustersPerPeak) * NTPCPeaks(tpcDigits, perSector)); } inline size_t NTPCStartHits(size_t tpcHits) { return getValue(tpcMaxStartHits, tpcHits * tpcStartHitsPerHit); } inline size_t NTPCRowStartHits(size_t tpcHits) { return getValue(tpcMaxRowStartHits, std::max(NTPCStartHits(tpcHits) * (tpcHits < 30000000 ? 20 : 12) / GPUCA_ROW_COUNT, tpcMinRowStartHits)); } - inline size_t NTPCTracklets(size_t tpcHits) { return getValue(tpcMaxTracklets, NTPCStartHits(tpcHits) * tpcTrackletsPerStartHit); } - inline size_t NTPCTrackletHits(size_t tpcHits) { return getValue(tpcMaxTrackletHits, hitOffset + tpcHits * tpcTrackletHitsPerHit); } + inline size_t NTPCTracklets(size_t tpcHits, bool lowField) { return getValue(tpcMaxTracklets, NTPCStartHits(tpcHits) * (lowField ? tpcTrackletsPerStartHitLowField : tpcTrackletsPerStartHit)); } + inline size_t NTPCTrackletHits(size_t tpcHits, bool lowField) { return getValue(tpcMaxTrackletHits, hitOffset + tpcHits * (lowField ? tpcTrackletHitsPerHitLowField : tpcTrackletHitsPerHit)); } inline size_t NTPCSectorTracks(size_t tpcHits) { return getValue(tpcMaxSectorTracks, tpcHits * tpcSectorTracksPerHit); } inline size_t NTPCSectorTrackHits(size_t tpcHits, uint8_t withRejection = 0) { return getValue(tpcMaxSectorTrackHits, tpcHits * (withRejection ? tpcSectorTrackHitsPerHitWithRejection : tpcSectorTrackHitsPerHit)); } inline size_t NTPCMergedTracks(size_t tpcSectorTracks) { return getValue(tpcMaxMergedTracks, tpcSectorTracks * (conservative ? 1.0 : tpcMergedTrackPerSectorTrack)); } diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx index 70b445f7befc0..674b7a317b477 100644 --- a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx @@ -28,64 +28,33 @@ void GPUTRDRecoParam::init(float bz, const GPUSettingsRec* rec) if (CAMath::Abs(CAMath::Abs(bz) - 2) < 0.1) { if (bz > 0) { // magnetic field +0.2 T - mRPhiA2 = resRPhiIdeal2; - mRPhiB = -1.43e-2f; mRPhiC2 = 4.55e-2f; - - mDyA2 = 1.225e-3f; - mDyB = -9.8e-3f; - mDyC2 = 3.88e-2f; - - mAngleToDyA = -0.1f; - mAngleToDyB = 1.89f; - mAngleToDyC = -0.4f; } else { // magnetic field -0.2 T - mRPhiA2 = resRPhiIdeal2; - mRPhiB = 1.43e-2f; mRPhiC2 = 4.55e-2f; - - mDyA2 = 1.225e-3f; - mDyB = 9.8e-3f; - mDyC2 = 3.88e-2f; - - mAngleToDyA = 0.1f; - mAngleToDyB = 1.89f; - mAngleToDyC = 0.4f; } } else if (CAMath::Abs(CAMath::Abs(bz) - 5) < 0.1) { if (bz > 0) { // magnetic field +0.5 T - mRPhiA2 = resRPhiIdeal2; - mRPhiB = 0.125f; mRPhiC2 = 0.0961f; - - mDyA2 = 1.681e-3f; - mDyB = 0.15f; - mDyC2 = 0.1849f; - - mAngleToDyA = 0.13f; - mAngleToDyB = 2.43f; - mAngleToDyC = -0.58f; } else { // magnetic field -0.5 T - mRPhiA2 = resRPhiIdeal2; - mRPhiB = -0.14f; mRPhiC2 = 0.1156f; - - mDyA2 = 2.209e-3f; - mDyB = -0.15f; - mDyC2 = 0.2025f; - - mAngleToDyA = -0.15f; - mAngleToDyB = 2.34f; - mAngleToDyC = 0.56f; } } else { LOGP(warning, "No error parameterization available for Bz= {}. Keeping default value (sigma_y = const. = 1cm)", bz); } - LOGP(info, "Loaded parameterizations for Bz={}: PhiRes:[{},{},{}] DyRes:[{},{},{}] Angle2Dy:[{},{},{}]", - bz, mRPhiA2, mRPhiB, mRPhiC2, mDyA2, mDyB, mDyC2, mAngleToDyA, mAngleToDyB, mAngleToDyC); + + mRPhiA2 = resRPhiIdeal2; + mLorentzAngle = -0.02f + 0.13f * bz / 5.f; + + mDyA2 = 6e-3f; + mDyC2 = 0.3f; + mCorrYDyA = 0.27f; + mCorrYDyC = -0.44f; + + LOGP(info, "Loaded parameterizations for Bz={}: PhiRes:[{},{},{}] DyRes:[{},{},{}] CorrYDy:[{},{},{}]", + bz, mRPhiA2, mLorentzAngle, mRPhiC2, mDyA2, mLorentzAngle, mDyC2, mCorrYDyA, mLorentzAngle, mCorrYDyC); } void GPUTRDRecoParam::recalcTrkltCov(const float tilt, const float snp, const float rowSize, float* cov) const diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h index ad0285487d3c3..a0a8e71143d94 100644 --- a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h @@ -19,6 +19,7 @@ #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" #include "GPUCommonArray.h" +#include "GPUCommonMath.h" namespace o2 { @@ -51,31 +52,30 @@ class GPUTRDRecoParam /// more details are given in http://cds.cern.ch/record/2724259 in section 5.3.3 /// \param phi angle of related track /// \return sigma_y^2 of tracklet - GPUd() float getRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mRPhiB) * (snp - mRPhiB)); } - GPUd() float getDyRes(float snp) const { return mDyA2 + mDyC2 * (snp - mDyB) * (snp - mDyB); } // // a^2 + c^2 * (snp - b)^2 - GPUd() float convertAngleToDy(float snp) const { return mAngleToDyA + mAngleToDyB * snp + mAngleToDyC * snp * snp; } // a + b*snp + c*snp^2 is more accurate than sin(phi) = (dy / xDrift) / sqrt(1+(dy/xDrift)^2) + GPUd() float getRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mLorentzAngle) * (snp - mLorentzAngle)); } + GPUd() float getDyRes(float snp) const { return mDyA2 + mDyC2 * (snp - mLorentzAngle) * (snp - mLorentzAngle); } // a^2 + c^2 * (snp - b)^2 + GPUd() float convertAngleToDy(float snp) const { return 3.f * snp / CAMath::Sqrt(1 - snp * snp); } // when calibrated, sin(phi) = (dy / xDrift) / sqrt(1+(dy/xDrift)^2) works well + GPUd() float getCorrYDy(float snp) const { return mCorrYDyA + mCorrYDyC * (snp - mLorentzAngle) * (snp - mLorentzAngle); } // a + c * (snp - b)^2 /// Get tracklet z correction coefficient for track-eta based corraction GPUd() float getZCorrCoeffNRC() const { return mZCorrCoefNRC; } private: // tracklet error parameterization depends on the magnetic field + float mLorentzAngle{0.f}; // rphi float mRPhiA2{1.f}; ///< parameterization for tracklet position resolution - float mRPhiB{0.f}; ///< parameterization for tracklet position resolution float mRPhiC2{0.f}; ///< parameterization for tracklet position resolution // angle float mDyA2{1.225e-3f}; ///< parameterization for tracklet angular resolution - float mDyB{0.f}; ///< parameterization for tracklet angular resolution float mDyC2{0.f}; ///< parameterization for tracklet angular resolution - // angle to Dy - float mAngleToDyA; // parameterization for conversion track angle -> tracklet deflection - float mAngleToDyB; // parameterization for conversion track angle -> tracklet deflection - float mAngleToDyC; // parameterization for conversion track angle -> tracklet deflection + // correlation coefficient between y residual and dy residual + float mCorrYDyA{0.f}; + float mCorrYDyC{0.f}; float mZCorrCoefNRC{1.4f}; ///< tracklet z-position depends linearly on track dip angle - ClassDefNV(GPUTRDRecoParam, 2); + ClassDefNV(GPUTRDRecoParam, 3); }; } // namespace gpu diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index c61056466929e..cf6b913551ab5 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -184,6 +184,7 @@ AddOptionRTC(extraRoadZ, float, 10.f, "", 0, "Addition to search road around tra AddOptionRTC(trkltResRPhiIdeal, float, 1.f, "", 0, "Optimal tracklet rphi resolution in cm (in case phi of track = lorentz angle)") AddOptionRTC(maxChi2Red, float, 99.f, "", 0, "maximum chi2 per attached tracklet for TRD tracks TODO: currently effectively disabled, requires tuning") AddOptionRTC(applyDeflectionCut, uint8_t, 0, "", 0, "Set to 1 to enable tracklet selection based on deflection") +AddOptionRTC(addDeflectionInChi2, uint8_t, 0, "", 0, "Set to 1 to add the deflection in the chi2 calculation for matching") AddOptionRTC(stopTrkAfterNMissLy, uint8_t, 6, "", 0, "Abandon track following after N layers without a TRD match") AddOptionRTC(nTrackletsMin, uint8_t, 3, "", 0, "Tracks with less attached tracklets are discarded after the tracking") AddOptionRTC(matCorrType, uint8_t, 2, "", 0, "Material correction to use: 0 - none, 1 - TGeo, 2 - matLUT") @@ -372,6 +373,7 @@ AddOption(tpcWriteClustersAfterRejection, bool, false, "", 0, "Apply TPC rejecti AddOption(oclPlatformNum, int32_t, -1, "", 0, "Platform to use, in case the backend provides multiple platforms (OpenCL only, -1 = auto-select, -2 query all platforms (also incompatible))") AddOption(oclCompileFromSources, bool, false, "", 0, "Compile OpenCL binary from included source code instead of using included spirv code") AddOption(oclOverrideSourceBuildFlags, std::string, "", "", 0, "Override OCL build flags for compilation from source, put a space for empty options") +AddOption(hipOverrideAMDEUSperCU, int32_t, -1, "", 0, "Override AMD_EUS_PER_CU setting") AddOption(printSettings, bool, false, "", 0, "Print all settings when initializing") AddOption(tpcFreeAllocatedMemoryAfterProcessing, bool, false, "", 0, "Clean all memory allocated by TPC when TPC processing done, only data written to external output resources will remain") AddOption(debugOnFailure, int32_t, 0, "", 0, "Dump raw data in case an error occured, bit 1 enables all dumps, otherwise bitmask for: 2 = signal, 3 = GPUErrorCode", def(1)) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index ca1352b3bda1b..89d47d0e1b86c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -273,10 +273,10 @@ int32_t GPUChainTracking::RunTPCDecompression() CompressedClusters& inputGPUShadow = DecompressorShadow.mInputGPU; if (cmprClsHost.nTracks && cmprClsHost.solenoidBz != -1e6f && cmprClsHost.solenoidBz != param().bzkG) { - throw std::runtime_error("Configured solenoid Bz does not match value used for track model encoding"); + throw std::runtime_error("Configured solenoid Bz " + std::to_string(param().bzkG) + " does not match value used for track model encoding " + std::to_string(cmprClsHost.solenoidBz)); } if (cmprClsHost.nTracks && cmprClsHost.maxTimeBin != -1e6 && cmprClsHost.maxTimeBin != param().continuousMaxTimeBin) { - throw std::runtime_error("Configured max time bin does not match value used for track model encoding"); + throw std::runtime_error("Configured max time bin " + std::to_string(param().continuousMaxTimeBin) + " does not match value used for track model encoding " + std::to_string(cmprClsHost.maxTimeBin)); } int32_t inputStream = 0; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 260781c17406b..3622e51bd663f 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -555,7 +555,7 @@ GPUd() int32_t GPUTPCGMMerger::RefitSectorTrack(GPUTPCGMSectorTrack& sectorTrack prop.SetMaterialTPC(); prop.SetMaxSinPhi(GPUCA_MAX_SIN_PHI); prop.SetSeedingErrors(true); // Larger errors for seeds, better since we don't start with good hypothesis - prop.SetFitInProjections(false); + prop.SetFitInProjections(true); // TODO: Was false, consider reenabling after fitInProjection is fixed prop.SetPolynomialField(&Param().polynomialField); GPUTPCGMTrackParam trk; trk.X() = inTrack->Param().GetX(); @@ -718,9 +718,6 @@ GPUd() void GPUTPCGMMerger::MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nT } else if (iBorder == 3) { // transport to the middle of the sector and rotate vertically to the border on the right dAlpha = -dAlpha; x0 = GPUTPCGeometry::Row2X(63); - } else if (iBorder == 4) { // transport to the middle of the sßector, w/o rotation - dAlpha = 0; - x0 = GPUTPCGeometry::Row2X(63); } const float maxSin = CAMath::Sin(60.f / 180.f * CAMath::Pi()); @@ -783,14 +780,14 @@ GPUd() void GPUTPCGMMerger::MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nT } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { CADEBUG(GPUInfo("\nMERGING Sectors %d %d NTracks %d %d CROSS %d", iSector1, iSector2, N1, N2, mergeMode)); GPUTPCGMBorderRange* range1 = mBorderRange[iSector1]; GPUTPCGMBorderRange* range2 = mBorderRange[iSector2] + *GetConstantMem()->tpcTrackers[iSector2].NTracks(); bool sameSector = (iSector1 == iSector2); for (int32_t itr = iBlock * nThreads + iThread; itr < N1; itr += nThreads * nBlocks) { - GPUTPCGMBorderTrack& b = B1[itr]; + const GPUTPCGMBorderTrack& b = B1[itr]; float d = CAMath::Max(0.5f, 3.5f * CAMath::Sqrt(b.Cov()[1])); if (CAMath::Abs(b.Par()[4]) * Param().qptB5Scaler >= 20) { d *= 2; @@ -809,7 +806,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThrea } if (!sameSector) { for (int32_t itr = iBlock * nThreads + iThread; itr < N2; itr += nThreads * nBlocks) { - GPUTPCGMBorderTrack& b = B2[itr]; + const GPUTPCGMBorderTrack& b = B2[itr]; float d = CAMath::Max(0.5f, 3.5f * CAMath::Sqrt(b.Cov()[1])); if (CAMath::Abs(b.Par()[4]) * Param().qptB5Scaler >= 20) { d *= 2; @@ -827,7 +824,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<0>(int32_t nBlocks, int32_t nThrea } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<1>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<1>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { #if !defined(GPUCA_GPUCODE_COMPILEKERNELS) GPUTPCGMBorderRange* range1 = mBorderRange[iSector1]; @@ -860,7 +857,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<3>(int32_t nBlocks, int32_t nThrea } template <> -GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) +GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode) { // int32_t statAll = 0, statMerged = 0; float factor2ys = Param().rec.tpc.trackMergerFactor2YS; @@ -887,7 +884,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThrea i2++; } - GPUTPCGMBorderTrack& b1 = B1[r1.fId]; + const GPUTPCGMBorderTrack& b1 = B1[r1.fId]; if (b1.NClusters() < minNPartHits) { continue; } @@ -904,7 +901,7 @@ GPUd() void GPUTPCGMMerger::MergeBorderTracks<2>(int32_t nBlocks, int32_t nThrea } // do check - GPUTPCGMBorderTrack& b2 = B2[r2.fId]; + const GPUTPCGMBorderTrack& b2 = B2[r2.fId]; #if defined(GPUCA_MERGER_BY_MC_LABEL) && !defined(GPUCA_GPUCODE) int64_t label1 = GetTrackLabel(b1); int64_t label2 = GetTrackLabel(b2); @@ -1019,7 +1016,7 @@ GPUd() void GPUTPCGMMerger::MergeWithinSectorsPrepare(int32_t nBlocks, int32_t n const float maxSin = CAMath::Sin(60.f / 180.f * CAMath::Pi()); for (int32_t itr = iBlock * nThreads + iThread; itr < SectorTrackInfoLocalTotal(); itr += nThreads * nBlocks) { - GPUTPCGMSectorTrack& track = mSectorTrackInfos[itr]; + const GPUTPCGMSectorTrack& track = mSectorTrackInfos[itr]; int32_t iSector = track.Sector(); GPUTPCGMBorderTrack b; if (track.TransportToX(this, x0, Param().bzCLight, b, maxSin)) { @@ -1852,7 +1849,7 @@ GPUd() void GPUTPCGMMerger::PrepareForFit1(int32_t nBlocks, int32_t nThreads, in if (CAMath::Abs(trk.GetParam().GetQPt() * Param().qptB5Scaler) <= Param().rec.tpc.rejectQPtB5 && !trk.MergedLooper() && trk.Leg() == 0) { weight |= attachProtect; } - mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num] = weight; + CAMath::AtomicMax(&mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num], weight); CAMath::AtomicAdd(&mSharedCount[mClusters[trk.FirstClusterRef() + j].num], 1u); } if (!trk.CCE() && !trk.MergedLooper()) { diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index 14974bdec2303..8f554c24c1d8c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h @@ -199,6 +199,8 @@ class GPUTPCGMMerger : public GPUProcessor void DumpRefit(std::ostream& out) const; void DumpFinal(std::ostream& out) const; void DumpLoopers(std::ostream& out) const; + void DumpTrackParam(std::ostream& out) const; + void DumpTrackClusters(std::ostream& out, bool non0StateOnly = false, bool noNDF0 = false) const; template void MergedTrackStreamerInternal(const GPUTPCGMBorderTrack& b1, const GPUTPCGMBorderTrack& b2, const char* name, int32_t sector1, int32_t sector2, int32_t mergeMode, float weight, float frac) const; @@ -224,7 +226,7 @@ class GPUTPCGMMerger : public GPUProcessor private: GPUd() void MergeSectorsPrepareStep2(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iBorder, GPUTPCGMBorderTrack** B, GPUAtomic(uint32_t) * nB, bool useOrigTrackParam = false); template - GPUd() void MergeBorderTracks(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode = 0); + GPUd() void MergeBorderTracks(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, int32_t iSector1, const GPUTPCGMBorderTrack* B1, int32_t N1, int32_t iSector2, const GPUTPCGMBorderTrack* B2, int32_t N2, int32_t mergeMode = 0); GPUd() void MergeCEFill(const GPUTPCGMSectorTrack* track, const GPUTPCGMMergedTrackHit& cls, int32_t itr); diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx index 0a83bf47f5725..f6afc46609a11 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx @@ -43,10 +43,10 @@ using namespace gputpcgmmergertypes; void GPUTPCGMMerger::DumpSectorTracks(std::ostream& out) const { std::streamsize ss = out.precision(); - out << std::setprecision(2); + out << std::setprecision(10); out << "\nTPC Merger Sector Tracks\n"; for (int32_t iSector = 0; iSector < NSECTORS; iSector++) { - out << "Sector Track Info Index " << (mSectorTrackInfoIndex[iSector + 1] - mSectorTrackInfoIndex[iSector]) << " / " << (mSectorTrackInfoIndex[NSECTORS + iSector + 1] - mSectorTrackInfoIndex[NSECTORS + iSector]) << "\n"; + out << "Sector Track Info Sector " << iSector << " Index " << (mSectorTrackInfoIndex[iSector + 1] - mSectorTrackInfoIndex[iSector]) << " / " << (mSectorTrackInfoIndex[NSECTORS + iSector + 1] - mSectorTrackInfoIndex[NSECTORS + iSector]) << "\n"; for (int32_t iGlobal = 0; iGlobal < 2; iGlobal++) { out << " Track type " << iGlobal << "\n"; for (int32_t j = mSectorTrackInfoIndex[iSector + NSECTORS * iGlobal]; j < mSectorTrackInfoIndex[iSector + NSECTORS * iGlobal + 1]; j++) { @@ -134,9 +134,14 @@ void GPUTPCGMMerger::DumpMergedBetweenSectors(std::ostream& out) const void GPUTPCGMMerger::DumpCollected(std::ostream& out) const { - std::streamsize ss = out.precision(); - out << std::setprecision(6); out << "\nTPC Merger Collected Tracks\n"; + DumpTrackParam(out); +} + +void GPUTPCGMMerger::DumpTrackParam(std::ostream& out) const +{ + std::streamsize ss = out.precision(); + out << std::setprecision(10); for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { const auto& trk = mMergedTracks[i]; const auto& p = trk.GetParam(); @@ -157,33 +162,42 @@ void GPUTPCGMMerger::DumpMergeCE(std::ostream& out) const } } -void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const +void GPUTPCGMMerger::DumpTrackClusters(std::ostream& out, bool non0StateOnly, bool noNDF0) const { - out << "\nTPC Merger Refit Prepare\n"; - out << " Sort\n"; - for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { - out << " " << i << ": " << mTrackOrderAttach[i] << "\n"; - } - out << " Clusters\n"; for (uint32_t j = 0; j < mMemory->nMergedTracks; j++) { const auto& trk = mMergedTracks[j]; - out << " Track " << j << ": "; + if (trk.NClusters() == 0) { + continue; + } + if (noNDF0 && (!trk.OK() || trk.GetParam().GetNDF() < 0)) { + continue; + } + out << " Track " << j << ": (" << trk.NClusters() << "): "; for (uint32_t i = trk.FirstClusterRef(); i < trk.FirstClusterRef() + trk.NClusters(); i++) { - out << j << "/" << (i - trk.FirstClusterRef()) << ": " << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; + if (!non0StateOnly || mClusters[i].state != 0) { + out << j << "/" << (i - trk.FirstClusterRef()) << ": " << (int32_t)mClusters[i].row << "/" << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; + } } out << "\n"; } - uint32_t maxId = mNMaxClusters; +} + +void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const +{ + out << "\nTPC Merger Refit Prepare\n"; + out << " Sort\n"; + for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { + out << " " << i << ": " << mTrackOrderAttach[i] << "\n"; + } + out << " Track Clusters"; + DumpTrackClusters(out); uint32_t j = 0; - for (uint32_t i = 0; i < maxId; i++) { + for (uint32_t i = 0; i < mNMaxClusters; i++) { if ((mClusterAttachment[i] & attachFlagMask) != 0) { - if (++j % 10 == 0) { - out << " Cluster attachment "; + if (j++ % 10 == 0) { + out << "\n Cluster attachment "; } out << i << ": " << (mClusterAttachment[i] & attachTrackMask) << " / " << (mClusterAttachment[i] & attachFlagMask) << " - "; - if (j % 10 == 0) { - out << "\n"; - } } } out << "\n"; @@ -192,7 +206,7 @@ void GPUTPCGMMerger::DumpFitPrepare(std::ostream& out) const void GPUTPCGMMerger::DumpRefit(std::ostream& out) const { std::streamsize ss = out.precision(); - out << std::setprecision(2); + out << std::setprecision(10); out << "\nTPC Merger Refit\n"; for (uint32_t i = 0; i < mMemory->nMergedTracks; i++) { const auto& trk = mMergedTracks[i]; @@ -224,22 +238,10 @@ void GPUTPCGMMerger::DumpLoopers(std::ostream& out) const void GPUTPCGMMerger::DumpFinal(std::ostream& out) const { out << "\nTPC Merger Finalized\n"; - for (uint32_t j = 0; j < mMemory->nMergedTracks; j++) { - const auto& trk = mMergedTracks[j]; - if (trk.NClusters() == 0) { - continue; - } - out << " Track " << j << ": "; - for (uint32_t i = trk.FirstClusterRef(); i < trk.FirstClusterRef() + trk.NClusters(); i++) { - if (mClusters[i].state != 0) { - out << j << "/" << (i - trk.FirstClusterRef()) << ": " << mClusters[i].num << "/" << (int32_t)mClusters[i].state << ", "; - } - } - out << "\n"; - } - uint32_t maxId = mNMaxClusters; + out << "Track Clusters\n"; + DumpTrackClusters(out, true); uint32_t j = 0; - for (uint32_t i = 0; i < maxId; i++) { + for (uint32_t i = 0; i < mNMaxClusters; i++) { if ((mClusterAttachment[i] & attachFlagMask) != 0) { if (++j % 10 == 0) { out << " Cluster attachment "; diff --git a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h index 60febbb4428f6..84102cd14ce5c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h @@ -54,6 +54,7 @@ class GPUTPCGMSectorTrack GPUd() float SecPhi() const { return mParam.mSecPhi; } GPUd() float DzDs() const { return mParam.mDzDs; } GPUd() float QPt() const { return mParam.mQPt; } + GPUd() const auto& Param() const { return mParam; } GPUd() float TOffset() const { return mTOffset; } GPUd() int32_t LocalTrackId() const { return mLocalTrackId; } @@ -75,7 +76,7 @@ class GPUTPCGMSectorTrack GPUd() void Set(const GPUTPCGMTrackParam& trk, const GPUTPCTrack* sectorTr, float alpha, int32_t sector); GPUd() void SetParam2(const GPUTPCGMTrackParam& trk); GPUd() void Set(const GPUTPCGMMerger* merger, const GPUTPCTrack* sectorTr, float alpha, int32_t sector); - GPUd() void UseParam2() { mParam = mParam2; } + GPUd() void UseParam2() { mParam = mParam2; } // TODO: Clean this up! GPUd() void SetX2(float v) { mParam2.mX = v; } GPUd() float X2() const { return mParam2.mX; } diff --git a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx index 03931f73a4a12..506f90c55abf3 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx @@ -144,8 +144,9 @@ void GPUTPCTracker::SetMaxData(const GPUTrackingInOutPointers& io) } else { mNMaxRowStartHits = mRec->MemoryScalers()->NTPCRowStartHits(mData.NumberOfHits()); } - mNMaxTracklets = mRec->MemoryScalers()->NTPCTracklets(mData.NumberOfHits()); - mNMaxRowHits = mRec->MemoryScalers()->NTPCTrackletHits(mData.NumberOfHits()); + bool lowField = CAMath::Abs(Param().bzkG) < 4; + mNMaxTracklets = mRec->MemoryScalers()->NTPCTracklets(mData.NumberOfHits(), lowField); + mNMaxRowHits = mRec->MemoryScalers()->NTPCTrackletHits(mData.NumberOfHits(), lowField); mNMaxTracks = mRec->MemoryScalers()->NTPCSectorTracks(mData.NumberOfHits()); if (io.clustersNative) { uint32_t sectorOffset = mISector >= GPUCA_NSECTORS / 2 ? GPUCA_NSECTORS / 2 : 0; diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h b/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h index d45959f99b056..a7b0166122c1b 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h @@ -151,6 +151,7 @@ class trackInterface : public GPUTPCGMTrackParam GPUd() float getPt() const { return CAMath::Abs(getQ2Pt()) > 0 ? CAMath::Abs(1.f / getQ2Pt()) : 99999.f; } GPUd() float getSigmaY2() const { return GetErr2Y(); } GPUd() float getSigmaZ2() const { return GetErr2Z(); } + GPUd() float getSigmaZY() const { return GetCov(1); } GPUd() const float* getPar() const { return GetPar(); } GPUd() const float* getCov() const { return GetCov(); } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx index d5d400e30df53..80098ff151ebe 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx @@ -93,7 +93,7 @@ void* GPUTRDTracker_t::SetPointersTracks(void* base) } template -GPUTRDTracker_t::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mGenerateSpacePoints(false), mProcessPerTimeFrame(false), mNAngleHistogramBins(25), mAngleHistogramRange(50), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(0), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mTrackAttribs(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mMaxBackendThreads(100), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mGeo(nullptr), mRecoParam(nullptr), mDebugOutput(false), mMaxEta(0.84f), mRoadZ(18.f), mZCorrCoefNRC(1.4f), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug()) +GPUTRDTracker_t::GPUTRDTracker_t() : mR(nullptr), mIsInitialized(false), mGenerateSpacePoints(false), mProcessPerTimeFrame(false), mNAngleHistogramBins(25), mAngleHistogramRange(50), mMemoryPermanent(-1), mMemoryTracklets(-1), mMemoryTracks(-1), mNMaxCollisions(0), mNMaxTracks(0), mNMaxSpacePoints(0), mTracks(nullptr), mTrackAttribs(nullptr), mNCandidates(1), mNTracks(0), mNEvents(0), mMaxBackendThreads(100), mTrackletIndexArray(nullptr), mHypothesis(nullptr), mCandidates(nullptr), mSpacePoints(nullptr), mGeo(nullptr), mRecoParam(nullptr), mDebugOutput(false), mMaxEta(0.84f), mRoadZ(18.f), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug()) { //-------------------------------------------------------------------- // Default constructor @@ -579,12 +579,14 @@ GPUd() bool GPUTRDTracker_t::FollowProlongation(PROP* prop, TRDTRK prop->getPropagatedYZ(spacePoints[trkltIdx].getX(), projY, projZ); // correction for tilted pads (only applied if deltaZ < lPad && track z err << lPad) float tiltCorr = tilt * (spacePoints[trkltIdx].getZ() - projZ); + float dyTiltCorr = tilt * trkWork->getTgl() * mGeo->GetCdrHght(); float lPad = pad->GetRowSize(tracklets[trkltIdx].GetZbin()); if (!((CAMath::Abs(spacePoints[trkltIdx].getZ() - projZ) < lPad) && (trkWork->getSigmaZ2() < (lPad * lPad / 12.f)))) { tiltCorr = 0.f; // will be zero also for TPC tracks which are shifted in z + dyTiltCorr = 0.f; } // correction for mean z position of tracklet (is not the center of the pad if track eta != 0) - float zPosCorr = spacePoints[trkltIdx].getZ() + mZCorrCoefNRC * trkWork->getTgl(); + float zPosCorr = spacePoints[trkltIdx].getZ() + mRecoParam->getZCorrCoeffNRC() * trkWork->getTgl(); float yPosCorr = spacePoints[trkltIdx].getY() - tiltCorr; zPosCorr -= zShiftTrk; // shift tracklet instead of track in order to avoid having to do a re-fit for each collision float deltaY = yPosCorr - projY; @@ -595,8 +597,22 @@ GPUd() bool GPUTRDTracker_t::FollowProlongation(PROP* prop, TRDTRK // tracklet is in windwow: get predicted chi2 for update and store tracklet index if best guess RecalcTrkltCov(tilt, trkWork->getSnp(), pad->GetRowSize(tracklets[trkltIdx].GetZbin()), trkltCovTmp); float chi2 = prop->getPredictedChi2(trkltPosTmpYZ, trkltCovTmp); + if (Param().rec.trd.addDeflectionInChi2 && (trkWork->getSnp() < 1.f - 1e-6f) && (trkWork->getSnp() > -1.f + 1e-6f)) { + // we add the slope in the chi2 calculation + float trkltCovTmpWithDy[6] = {trkltCovTmp[0], trkltCovTmp[1], trkltCovTmp[2], 0.f, 0.f, 0.f}; + RecalcTrkltCovDy(tilt, trkWork->getSnp(), trkltCovTmpWithDy); + trkltCovTmpWithDy[0] += trkWork->getSigmaY2(); + trkltCovTmpWithDy[1] += trkWork->getSigmaZY(); + trkltCovTmpWithDy[2] += trkWork->getSigmaZ2(); + + // For now, dy uncertainty parametrization also includes track uncertainty, so no need to add additional uncertainty + if (InvertCov(trkltCovTmpWithDy)) { + float deltaDy = spacePoints[trkltIdx].getDy() + dyTiltCorr - mRecoParam->convertAngleToDy(trkWork->getSnp()); + chi2 = deltaY * trkltCovTmpWithDy[0] * deltaY + 2 * deltaY * trkltCovTmpWithDy[1] * deltaZ + 2 * deltaY * trkltCovTmpWithDy[3] * deltaDy + deltaZ * trkltCovTmpWithDy[2] * deltaZ + 2 * deltaZ * trkltCovTmpWithDy[4] * deltaDy + deltaDy * trkltCovTmpWithDy[5] * deltaDy; + } + } // TODO cut on angular pull should be made stricter when proper v-drift calibration for the TRD tracklets is implemented - if ((chi2 > Param().rec.trd.maxChi2) || (Param().rec.trd.applyDeflectionCut && CAMath::Abs(GetAngularPull(spacePoints[trkltIdx].getDy(), trkWork->getSnp())) > 4)) { + if ((chi2 > Param().rec.trd.maxChi2) || (Param().rec.trd.applyDeflectionCut && CAMath::Abs(GetAngularPull(spacePoints[trkltIdx].getDy() + dyTiltCorr, trkWork->getSnp())) > 4)) { continue; } Hypothesis hypo(trkWork->getNlayersFindable(), iCandidate, trkltIdx, trkWork->getChi2() + chi2); @@ -674,7 +690,7 @@ GPUd() bool GPUTRDTracker_t::FollowProlongation(PROP* prop, TRDTRK pad = mGeo->GetPadPlane(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector()); float tiltCorrUp = tilt * (spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() - trkWork->getZ()); - float zPosCorrUp = spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() + mZCorrCoefNRC * trkWork->getTgl(); + float zPosCorrUp = spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() + mRecoParam->getZCorrCoeffNRC() * trkWork->getTgl(); zPosCorrUp -= zShiftTrk; float padLength = pad->GetRowSize(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetZbin()); if (!((trkWork->getSigmaZ2() < (padLength * padLength / 12.f)) && (CAMath::Abs(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() - trkWork->getZ()) < padLength))) { @@ -938,6 +954,69 @@ GPUd() void GPUTRDTracker_t::RecalcTrkltCov(const float tilt, cons cov[2] = c2 * (t2 * sy2 + sz2); } +template +GPUd() void GPUTRDTracker_t::RecalcTrkltCovDy(const float tilt, const float snp, float (&cov)[6]) +{ + float t2 = tilt * tilt; // tan^2 (tilt) + float c2 = 1.f / (1.f + t2); // cos^2 (tilt) + float sy2 = mRecoParam->getRPhiRes(snp); + float sdy2 = mRecoParam->getDyRes(snp); + cov[3] = mRecoParam->getCorrYDy(snp) * CAMath::Sqrt(sdy2 * c2 * sy2); + cov[4] = -tilt * mRecoParam->getCorrYDy(snp) * CAMath::Sqrt(sdy2 * c2 * sy2); + cov[5] = sdy2; +} + +template +GPUd() bool GPUTRDTracker_t::InvertCov(float (&cov)[6]) +{ + // invert a 3*3 symmetric matrix. Adapted from https://root.cern.ch/doc/master/TMatrixTSymCramerInv_8cxx_source.html + + float c00 = cov[2] * cov[5] - cov[4] * cov[4]; + float c01 = cov[4] * cov[3] - cov[1] * cov[5]; + float c02 = cov[1] * cov[4] - cov[2] * cov[3]; + float c11 = cov[5] * cov[0] - cov[3] * cov[3]; + float c12 = cov[3] * cov[1] - cov[4] * cov[0]; + float c22 = cov[0] * cov[2] - cov[1] * cov[1]; + + float t0 = CAMath::Abs(cov[0]); + float t1 = CAMath::Abs(cov[1]); + float t2 = CAMath::Abs(cov[3]); + + float det; + float tmp; + + if (t0 >= t1) { + if (t2 >= t0) { + tmp = cov[3]; + det = c12 * c01 - c11 * c02; + } else { + tmp = cov[0]; + det = c11 * c22 - c12 * c12; + } + } else if (t2 >= t1) { + tmp = cov[3]; + det = c12 * c01 - c11 * c02; + } else { + tmp = cov[1]; + det = c02 * c12 - c01 * c22; + } + + if (det == 0 || tmp == 0) { + return false; + } + + float s = tmp / det; + + cov[0] = s * c00; + cov[1] = s * c01; + cov[3] = s * c02; + cov[2] = s * c11; + cov[4] = s * c12; + cov[5] = s * c22; + + return true; +} + template GPUd() float GPUTRDTracker_t::GetAngularPull(float dYtracklet, float snp) const { diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h index 5d7530ccecc11..f698e570d2158 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h @@ -117,6 +117,8 @@ class GPUTRDTracker_t : public GPUProcessor GPUd() float GetAlphaOfSector(const int32_t sec) const; GPUd() float GetAngularPull(float dYtracklet, float snp) const; GPUd() void RecalcTrkltCov(const float tilt, const float snp, const float rowSize, float (&cov)[3]); + GPUd() void RecalcTrkltCovDy(const float tilt, const float snp, float (&cov)[6]); + GPUd() bool InvertCov(float (&cov)[6]); GPUd() void FindChambersInRoad(const TRDTRK* t, const float roadY, const float roadZ, const int32_t iLayer, int32_t* det, const float zMax, const float alpha, const float zShiftTrk) const; GPUd() bool IsGeoFindable(const TRDTRK* t, const int32_t layer, const float alpha, const float zShiftTrk) const; GPUd() void InsertHypothesis(Hypothesis hypo, int32_t& nCurrHypothesis, int32_t idxOffset); @@ -172,13 +174,11 @@ class GPUTRDTracker_t : public GPUProcessor TRDTRK* mCandidates; // array of tracks for multiple hypothesis tracking GPUTRDSpacePoint* mSpacePoints; // array with tracklet coordinates in global tracking frame const GPUTRDGeometry* mGeo; // TRD geometry - const GPUTRDRecoParam* mRecoParam; // TRD RecoParam - /// ---- end error parametrization ---- + const GPUTRDRecoParam* mRecoParam; // TRD RecoParam bool mDebugOutput; // store debug output static constexpr const float sRadialOffset = -0.1f; // due to (possible) mis-calibration of t0 -> will become obsolete when tracklet conversion is done outside of the tracker float mMaxEta; // TPC tracks with higher eta are ignored float mRoadZ; // in z, a constant search road is used - float mZCorrCoefNRC; // tracklet z-position depends linearly on track dip angle float mTPCVdrift; // TPC drift velocity used for shifting TPC tracks along Z float mTPCTDriftOffset; // TPC drift time additive offset GPUTRDTrackerDebug* mDebug; // debug output diff --git a/GPU/GPUTracking/cmake/vulkan_display.cmake b/GPU/GPUTracking/cmake/vulkan_display.cmake index 7cbfd0328c323..7859742363755 100644 --- a/GPU/GPUTracking/cmake/vulkan_display.cmake +++ b/GPU/GPUTracking/cmake/vulkan_display.cmake @@ -27,7 +27,7 @@ function(add_glslc_shader TARGET SHADER) OUTPUT ${spirv-file} COMMAND ${Vulkan_GLSLC_EXECUTABLE} -o ${spirv-file} ${input-file-abs} -MD -MT ${spirv-file} -MF ${spirv-file}.d DEPENDS ${input-file-abs} - DEPFILE ${input-file-abs}.d + DEPFILE ${spirv-file}.d COMMENT "Compiling GLSL to SPIRV: ${SHADER}" VERBATIM ) diff --git a/Generators/include/Generators/GeneratorFromFile.h b/Generators/include/Generators/GeneratorFromFile.h index 3b469751a4d47..706557ea2484b 100644 --- a/Generators/include/Generators/GeneratorFromFile.h +++ b/Generators/include/Generators/GeneratorFromFile.h @@ -87,11 +87,10 @@ class GeneratorFromO2Kine : public o2::eventgen::Generator void SetStartEvent(int start); void setContinueMode(bool val) { mContinueMode = val; }; - - private: /** methods that can be overridden **/ void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override; + private: TFile* mEventFile = nullptr; //! the file containing the persistent events TBranch* mEventBranch = nullptr; //! the branch containing the persistent events TBranch* mMCHeaderBranch = nullptr; //! branch containing MC event headers @@ -143,6 +142,11 @@ class GeneratorFromEventPool : public o2::eventgen::Generator return import_good; } + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override + { + mO2KineGenerator->updateHeader(eventHeader); + } + // determine the collection of available files std::vector setupFileUniverse(std::string const& path) const; diff --git a/Generators/include/Generators/GeneratorService.h b/Generators/include/Generators/GeneratorService.h index a0037707bcdd6..13ebe054f2940 100644 --- a/Generators/include/Generators/GeneratorService.h +++ b/Generators/include/Generators/GeneratorService.h @@ -20,6 +20,7 @@ #include #include // could be forward declaration #include +#include namespace o2 { @@ -66,6 +67,7 @@ class GeneratorService std::pair, o2::dataformats::MCEventHeader> generateEvent(); void generateEvent_MCTracks(std::vector& tracks, o2::dataformats::MCEventHeader& header); + void generateEvent_MCTracks(o2::pmr::vector& tracks, o2::dataformats::MCEventHeader& header); void generateEvent_TParticles(std::vector& tparts, o2::dataformats::MCEventHeader& header); private: diff --git a/Generators/src/GeneratorHepMC.cxx b/Generators/src/GeneratorHepMC.cxx index 371e0cf1acce1..180a088c02a92 100644 --- a/Generators/src/GeneratorHepMC.cxx +++ b/Generators/src/GeneratorHepMC.cxx @@ -27,6 +27,7 @@ #include #include "FairPrimaryGenerator.h" #include +#include namespace o2 { @@ -420,6 +421,34 @@ void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) auto pdfInfo = mEvent->pdf_info(); auto hiInfo = mEvent->heavy_ion(); + // Workaround for a bug in HepMC3 (3.3.1 on 23/02/2026): GenHeavyIon::from_string() for the "v0" + // format skips reading user_cent_estimate, but to_string() always writes it. + // This shifts all subsequent fields by one, causing a istringstream failure and and heavy_ion() + // to return null even when the attribute is present and well-formed. + // For now we use this manual parser in case the infos are available + if (!hiInfo) { + auto attStr = mEvent->attribute_as_string("GenHeavyIon"); + if (!attStr.empty() && attStr[0] == 'v') { + std::istringstream is(attStr); + std::string version; + is >> version; + if (version == "v0") { + auto hi = std::make_shared(); + double spectNeutrons, spectProtons, eccentricity, userCentEst; + is >> hi->Ncoll_hard >> hi->Npart_proj >> hi->Npart_targ >> hi->Ncoll >> spectNeutrons >> spectProtons // deprecated v0 fields + >> hi->N_Nwounded_collisions >> hi->Nwounded_N_collisions >> hi->Nwounded_Nwounded_collisions >> hi->impact_parameter >> hi->event_plane_angle >> eccentricity // deprecated v0 field + >> hi->sigma_inel_NN >> hi->centrality >> userCentEst // GenHeavyIon::to_string always writes this, but GenHeavyIon::from_string skips it for v0 (HepMC3 bug to fix) + >> hi->Nspec_proj_n >> hi->Nspec_targ_n >> hi->Nspec_proj_p >> hi->Nspec_targ_p; + if (!is.fail()) { + LOG(debug) << "GenHeavyIon: using manual v0 parser (workaround for HepMC3 from_string bug)"; + hiInfo = hi; + } else { + LOG(warn) << "GenHeavyIon: manual v0 parser also failed on: [" << attStr << "]"; + } + } + } + } + // Set default cross-section if (xSection) { eventHeader->putInfo(Key::xSection, xSection->xsec()); @@ -457,8 +486,9 @@ void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) // Set heavy-ion information if (hiInfo) { - eventHeader->putInfo(Key::impactParameter, - hiInfo->impact_parameter); + eventHeader->SetB(hiInfo->impact_parameter); // sets the impact parameter to the FairMCEventHeader field for quick access in the AO2D + eventHeader->putInfo(Key::impactParameter, + hiInfo->impact_parameter); eventHeader->putInfo(Key::nPart, hiInfo->Npart_proj + hiInfo->Npart_targ); eventHeader->putInfo(Key::nPartProjectile, hiInfo->Npart_proj); @@ -471,11 +501,9 @@ void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) 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::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); diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index 2a13f9876e717..f853b772e3cd3 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -420,6 +420,7 @@ bool GeneratorHybrid::importParticles() mMCEventHeader.clearInfo(); if (mCocktailMode) { // in cocktail mode we need to merge the particles from the different generators + bool baseGen = true; // first generator of the cocktail is used as reference to update the event header information for (auto subIndex : subGenIndex) { LOG(info) << "Importing particles for task " << subIndex; auto subParticles = gens[subIndex]->getParticles(); @@ -441,8 +442,10 @@ bool GeneratorHybrid::importParticles() } mParticles.insert(mParticles.end(), subParticles.begin(), subParticles.end()); - // fetch the event Header information from the underlying generator - gens[subIndex]->updateHeader(&mMCEventHeader); + if (baseGen) { + gens[subIndex]->updateHeader(&mMCEventHeader); + baseGen = false; + } mInputTaskQueue.push(subIndex); mTasksStarted++; } @@ -481,7 +484,9 @@ bool GeneratorHybrid::importParticles() void GeneratorHybrid::updateHeader(o2::dataformats::MCEventHeader* eventHeader) { if (eventHeader) { - // we forward the original header information if any + // Forward the base class fields from FairMCEventHeader + static_cast(*eventHeader) = static_cast(mMCEventHeader); + // Copy the key-value store info eventHeader->copyInfoFrom(mMCEventHeader); // put additional information about diff --git a/Generators/src/GeneratorService.cxx b/Generators/src/GeneratorService.cxx index 902b482dc839b..ae0de385a1b23 100644 --- a/Generators/src/GeneratorService.cxx +++ b/Generators/src/GeneratorService.cxx @@ -61,6 +61,18 @@ void GeneratorService::generateEvent_MCTracks(std::vector& tracks, o2:: } } +void GeneratorService::generateEvent_MCTracks(o2::pmr::vector& tracks, o2::dataformats::MCEventHeader& header) +{ + mPrimGen.SetEvent(&header); + mStack.Reset(); + mPrimGen.GenerateEvent(&mStack); // this is the usual FairROOT interface going via stack + + tracks.reserve(mStack.getPrimaries().size()); + for (auto& tparticle : mStack.getPrimaries()) { + tracks.emplace_back(tparticle); + } +} + std::pair, o2::dataformats::MCEventHeader> GeneratorService::generateEvent() { std::vector tracks; diff --git a/Steer/include/Steer/O2MCApplicationBase.h b/Steer/include/Steer/O2MCApplicationBase.h index 36966be9bde62..d61199baba0ae 100644 --- a/Steer/include/Steer/O2MCApplicationBase.h +++ b/Steer/include/Steer/O2MCApplicationBase.h @@ -58,6 +58,8 @@ class O2MCApplicationBase : public FairMCApplication typedef std::function TrackRefFcn; + void fixTGeoRuntimeShapes(); + protected: o2::conf::SimCutParams const& mCutParams; // reference to parameter system unsigned long long mStepCounter{0}; diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 584598d350581..1e3f925042d01 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -37,6 +37,12 @@ #include "SimConfig/GlobalProcessCutSimParam.h" #include "DetectorsBase/GeometryManagerParam.h" #include +#include +#include +#include +#include +#include +#include "SimConfig/G4Params.h" namespace o2 { @@ -209,10 +215,70 @@ bool O2MCApplicationBase::MisalignGeometry() gGeoManager->SetUseParallelWorldNav(true); } + // performs possible optimizations (shape replacements on the runtime geometry) + fixTGeoRuntimeShapes(); + // return original return value of misalignment procedure return true; } +void O2MCApplicationBase::fixTGeoRuntimeShapes() +{ + auto& g4Params = o2::conf::G4Params::Instance(); + if (g4Params.navmode != o2::conf::EG4Nav::kTGeo) { + return; + } + + // Replace TGeo shapes by other ones for performance or other reasons. + // Should only affect runtime of simulation. + + // TODO: make this configurable via external JSON rules/macro + + // Also delete original shapes for memory reasons + + // We follow a visitor pattern on a geom hierarchy + // for now replace a TGeoTessellate by our own implementation + std::unordered_set volumes_visited; + std::unordered_set old_shape_pointers; + + std::function visit; + visit = [&](TGeoNode* node) -> void { + if (!node) { + return; + } + auto vol = node->GetVolume(); + if (volumes_visited.find(vol) != volumes_visited.end()) { + return; + } + volumes_visited.insert(vol); + + // transform the shape of this volume + auto shape = vol->GetShape(); + if (shape->IsA() == TGeoTessellated::Class()) { + auto tsl = static_cast(shape); + + // make a new O2Tessellated until ROOT has proper support for navigation in TGeoTessellated + std::cout << "Converting to O2Tessellated for vol " << vol->GetName() << "\n"; + auto replacement_shape = new o2::base::O2Tessellated(*tsl, false); + vol->SetShape(replacement_shape); + old_shape_pointers.insert(shape); + } + // other cases could come here + + for (int i = 0; i < vol->GetNdaughters(); ++i) { + auto child_node = vol->GetNode(i); + visit(child_node); + } + }; + + visit(gGeoManager->GetTopNode()); + + for (auto ptr : old_shape_pointers) { + delete ptr; + ptr = nullptr; + } +} + void O2MCApplicationBase::finishEventCommon() { LOG(info) << "This event/chunk did " << mStepCounter << " steps"; diff --git a/macro/build_geometry.C b/macro/build_geometry.C index 6b13f2eac2766..ccc3b13fe728d 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -63,6 +63,8 @@ #include #endif +#include + using Return = o2::base::Detector*; void finalize_geometry(FairRunSim* run); @@ -182,6 +184,18 @@ void build_geometry(FairRunSim* run = nullptr) } #endif + if (isActivated("EXT")) { + // EXAMPLE!! how to pick geometry generated from external (CAD) module via `O2_CADtoTGeo.py` + o2::passive::ExternalModuleOptions options; + options.root_macro_file = "PATH_TO_EXTERNAL_GEOM_MODULE/geom.C"; + options.anchor_volume = "barrel"; // hook this into barrel + auto rot = new TGeoCombiTrans(); + rot->RotateX(90); + rot->SetDy(30); // we need to compensate for a shift of barrel with respect to zero + options.placement = rot; + run->AddModule(new o2::passive::ExternalModule("FOO", "BAR", options)); + } + // the absorber if (isActivated("ABSO")) { // the frame structure to support other detectors diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index f559fcdf91cf5..a8f01a3ef1822 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -572,6 +572,9 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then fi has_detector_gpu ITS && GPU_INPUT+=",its-clusters" +if [[ $BEAMTYPE != "cosmic" && $SYNCMODE != 1 ]]; then + has_detector_gpu ITS && GPU_INPUT+=",its-mean-vertex" +fi has_detector_gpu ITS && GPU_OUTPUT+=",its-tracks" # --------------------------------------------------------------------------------------------------------------------- diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index e2ecca590140f..07ccdf01d4566 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -40,6 +40,7 @@ export LC_ALL=C BEAMTYPE=${BEAMTYPE:-PbPb} NEvents=${NEvents:-10} #550 for full TF (the number of PbPb events) NEventsQED=${NEventsQED:-1000} #35000 for full TF +OrbitsBeforeTf=${OrbitsBeforeTf:-1} NCPUS=$(getNumberOfPhysicalCPUCores) echo "Found ${NCPUS} physical CPU cores" NJOBS=${NJOBS:-"${NCPUS}"} @@ -159,7 +160,7 @@ taskwrapper collcontext.log o2-steer-colcontexttool \ --extract-per-timeframe tf:o2sim \ --with-vertices kCCDB \ --maxCollsPerTF ${NEvents} \ - --orbitsEarly 1 \ + --orbitsEarly ${OrbitsBeforeTf} \ --bcPatternFile ccdb \ ${QEDSPEC} @@ -167,6 +168,13 @@ taskwrapper collcontext.log o2-steer-colcontexttool \ SIMOPTKEY+="GenTPCLoopers.colsys=${BEAMTYPE};" taskwrapper sim.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} --vertexMode kCollContext --seed $O2SIMSEED -n $NEvents --configKeyValues "\"$SIMOPTKEY\"" -g ${FST_GENERATOR} -e ${FST_MC_ENGINE} -j $NJOBS --run ${RUNNUMBER} -o o2sim --fromCollContext collisioncontext.root:o2sim +# Test MCTracks to AO2D conversion tool +taskwrapper kine2aod.log "o2-sim-kine-publisher -b --kineFileName o2sim --aggregate-timeframe $NEvents | o2-sim-mctracks-to-aod -b --aod-writer-keep dangling | o2-analysis-mctracks-to-aod-simple-task -b" +if [[ ! -s AnalysisResults_trees.root ]] || [[ ! -s AnalysisResults.root ]]; then + echo "Error: AnalysisResults_trees.root (AO2D from Kine file) or AnalysisResults.root (simple analysis task output) missing or empty" + exit 1 +fi + if [[ $DO_EMBEDDING == 1 ]]; then taskwrapper embed.log o2-sim ${FST_BFIELD+--field=}${FST_BFIELD} -j $NJOBS --run ${RUNNUMBER} -n $NEvents -g pythia8pp -e ${FST_MC_ENGINE} -o sig --configKeyValues ${FST_EMBEDDING_CONFIG} --embedIntoFile o2sim_MCHeader.root fi diff --git a/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh b/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh index f08de81b92d8c..7671d03b97b8f 100755 --- a/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh +++ b/run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh @@ -18,7 +18,7 @@ set -x # PART b) ... apply vertex smearing on top of HepMC events and perform simple analysis NEV=$(grep EVENT slight.out | wc -l) -o2-sim-dpl-eventgen -b --nevents ${NEV} --generator hepmc --confKeyValues \ +o2-sim-dpl-eventgen -b --nEvents ${NEV} --generator hepmc --configKeyValues \ "GeneratorFileOrCmd.fileNames=starlight.hepmc;Diamond.position[2]=0.1;Diamond.width[2]=0.05" |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b diff --git a/run/SimExamples/McTracksToAOD/run_O2Kine.sh b/run/SimExamples/McTracksToAOD/run_O2Kine.sh index 7506f00834fcf..9afac20cd1a0b 100755 --- a/run/SimExamples/McTracksToAOD/run_O2Kine.sh +++ b/run/SimExamples/McTracksToAOD/run_O2Kine.sh @@ -10,6 +10,7 @@ NEVENTS=1000 # launch generator process (for 10000 min bias Pythia8 events; no Geant; no geometry) # o2-sim -j 1 -g pythia8pp -n ${NEVENTS} --noGeant --vertexMode kNoVertex &> sim.log +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) # Option 1) -- use o2-mckine-publisher [ -f AnalysisResults.root ] && rm AnalysisResults.root o2-sim-kine-publisher -b --kineFileName o2sim --aggregate-timeframe 10 |\ @@ -19,8 +20,8 @@ mv AnalysisResults.root AnalysisResult_1.root # Option 2) -- use o2-sim-dpl-eventgen + extkinO2 generator (this should be equivalent to Option 1) [ -f AnalysisResults.root ] && rm AnalysisResults.root -o2-sim-dpl-eventgen -b --nevents ${NEVENTS} --aggregate-timeframe 10 --generator extkinO2 \ - --confKeyValues "GeneratorFromO2Kine.fileName=o2sim_Kine.root" --vertexMode kNoVertex |\ +o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator extkinO2 \ + --configKeyValues "GeneratorFromO2Kine.fileName=o2sim_Kine.root" --vertexMode kNoVertex |\ o2-sim-mctracks-to-aod -b |\ o2-analysis-mctracks-to-aod-simple-task -b &> log2 mv AnalysisResults.root AnalysisResult_2.root diff --git a/run/SimExamples/McTracksToAOD/run_Pythia8.sh b/run/SimExamples/McTracksToAOD/run_Pythia8.sh index 8bac774c5892b..93d2024b05d37 100755 --- a/run/SimExamples/McTracksToAOD/run_Pythia8.sh +++ b/run/SimExamples/McTracksToAOD/run_Pythia8.sh @@ -8,7 +8,7 @@ NEVENTS=1000 # --aggregate-timeframe 10 is used to combine 10 generated events into a timeframe that is then converted to AOD tables # note that if you need special configuration for the analysis tasks, it needs to be passed to proxy and converter as well - +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --vertexMode kNoVertex |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b &> pythia8.log diff --git a/run/SimExamples/McTracksToAOD/run_trigger.sh b/run/SimExamples/McTracksToAOD/run_trigger.sh index ca720191cbad2..5b278c0b666e5 100755 --- a/run/SimExamples/McTracksToAOD/run_trigger.sh +++ b/run/SimExamples/McTracksToAOD/run_trigger.sh @@ -5,9 +5,9 @@ set -x NEVENTS=1000 - +## Add --aod-writer-keep dangling to o2-sim-mctracks-to-aod to write the AO2D file to disc (as AnalysisResults_trees.root) CONFKEY="TriggerExternal.fileName=trigger.macro;TriggerExternal.funcName=trigger()" -o2-sim-dpl-eventgen -b --nevents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --trigger external \ - --vertexMode kDiamondParam --confKeyValues "${CONFKEY}" |\ +o2-sim-dpl-eventgen -b --nEvents ${NEVENTS} --aggregate-timeframe 10 --generator pythia8pp --trigger external \ + --vertexMode kDiamondParam --configKeyValues "${CONFKEY}" |\ o2-sim-mctracks-to-aod -b | o2-analysis-mctracks-to-aod-simple-task -b diff --git a/run/dpl_eventgen.cxx b/run/dpl_eventgen.cxx index 6202e965f3e8a..3df16ee3e5ebb 100644 --- a/run/dpl_eventgen.cxx +++ b/run/dpl_eventgen.cxx @@ -50,6 +50,9 @@ struct GeneratorTask { std::unique_ptr genservice; TStopwatch timer; + std::vector*> mctracks_vector; + std::vector mcheader_vector; + void init(o2::framework::InitContext& /*ic*/) { genservice.reset(new o2::eventgen::GeneratorService); @@ -85,25 +88,31 @@ struct GeneratorTask { outfile.reset(new TFile(kineoutfilename.c_str(), "RECREATE")); outtree.reset(new TTree("o2sim", "o2sim")); } + + mctracks_vector.reserve(aggregate); + mcheader_vector.reserve(aggregate); } void run(o2::framework::ProcessingContext& pc) { - std::vector mctracks; - o2::dataformats::MCEventHeader mcheader; - auto mctrack_ptr = &mctracks; - if (outfile.get()) { - auto br = o2::base::getOrMakeBranch(*outtree, "MCTrack", &mctrack_ptr); - br->SetAddress(&mctrack_ptr); - } + mctracks_vector.clear(); + mcheader_vector.clear(); - for (auto i = 0; i < std::min((GenCount)aggregate, nEvents - eventCounter); ++i) { - mctracks.clear(); - genservice->generateEvent_MCTracks(mctracks, mcheader); - pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcheader); - pc.outputs().snapshot(Output{"MC", "MCTRACKS", 0}, mctracks); + auto batch = std::min((GenCount)aggregate, nEvents - eventCounter); + for (auto i = 0U; i < batch; ++i) { + mctracks_vector.push_back(&pc.outputs().make>(Output{"MC", "MCTRACKS", 0})); + auto& mctracks = mctracks_vector.back(); + mcheader_vector.push_back(&pc.outputs().make(Output{"MC", "MCHEADER", 0})); + auto& mcheader = mcheader_vector.back(); + genservice->generateEvent_MCTracks(*mctracks, *mcheader); ++eventCounter; + auto mctrack_ptr = mctracks; + if (outfile.get()) { + auto br = o2::base::getOrMakeBranch(*outtree, "MCTrack", &mctrack_ptr); + br->SetAddress(&mctrack_ptr); + } + if (outfile.get() && outtree.get()) { outtree->Fill(); } @@ -112,6 +121,7 @@ struct GeneratorTask { // report number of TFs injected for the rate limiter to work ++tfCounter; pc.services().get().send(o2::monitoring::Metric{(uint64_t)tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + bool time_expired = false; if (ttl > 0) { timer.Stop(); @@ -125,7 +135,7 @@ struct GeneratorTask { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); - // write out data to disc if asked + // write out data to disk if asked if (outfile.get()) { outtree->SetEntries(eventCounter); outtree->Write(); diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index f72dd6eebaaf0..cfbea6ae02a5f 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -13,7 +13,6 @@ #include "Framework/AnalysisTask.h" #include "Monitoring/Monitoring.h" #include "Framework/CommonDataProcessors.h" -#include "SimulationDataFormat/MCTrack.h" #include "Steer/MCKinematicsReader.h" #include "Framework/runDataProcessing.h" @@ -64,6 +63,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) spec.outputs.emplace_back("MC", "MCHEADER", 0, Lifetime::Timeframe); spec.outputs.emplace_back("MC", "MCTRACKS", 0, Lifetime::Timeframe); spec.requiredServices.push_back(o2::framework::ArrowSupport::arrowBackendSpec()); - spec.algorithm = CommonDataProcessors::wrapWithRateLimiting(spec.algorithm); + spec.algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(spec.algorithm); return {spec}; } diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index f7a85e62a3f9b..124e8aa7b3e42 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -94,9 +94,8 @@ struct MctracksToAod { // TODO: include BC simulation auto bcCounter = 0UL; size_t offset = 0; + LOG(debug) << "--- Loop over " << nParts << " parts ---"; for (auto i = 0U; i < nParts; ++i) { - LOG(debug) << "--- Loop over " << nParts << " parts ---"; - auto record = mSampler.generateCollisionTime(); auto header = pc.inputs().get("mcheader", i); auto tracks = pc.inputs().get("mctracks", i); diff --git a/scripts/geometry/O2_CADtoTGeo.py b/scripts/geometry/O2_CADtoTGeo.py new file mode 100644 index 0000000000000..d564cdc6124a8 --- /dev/null +++ b/scripts/geometry/O2_CADtoTGeo.py @@ -0,0 +1,602 @@ +#!/usr/bin/env python3 +""" +A Python script, doing a deep STEP/XCAF -> ROOT TGeo conversion. +For now, all CAD solids are simply meshed. The ROOT geometry is build as a C++ ROOT macro +and facet data is stored in binary form to keep disc space minimal. + +Generates (into --output-folder): + - geom.C (small ROOT macro) + - facets__.bin for each leaf logical volume (float32 triangles) + +Facet file format (little-endian): + uint32 nTriangles + then nTriangles * 9 * float32: + ax ay az bx by bz cx cy cz + +VOLNAME is a filename-safe version of the XCAF label name when available (e.g. "nut"), +and LID is the XCAF label entry (e.g. "0:1:1:7" -> "0_1_1_7") to keep filenames unique. + +Naming: + - C++ variable names stay based on XCAF label entry (e.g. 0:1:1:7) for uniqueness. + - ROOT object names (TGeoVolume / TGeoTessellated / TGeoVolumeAssembly) use the label's + human name when available (e.g. "nut", "rod-assembly"), falling back to the entry. + +Units: + - By default, the script tries to detect the STEP LENGTH unit by scanning the STEP file + header/contents (common patterns like .MILLI. / .CENTI. / .METRE. / INCH / FOOT). + - You can override with --step-unit {auto,mm,cm,m,in,ft}. TGeo expects cm. + +Author: + - Sandro Wenzel, CERN (02/2026) +""" + +import warnings +warnings.filterwarnings("ignore", message=".*all to deprecated function.*", category=DeprecationWarning) + +import argparse +import re +import struct +from pathlib import Path as _Path + +from OCC.Core.Bnd import Bnd_Box +from OCC.Core.BRepBndLib import brepbndlib +from OCC.Core.BRepMesh import BRepMesh_IncrementalMesh +from OCC.Core.BRep import BRep_Tool +from OCC.Core.TopLoc import TopLoc_Location +from OCC.Core.TopAbs import TopAbs_REVERSED +from OCC.Extend.TopologyUtils import TopologyExplorer + +from OCC.Core.STEPCAFControl import STEPCAFControl_Reader +from OCC.Core.TDocStd import TDocStd_Document +from OCC.Core.XCAFDoc import XCAFDoc_DocumentTool +from OCC.Core.IFSelect import IFSelect_RetDone + +from OCC.Core.TDF import TDF_Label, TDF_LabelSequence, TDF_Tool +from OCC.Core.TCollection import TCollection_AsciiString +from OCC.Core.gp import gp_Trsf + + +# ------------------------------- +# STEP/XCAF loading +# ------------------------------- + +def load_step_with_xcaf(path: str): + doc = TDocStd_Document("pythonocc-doc") + reader = STEPCAFControl_Reader() + reader.SetColorMode(True) + reader.SetNameMode(True) + reader.SetLayerMode(True) + + status = reader.ReadFile(path) + if status != IFSelect_RetDone: + raise RuntimeError(f"STEP read failed for: {path}") + + reader.Transfer(doc) + shape_tool = XCAFDoc_DocumentTool.ShapeTool(doc.Main()) + return doc, shape_tool + + +def label_id(label: TDF_Label) -> str: + s = TCollection_AsciiString() + TDF_Tool.Entry(label, s) + return s.ToCString() + + +def label_name(label: TDF_Label) -> str: + # Uses the XCAF/STEP name when present; can be empty. + try: + n = label.GetLabelName() + if n: + return str(n) + except Exception: + pass + return "" + + +# ------------------------------- +# Units +# ------------------------------- + +def step_unit_scale_to_cm(step_unit: str) -> float: + step_unit = (step_unit or "auto").lower() + if step_unit == "mm": + return 0.1 + if step_unit == "cm": + return 1.0 + if step_unit == "m": + return 100.0 + if step_unit == "in": + return 2.54 + if step_unit == "ft": + return 30.48 + raise ValueError(f"Unknown --step-unit {step_unit} (use auto, mm, cm, m, in, ft)") + + +def detect_step_length_unit(step_path: str) -> str: + """ + Heuristic unit detection by scanning STEP file text for common unit tokens. + This avoids relying on OCCT APIs that can vary across pythonOCC builds. + + Returns one of: mm, cm, m, in, ft. Defaults to mm if uncertain. + """ + p = _Path(step_path) + # STEP can be huge: read only the first few MB; units are near the header. + max_bytes = 4 * 1024 * 1024 + data = p.open("rb").read(max_bytes).decode("latin-1", errors="ignore").upper() + + if ".MILLI." in data: + return "mm" + if ".CENTI." in data: + return "cm" + if ".METRE." in data or ".METER." in data: + return "m" + if "INCH" in data: + return "in" + if "FOOT" in data or "FEET" in data: + return "ft" + + # Conservative default for mechanical CAD STEP is mm + return "mm" + + +# ------------------------------- +# Triangulation helpers +# ------------------------------- + +def _scale_triangles(triangles, s: float): + if s == 1.0: + return triangles + out = [] + for (a, b, c) in triangles: + out.append(( + (a[0] * s, a[1] * s, a[2] * s), + (b[0] * s, b[1] * s, b[2] * s), + (c[0] * s, c[1] * s, c[2] * s), + )) + return out + + +def triangulate_asbbox(shape, scale_to_cm: float = 1.0): + box = Bnd_Box() + brepbndlib.Add(shape, box) + xmin, ymin, zmin, xmax, ymax, zmax = box.Get() + + p000 = (xmin, ymin, zmin) + p001 = (xmin, ymin, zmax) + p010 = (xmin, ymax, zmin) + p011 = (xmin, ymax, zmax) + p100 = (xmax, ymin, zmin) + p101 = (xmax, ymin, zmax) + p110 = (xmax, ymax, zmin) + p111 = (xmax, ymax, zmax) + + triangles = [ + (p000, p100, p110), (p000, p110, p010), + (p001, p111, p101), (p001, p011, p111), + (p000, p101, p100), (p000, p001, p101), + (p010, p110, p111), (p010, p111, p011), + (p000, p010, p011), (p000, p011, p001), + (p100, p101, p111), (p100, p111, p110), + ] + return _scale_triangles(triangles, scale_to_cm) + + +def triangulate_CAD_solid(my_solid, meshparam, scale_to_cm: float = 1.0): + lin_defl = float(meshparam.get("lin_defl", 0.1)) + ang_defl = float(meshparam.get("ang_defl", 0.1)) + + parallel = True + try: + BRepMesh_IncrementalMesh(my_solid, lin_defl, False, ang_defl, bool(parallel)) + except TypeError: + BRepMesh_IncrementalMesh(my_solid, lin_defl, False, ang_defl) + + triangles = [] + for face in TopologyExplorer(my_solid).faces(): + loc = TopLoc_Location() + triangulation = BRep_Tool.Triangulation(face, loc) + if triangulation is None: + continue + + trsf = loc.Transformation() + reverse = (face.Orientation() == TopAbs_REVERSED) + + for i in range(1, triangulation.NbTriangles() + 1): + tri = triangulation.Triangle(i) + n1, n2, n3 = tri.Get() + + p1 = triangulation.Node(n1).Transformed(trsf) + p2 = triangulation.Node(n2).Transformed(trsf) + p3 = triangulation.Node(n3).Transformed(trsf) + + if reverse: + p2, p3 = p3, p2 + + triangles.append(( + (p1.X(), p1.Y(), p1.Z()), + (p2.X(), p2.Y(), p2.Z()), + (p3.X(), p3.Y(), p3.Z()), + )) + + return _scale_triangles(triangles, scale_to_cm) + + +# ------------------------------- +# Naming helpers +# ------------------------------- + +def sanitize_cpp_name(s: str) -> str: + safe = re.sub(r"[^0-9a-zA-Z]", "_", s) + if not safe: + safe = "x" + if not (safe[0].isalpha() or safe[0] == "_"): + safe = "_" + safe + return safe + + +def sanitize_filename(s: str) -> str: + safe = re.sub(r"[^0-9a-zA-Z]", "_", s) + return safe or "x" + + +# ------------------------------- +# Binary facet IO +# ------------------------------- + +def write_facets_bin(path: _Path, triangles): + path.parent.mkdir(parents=True, exist_ok=True) + with open(path, "wb") as f: + f.write(struct.pack(" str: + m = trsf.GetRotation().GetMatrix() + t = trsf.TranslationPart() + return f""" + Double_t {name}_m[9] = {{ + {m.Value(1,1)}, {m.Value(1,2)}, {m.Value(1,3)}, + {m.Value(2,1)}, {m.Value(2,2)}, {m.Value(2,3)}, + {m.Value(3,1)}, {m.Value(3,2)}, {m.Value(3,3)} + }}; + TGeoRotation *{name}_rot = new TGeoRotation(); + {name}_rot->SetMatrix({name}_m); + TGeoCombiTrans *{name} = new TGeoCombiTrans({t.X()*scale_to_cm}, {t.Y()*scale_to_cm}, {t.Z()*scale_to_cm}, {name}_rot); +""" + + +def emit_cpp_prelude() -> str: + return """#include +#include +#include +#include +#include +#include + +static void LoadFacets(const std::string& file, TGeoTessellated* solid, bool check=false) +{ + std::ifstream in(file, std::ios::binary); + if (!in) throw std::runtime_error("Cannot open facet file: " + file); + + uint32_t nTri = 0; + in.read(reinterpret_cast(&nTri), sizeof(nTri)); + if (!in) throw std::runtime_error("Bad facet header in: " + file); + + for (uint32_t i=0;i(v), sizeof(v)); + if (!in) throw std::runtime_error("Unexpected EOF in: " + file); + + solid->AddFacet(TGeoTessellated::Vertex_t(v[0],v[1],v[2]), + TGeoTessellated::Vertex_t(v[3],v[4],v[5]), + TGeoTessellated::Vertex_t(v[6],v[7],v[8])); + } + solid->CloseShape(check, true); +} +""" + + +def emit_materials_cpp() -> str: + return """ // Default material/medium (placeholder; can be replaced later) + TGeoMaterial *mat_Default = new TGeoMaterial("Default", 0., 0., 0.); + TGeoMedium *med_Default = new TGeoMedium("Default", 1, mat_Default); +""" + + +def emit_tessellated_cpp(lid: str, vol_display_name: str, facet_abspath: str, ntriangles: int) -> str: + safe = sanitize_cpp_name(lid) + shape_name = vol_display_name if vol_display_name else lid + + if ntriangles <= 0: + out = [] + out.append(f' TGeoBBox *solid_{safe} = new TGeoBBox("{shape_name}", 0.001, 0.001, 0.001);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + return "\n".join(out) + + out = [] + out.append(f' TGeoTessellated *solid_{safe} = new TGeoTessellated("{shape_name}", {ntriangles});') + out.append(f' LoadFacets("{facet_abspath}", solid_{safe}, check);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, med_Default);') + return "\n".join(out) + + +def emit_assembly_cpp(lid: str, asm_display_name: str) -> str: + safe = sanitize_cpp_name(lid) + name = asm_display_name if asm_display_name else lid + return f' TGeoVolumeAssembly *asm_{safe} = new TGeoVolumeAssembly("{name}");' + + +# ------------------------------- +# Definition graph extraction +# ------------------------------- + +logical_volumes = {} # def_lid -> triangles +def_names = {} # def_lid -> human display name (may be "") +assemblies = set() # def_lid +placements = [] # (parent_def_lid, child_def_lid, gp_Trsf local) +top_defs = set() # top definition lids +visited_defs = set() # expanded defs + + +def cpp_var_for_def(lid: str) -> str: + safe = sanitize_cpp_name(lid) + return f"asm_{safe}" if lid in assemblies else f"vol_{safe}" + + +def expand_definition(def_label: TDF_Label, shape_tool, meshparam=None, scale_to_cm: float = 1.0): + def_lid = label_id(def_label) + if def_lid in visited_defs: + return + visited_defs.add(def_lid) + + nm = label_name(def_label) + if nm and def_lid not in def_names: + def_names[def_lid] = nm + elif def_lid not in def_names: + def_names[def_lid] = "" + + children = TDF_LabelSequence() + shape_tool.GetComponents(def_label, children) + has_children = children.Length() > 0 + + if has_children or shape_tool.IsAssembly(def_label): + assemblies.add(def_lid) + + for i in range(children.Length()): + child = children.Value(i + 1) + if shape_tool.IsReference(child): + referred = TDF_Label() + shape_tool.GetReferredShape(child, referred) + child_def_lid = label_id(referred) + + loc = shape_tool.GetLocation(child) + trsf = loc.Transformation() + placements.append((def_lid, child_def_lid, trsf)) + + expand_definition(referred, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + else: + child_def_lid = label_id(child) + placements.append((def_lid, child_def_lid, gp_Trsf())) + expand_definition(child, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + return + + if shape_tool.IsSimpleShape(def_label): + if def_lid not in logical_volumes: + shape = shape_tool.GetShape(def_label) + do_meshing = (meshparam is not None) and meshparam.get("do_meshing", None) is True + logical_volumes[def_lid] = triangulate_CAD_solid(shape, meshparam=meshparam, scale_to_cm=scale_to_cm) if do_meshing else triangulate_asbbox(shape, scale_to_cm=scale_to_cm) + return + + assemblies.add(def_lid) + + +def extract_graph(step_path: str, meshparam=None, scale_to_cm: float = 1.0): + global logical_volumes, def_names, assemblies, placements, top_defs, visited_defs + logical_volumes = {} + def_names = {} + assemblies = set() + placements = [] + top_defs = set() + visited_defs = set() + + doc, shape_tool = load_step_with_xcaf(step_path) + + roots = TDF_LabelSequence() + shape_tool.GetFreeShapes(roots) + + for i in range(roots.Length()): + root = roots.Value(i + 1) + if shape_tool.IsReference(root): + ref = TDF_Label() + shape_tool.GetReferredShape(root, ref) + top_defs.add(label_id(ref)) + expand_definition(ref, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + else: + top_defs.add(label_id(root)) + expand_definition(root, shape_tool, meshparam=meshparam, scale_to_cm=scale_to_cm) + + return doc, shape_tool + + +# ------------------------------- +# ROOT macro emission +# ------------------------------- + +def emit_placement_cpp(parent_def: str, child_def: str, trsf: gp_Trsf, copy_no: int, scale_to_cm: float) -> str: + parent_cpp = cpp_var_for_def(parent_def) + child_cpp = cpp_var_for_def(child_def) + tr_name = f"tr_{sanitize_cpp_name(parent_def)}_{sanitize_cpp_name(child_def)}_{copy_no}" + return trsf_to_tgeo(trsf, tr_name, scale_to_cm) + f" {parent_cpp}->AddNode({child_cpp}, {copy_no}, {tr_name});\n" + + +def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit: str = "auto"): + if (step_unit or "auto").lower() == "auto": + detected = detect_step_length_unit(step_path) + scale_to_cm = step_unit_scale_to_cm(detected) + print(f"Detected STEP length unit: {detected} (scale to cm = {scale_to_cm})") + else: + scale_to_cm = step_unit_scale_to_cm(step_unit) + print(f"Using overridden STEP length unit: {step_unit} (scale to cm = {scale_to_cm})") + + extract_graph(step_path, meshparam=meshparam, scale_to_cm=scale_to_cm) + + out_folder = out_folder.expanduser().resolve() + out_folder.mkdir(parents=True, exist_ok=True) + + facet_files = {} # def_lid -> absolute path string + for lid, tris in logical_volumes.items(): + disp = def_names.get(lid, "") + volname = sanitize_filename(disp) if disp else "vol" + lidname = sanitize_filename(lid) + fname = f"facets_{volname}_{lidname}.bin" + fpath = (out_folder / fname).resolve() + write_facets_bin(fpath, tris) + facet_files[lid] = str(fpath).replace("\\", "\\\\") # C++ string literal safety + + cpp = [] + cpp.append(emit_cpp_prelude()) + + cpp.append("TGeoVolume* build(bool check=true) {") + cpp.append(' if (!gGeoManager) { throw std::runtime_error("gGeoManager is null. Call build_and_export() or create a TGeoManager first."); }') + cpp.append(emit_materials_cpp()) + + for lid in logical_volumes.keys(): + ntriangles = len(logical_volumes[lid]) + cpp.append(emit_tessellated_cpp(lid, def_names.get(lid, ""), facet_files[lid], ntriangles)) + + for lid in sorted(assemblies): + cpp.append(emit_assembly_cpp(lid, def_names.get(lid, ""))) + + for idx, (parent, child, trsf) in enumerate(placements, start=1): + cpp.append(emit_placement_cpp(parent, child, trsf, idx, scale_to_cm)) + + if len(top_defs) == 1: + top = next(iter(top_defs)) + cpp.append(f" return {cpp_var_for_def(top)};") + else: + cpp.append(' TGeoVolumeAssembly *asm_WORLD = new TGeoVolumeAssembly("WORLD");') + for i, node in enumerate(sorted(top_defs), start=1): + cpp.append(f" asm_WORLD->AddNode({cpp_var_for_def(node)}, {i});") + cpp.append(" return asm_WORLD;") + + cpp.append("}") + + # exports a function allowing to export the geometry to TGeo file + cpp.append('void build_and_export(const char* out_root = "geom.root", bool check=true) {') + cpp.append(' if (!gGeoManager) { new TGeoManager("geom","geom"); }') + cpp.append(' TGeoVolume* top = build(check);') + cpp.append(' gGeoManager->SetTopVolume(top);') + cpp.append(' gGeoManager->CloseGeometry();') + cpp.append(' gGeoManager->CheckOverlaps();') + cpp.append(' gGeoManager->Export(out_root);') + cpp.append('}') + + # exports a function to get get hold of the builder function in ALICE O2 + cpp.append('std::function get_builder_hook_checked() {') + cpp.append(' return []() { return build(true); };') + cpp.append('}') + # exports a function to get get hold of the builder function in ALICE O2 + cpp.append('std::function get_builder_hook_unchecked() {') + cpp.append(' return []() { return build(false); };') + cpp.append('}') + + return "\n".join(cpp) + + +# ------------------------------- +# Geometry Tree printing (debug) +# ------------------------------- + +def label_entry(label): + s = TCollection_AsciiString() + TDF_Tool.Entry(label, s) + return s.ToCString() + + +def traverse_print(label, shape_tool, depth=0): + indent = " " * depth + name = label.GetLabelName() + entry = label_entry(label) + print(f"{indent}- {name} =>[{entry}]") + + if shape_tool.IsReference(label): + ref_label = TDF_Label() + shape_tool.GetReferredShape(label, ref_label) + traverse_print(ref_label, shape_tool, depth + 1) + return + + children = TDF_LabelSequence() + shape_tool.GetComponents(label, children) + if children.Length() > 0 or shape_tool.IsAssembly(label): + for i in range(children.Length()): + traverse_print(children.Value(i + 1), shape_tool, depth + 1) + return + + if shape_tool.IsSimpleShape(label): + shape = shape_tool.GetShape(label) + print(f"{indent} [LogicalShape id={id(shape)}]") + + +def print_geom(step_file): + print(f"Printing GEOM hierarchy for {step_file}") + doc, shape_tool = load_step_with_xcaf(step_file) + roots = TDF_LabelSequence() + shape_tool.GetFreeShapes(roots) + for i in range(roots.Length()): + traverse_print(roots.Value(i + 1), shape_tool) + + +# ------------------------------- +# CLI +# ------------------------------- + +def main(): + ap = argparse.ArgumentParser(description="Convert STEP/XCAF to ROOT TGeo macro, facets in per-volume binary files.") + ap.add_argument("step", help="Input STEP file") + ap.add_argument("-o", "--out", default="geom.C", help="Output ROOT macro file name (default: geom.C)") + ap.add_argument("--output-folder", default="./", help="Output folder for macro + facet files") + ap.add_argument("--out-path", default=None, help="(deprecated) Alias for --output-folder") + ap.add_argument("--mesh", action="store_true", help="Use full BRepMesh triangulation instead of bounding boxes") + ap.add_argument("--print-tree", action="store_true", help="Just prints the geometry tree") + ap.add_argument("--mesh-prec", default=0.1, help="meshing precision. lower --> slower") + ap.add_argument("--step-unit", default="auto", choices=["auto", "mm", "cm", "m", "in", "ft"], help="STEP length unit override (default: auto-detect)") + + args = ap.parse_args() + + step_path = str(_Path(args.step).expanduser().resolve()) + if args.print_tree: + print_geom(step_path) + return + + out_folder = _Path(args.output_folder) + if args.out_path is not None: + out_folder = _Path(args.out_path) + + meshparam = {"do_meshing": args.mesh, "lin_defl": args.mesh_prec, "ang_defl": args.mesh_prec} + + out_folder = out_folder.expanduser().resolve() + out_folder.mkdir(parents=True, exist_ok=True) + + out_macro = (out_folder / _Path(args.out).name).resolve() + code = emit_root_macro(step_path, out_folder, meshparam=meshparam, step_unit=args.step_unit) + out_macro.write_text(code) + + print(f"Wrote ROOT macro: {out_macro}") + print(f"Wrote facet files into: {out_folder}") + print("In ROOT you can do:") + print(f" root -l {out_macro}") + print(' build_and_export("geom.root");') + + +if __name__ == "__main__": + main() diff --git a/scripts/geometry/README.md b/scripts/geometry/README.md new file mode 100644 index 0000000000000..4fb2d1ec610d4 --- /dev/null +++ b/scripts/geometry/README.md @@ -0,0 +1,27 @@ +This is the tool O2_CADtoTGeo.py which translates from geometries in STEP format (CAD export) to +TGeo. + +To use the tool, setup a conda environment with python-occ core installed. +The following should work on standard linux x86: + +``` +# -) download miniconda into $HOME/miniconda (if not already done) +if [ ! -d $HOME/miniconda ]; then + curl -fsSL https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-aarch64.sh -o miniconda.sh + bash miniconda.sh -b -p $HOME/miniconda +fi + +# -) source conda into the environment (in every shell you want to use this) +source $HOME/miniconda/etc/profile.d/conda.sh + +# -) Create an OCC environment (for OpenCacade) +conda create -n occ python=3.10 -y +conda activate occ + +# 3) Install OpenCascade Python bindings +conda install -c conda-forge pythonocc-core -y + +# 4) Run the tool, e.g. +conda activate occ +python PATH_TO_ALICEO2_SOURCES/scripts/geometry/O2_CADtoTGeo.py --help +``` \ No newline at end of file diff --git a/scripts/geometry/simulating_CAD_modules.md b/scripts/geometry/simulating_CAD_modules.md new file mode 100644 index 0000000000000..ccd59a3523781 --- /dev/null +++ b/scripts/geometry/simulating_CAD_modules.md @@ -0,0 +1,72 @@ +# ALICE-O2 GEANT Simulation of CAD Geometries + +These are a few notes related to the inclusion of external (CAD-described) detector modules into the O2 simulation framework. + +## Description of the Workflow + +In principle, such integration is now possible and requires the following steps: + +1. The CAD geometry needs to be exported to STEP format and must contain only the final geometry (no artificial eta-cut elements). Ideally, the geometry should be fully hierarchical with proper solid reuse. The solids should retain their proper surface representation for detailed analysis. + +2. A tool `O2-CADtoTGeo.py` is provided to convert the STEP geometry into TGeo format. The tool is part of AliceO2 and is based on Python bindings (OCC) for OpenCascade. The tool can be used as follows: + + ```bash + python O2-CADtoTGeo.py STEP_FILE --output-folder my_detector -o geom.C --mesh \ + --mesh-prec 0.2 + ``` + + This will create a ROOT macro file `geom.C` containing the geometry description in ROOT format, as well as several binary files describing the TGeo solids. The `geom.C` file can either be used directly in ROOT to inspect the geometry or be provided to ALICE-O2 for inclusion in the geometry. + +3. Introduction of materials/media in the file `geom.C`. Currently, the file `geom.C` needs to be patched or edited to properly include `TGeoMaterial`/`TGeoMedium` definitions and connect them to the relevant `TGeoVolume` objects. At present, every solid has the same dummy material attached, which is not realistic. It may be a good idea to create a new file `geom_withMaterials.C`, which differs from `geom.C` by the addition of these material definitions. + +4. Once the conversion is complete, the module can be inserted into the O2 geometry via the `ExternalModule` class. To do so, follow this pattern in `build_geometry.C`: + + ```cpp + if (isActivated("EXT")) { + o2::passive::ExternalModuleOptions options; + options.root_macro_file = "PATH_TO_MY_DETECTOR/my_detector/geom_withMaterials.C"; + options.anchor_volume = "barrel"; // hook this into barrel + auto rot = new TGeoCombiTrans(); + rot->RotateX(90); + rot->SetDy(30); // compensate for a shift of the barrel with respect to zero + options.placement = rot; + run->AddModule(new o2::passive::ExternalModule("A3VTX", "ALICE3 beam pipe", options)); + } + ``` + +5. Create a custom detector geometry list file `my_det.json` in JSON format that includes the external detector (and any other required components, such as the L3 magnet in this example): + + ```json + { + "MY_DET": [ + "EXT", + "MAG" + ] + } + ``` + +6. Run the Geant simulation with: + + ```bash + o2-sim --detectorList MY_DET:my_det.json -g pythia8pp .... + ``` + +## Known Limitations + +- The `O2-CADtoTGeo.py` tool currently converts geometries only into TGeoTessellated solids. This may be suboptimal for primitive shapes or only an approximation for shapes with exact second-order surfaces (e.g., tubes). The precision (and therefore the number of surface triangles) can be controlled with the `--mesh-prec` parameter. The smaller the value, the more precise the mesh. + +- Meshed solids created by the tool may have issues, such as topological errors or non-watertight surfaces. It is planned to include "healing" steps via additional processing with well-known geometry kernels (e.g., CGAL). + +- The tool does not currently export materials or TGeoMedia. These must be inserted or edited manually. It is planned to make this process more automatic and user-friendly. + +- The Python tool requires the OCC Python module, which is currently not part of our software distribution. We have found it most practical to run the tool in a separate conda environment (fully decoupled from the ALICE software stack). + +- The tool currently generates a `geom.C` macro file. In the future, it may be possible to directly create an in-memory TGeo representation for deeper integration. + +- Currently, only passive modules can be integrated. Treatment of sensitive volumes or parts will be addressed in a future step. + +## Software Installation + +- The simulation must be run in the standard O2 environment built with alibuild. + +- The CAD conversion tool must currently be run in a dedicated conda environment, as described in scripts/geometry/README.md in the AliceO2 source code. \ No newline at end of file