diff --git a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt index 04288f205d8f4..a9964670a99c1 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt @@ -12,4 +12,5 @@ add_subdirectory(base) add_subdirectory(simulation) add_subdirectory(DataFormatsIOTOF) +add_subdirectory(workflow) add_subdirectory(macros) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h index 19b5dc3bcd72b..1e526716972d5 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/include/DataFormatsIOTOF/Digit.h @@ -19,6 +19,7 @@ #ifndef ALICEO2_IOTOF_DIGIT_H #define ALICEO2_IOTOF_DIGIT_H +#include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsITSMFT/Digit.h" namespace o2::iotof @@ -26,10 +27,9 @@ namespace o2::iotof class Digit : public o2::itsmft::Digit { public: - Digit() = default; ~Digit() = default; Digit(UShort_t chipindex = 0, UShort_t row = 0, UShort_t col = 0, Int_t charge = 0, double time = 0.) - : o2::itsmft::Digit(chipindex, row, col, charge), mTime(time) {}; + : o2::itsmft::Digit(chipindex, row, col, charge), mTime(time){}; // Setters void setTime(double time) { mTime = time; } @@ -37,10 +37,39 @@ class Digit : public o2::itsmft::Digit // Getters double getTime() const { return mTime; } + static UInt_t getOrderingKey(UShort_t chipindex, UShort_t row, UShort_t col) + { + return (static_cast(chipindex) << 16) | (static_cast(row) << 8) | static_cast(col); + } + private: double mTime = 0.; ///< Measured time (ns) ClassDefNV(Digit, 1); }; +// McLabelRef is used to store the MC label of the hit contributing to a digit, and eventually link to extra contributions to the same pixel +struct McLabelRef { + o2::MCCompLabel mLabel; ///< hit label + int mNext = -1; ///< eventual next contribution to the same pixel + McLabelRef(o2::MCCompLabel label = 0, int next = -1) : mLabel(label), mNext(next) {} + + ClassDefNV(McLabelRef, 1); +}; + +class LabeledDigit : public Digit +{ + public: + LabeledDigit(UShort_t chipindex = 0, UShort_t row = 0, UShort_t col = 0, Int_t charge = 0, double time = 0., + o2::MCCompLabel label = 0) + : Digit(chipindex, row, col, charge, time), mLabel(label) {} + + void setLabel(McLabelRef label) { mLabel = label; } + McLabelRef getLabel() const { return mLabel; } + + private: + McLabelRef mLabel; ///< label of the hit contributing to the digit, and eventually reference to extra contributions to the same pixel + ClassDefNV(LabeledDigit, 1); +}; + } // namespace o2::iotof #endif // ALICEO2_IOTOF_DIGIT_H diff --git a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h index 8a167df4d6c7b..3f629289cf842 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/DataFormatsIOTOF/src/DataFormatsIOTOFLinkDef.h @@ -16,5 +16,12 @@ #pragma link off all functions; #pragma link C++ class o2::iotof::Digit + ; -// #pragma link C++ class std::vector < o2::iotof::Digit> + ; +#pragma link C++ class std::vector < o2::iotof::Digit> + ; + +#pragma link C++ class o2::iotof::McLabelRef + ; +#pragma link C++ class std::vector < o2::iotof::McLabelRef> + ; + +#pragma link C++ class o2::iotof::LabeledDigit + ; +#pragma link C++ class std::vector < o2::iotof::LabeledDigit> + ; + #endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h index b998619684b28..8aaa9c5e31bb8 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h @@ -23,6 +23,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache { public: using DetMatrixCache::getMatrixL2G; + using DetMatrixCache::getMatrixT2L; GeometryTGeo(bool build = false, int loadTrans = 0); void Build(int loadTrans); @@ -33,6 +34,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static const char* getIOTOFVolPattern() { return sIOTOFVolumeName.c_str(); } // Inner TOF + const int getITOFNumberOfChips() { return mNumberOfChipsIOTOF[0]; } static const char* getITOFLayerPattern() { return sITOFLayerName.c_str(); } static const char* getITOFStavePattern() { return sITOFStaveName.c_str(); } static const char* getITOFModulePattern() { return sITOFModuleName.c_str(); } @@ -40,6 +42,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static const char* getITOFSensorPattern() { return sITOFSensorName.c_str(); } // Outer TOF + const int getOTOFNumberOfChips() { return mNumberOfChipsIOTOF[1]; } static const char* getOTOFLayerPattern() { return sOTOFLayerName.c_str(); } static const char* getOTOFStavePattern() { return sOTOFStaveName.c_str(); } static const char* getOTOFModulePattern() { return sOTOFModuleName.c_str(); } @@ -47,11 +50,13 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static const char* getOTOFSensorPattern() { return sOTOFSensorName.c_str(); } // Forward TOF + const int getFTOFNumberOfChips() { return mNumberOfChipsFTOF; } static const char* getFTOFLayerPattern() { return sFTOFLayerName.c_str(); } static const char* getFTOFChipPattern() { return sFTOFChipName.c_str(); } static const char* getFTOFSensorPattern() { return sFTOFSensorName.c_str(); } // Backward TOF + const int getBTOFNumberOfChips() { return mNumberOfChipsBTOF; } static const char* getBTOFLayerPattern() { return sBTOFLayerName.c_str(); } static const char* getBTOFChipPattern() { return sBTOFChipName.c_str(); } static const char* getBTOFSensorPattern() { return sBTOFSensorName.c_str(); } @@ -90,8 +95,31 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache /// for a given chip 'index' by querying the TGeoManager TGeoHMatrix* extractMatrixSensor(int index) const; + // sensor ref X and alpha + void extractSensorXAlpha(int, float&, float&); + + // create matrix for tracking to local frame for IOTOF + TGeoHMatrix& createT2LMatrix(int); + TString getMatrixPath(int index) const; + // cache for tracking frames + void defineSensors(); + bool isTrackingFrameCached() const { return !mCacheRefX.empty(); } + void fillTrackingFramesCache(); + + float getSensorRefAlpha(int chipId) const + { + const int local = chipId; + return mCacheRefAlpha[local]; + } + + float getSensorX(int chipId) const + { + const int local = chipId; + return mCacheRefX[local]; + } + protected: // Determine the number of active parts in the geometry int extractNumberOfStavesIOTOF(int lay) const; @@ -141,6 +169,10 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache // Backward TOF int mNumberOfChipsBTOF; + std::vector sensors; + std::vector mCacheRefX; /// cache for X of IOTOF + std::vector mCacheRefAlpha; /// cache for sensor ref alpha IOTOF + private: static std::unique_ptr sInstance; }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index c4cf5fd8844a8..dc0d793f4410a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -15,6 +15,7 @@ #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" + namespace o2 { namespace iotof @@ -38,6 +39,39 @@ struct ChipSpecifics { float SensorSizeRows() const { return ActiveMatrixSizeRows() + PassiveEdgeTop + PassiveEdgeReadOut; } }; +struct ITOFChipSpecifics : ChipSpecifics { + ITOFChipSpecifics() + { + NCols = 258; + NRows = 271; + PitchCol = 250.00e-4; + PitchRow = 100.00e-4; + SensorLayerThicknessEff = 50.e-4; + SensorLayerThickness = 50.e-4; + } +}; + +struct OTOFChipSpecifics : ChipSpecifics { + OTOFChipSpecifics() + { + NCols = 517; + NRows = 243; + PitchCol = 250.00e-4; + PitchRow = 100.00e-4; + PassiveEdgeSide = 106.48e-4; + SensorLayerThicknessEff = 50.e-4; + SensorLayerThickness = 50.e-4; + } +}; + +struct ITOFChipSpecificParam : public o2::conf::ConfigurableParamPromoter { + O2ParamDef(ITOFChipSpecificParam, "ITOFChipSpecific"); +}; + +struct OTOFChipSpecificParam : public o2::conf::ConfigurableParamPromoter { + O2ParamDef(OTOFChipSpecificParam, "OTOFChipSpecific"); +}; + struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper { bool enableInnerTOF = true; // Enable Inner TOF layer bool enableOuterTOF = true; // Enable Outer TOF layer @@ -49,9 +83,6 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment float sensorThickness = 0.0050f; // thickness of the sensor in cm, for all layers for the moment, the default is set to 50 microns - ChipSpecifics iTofChipSpecifics{258, 271, 250.00e-4, 100.00e-4, 0.00f, 0.00e-4, 0.00e-4, 50.e-4, 50.e-4}; - ChipSpecifics oTofChipSpecifics{251, 487, 250.00e-4, 100.00e-4, 0.00f, 0.00e-4, 106.48e-4, 50.e-4, 50.e-4}; - O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx index eb209931207e3..edaeb1e848d06 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx @@ -12,6 +12,7 @@ #include #include #include +#include namespace o2 { @@ -261,8 +262,31 @@ void GeometryTGeo::Build(int loadTrans) LOG(info) << "numberOfChipsITOF = " << mNumberOfChipsIOTOF[0] << ", numberOfChipsOTOF = " << mNumberOfChipsIOTOF[1] << ", numberOfChips = " << numberOfChips << ", mNumberOfChipesPerStaveITOF" << mNumberOfChipsPerStaveIOTOF[0]; setSize(numberOfChips); + defineSensors(); + fillTrackingFramesCache(); fillMatrixCache(loadTrans); - // fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); +// fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); +} + +void GeometryTGeo::defineSensors() +{ + for (int i = 0; i < mSize; i++) { + sensors.push_back(i); + } +} + +void GeometryTGeo::fillTrackingFramesCache() +{ + // fill for every sensor of IOTOF its tracking frame parameters + if (!isTrackingFrameCached() && !sensors.empty()) { + size_t newSize = sensors.size(); + mCacheRefX.resize(newSize); + mCacheRefAlpha.resize(newSize); + for (int i = 0; i < newSize; i++) { + int sensorId = sensors[i]; + extractSensorXAlpha(sensorId, mCacheRefX[i], mCacheRefAlpha[i]); + } + } } void GeometryTGeo::fillMatrixCache(int mask) @@ -273,6 +297,8 @@ void GeometryTGeo::fillMatrixCache(int mask) return; } + LOG(debug) << "Filling matrix cache for " << getName() << " with mask " << mask; + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)) && !getCacheL2G().isFilled()) { // Matrices for Local (Sensor!!! rather than the full chip) to Global frame transformation LOG(info) << "Loading " << getName() << " L2G matrices from TGeo; there are " << mSize << " matrices"; @@ -284,6 +310,51 @@ void GeometryTGeo::fillMatrixCache(int mask) cacheL2G.setMatrix(o2::math_utils::Transform3D(*hm), i); } } + + // build T2L matrices for IOTOF + if ((mask & o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L)) && !getCacheT2L().isFilled()) { + LOGP(info, "Loading {} T2L matrices from TGeo for IOTOF", getName()); + if (sensors.size()) { + int m_Size = sensors.size(); + auto& cacheT2L = getCacheT2L(); + cacheT2L.setSize(m_Size); + for (int i = 0; i < m_Size; i++) { + int sensorID = sensors[i]; + TGeoHMatrix& hm = createT2LMatrix(sensorID); + cacheT2L.setMatrix(Mat3D(hm), i); + } + } + } +} + +void GeometryTGeo::extractSensorXAlpha(int chipID, float& x, float& alp) +{ + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + double xp{0}, yp{0}; + + const TGeoHMatrix* matL2G = extractMatrixSensor(chipID); + matL2G->LocalToMaster(locA, gloA); + matL2G->LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + xp = gloB[0] - dx * t; + yp = gloB[1] - dy * t; + + alp = std::atan2(yp, xp); + x = std::hypot(xp, yp); + o2::math_utils::bringTo02Pi(alp); +} + +TGeoHMatrix& GeometryTGeo::createT2LMatrix(int chipID) +{ + static TGeoHMatrix t2l; + t2l.Clear(); + float alpha = getSensorRefAlpha(chipID); + t2l.RotateZ(alpha * TMath::RadToDeg()); + const TGeoHMatrix* matL2G = extractMatrixSensor(chipID); + const TGeoHMatrix& matL2Gi = matL2G->Inverse(); + t2l.MultiplyLeft(&matL2Gi); + return t2l; } GeometryTGeo* GeometryTGeo::Instance() diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/IOTOFBaseParam.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/IOTOFBaseParam.cxx index 22488b2cc9e14..f6799f41e3786 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/IOTOFBaseParam.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/IOTOFBaseParam.cxx @@ -11,4 +11,6 @@ #include "IOTOFBase/IOTOFBaseParam.h" -O2ParamImpl(o2::iotof::IOTOFBaseParam); \ No newline at end of file +O2ParamImpl(o2::iotof::IOTOFBaseParam); +O2ParamImpl(o2::iotof::ITOFChipSpecificParam); +O2ParamImpl(o2::iotof::OTOFChipSpecificParam); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt index 25d623c0047a9..63aaab105d9c7 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/CMakeLists.txt @@ -1,28 +1,31 @@ -# 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. +#Copyright 2019 - 2020 CERN and copyright holders of ALICE O2. +#See https: // alice-o2.web.cern.ch/copyright for details of the copyright holders. +#All rights not expressly granted are reserved. # -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +#This software is distributed under the terms of the GNU General Public +#License v3(GPL Version 3), copied verbatim in the file "COPYING". # -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization +#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(IOTOFSimulation SOURCES src/Layer.cxx + src/Chip.cxx src/Detector.cxx src/Digitizer.cxx - # src/IOTOFServices.cxx - src/Segmentation.cxx + src/DPLDigitizerParam.cxx + #src/IOTOFServices.cxx + src/Segmentation.cxx PUBLIC_LINK_LIBRARIES O2::IOTOFBase O2::DataFormatsIOTOF O2::ITSMFTSimulation) o2_target_root_dictionary(IOTOFSimulation - HEADERS include/IOTOFSimulation/Detector.h - include/IOTOFSimulation/Layer.h - include/IOTOFSimulation/Digitizer.h - # include/IOTOFSimulation/IOTOFServices.h - include/IOTOFSimulation/Segmentation.h - ) + HEADERS include/IOTOFSimulation/Detector.h + include/IOTOFSimulation/Chip.h + include/IOTOFSimulation/Layer.h + include/IOTOFSimulation/Digitizer.h + include/IOTOFSimulation/DPLDigitizerParam.h + #include/IOTOFSimulation/IOTOFServices.h + include/IOTOFSimulation/Segmentation.h) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Chip.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Chip.h new file mode 100644 index 0000000000000..43729682d06dc --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Chip.h @@ -0,0 +1,98 @@ +// 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. + +// +// TOF Chip class: it will be used to store the digits at TOF that +// fall in the same Chip +// + +//////////////////////////////////// +// To put in O2/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/Chip.h + +#ifndef ALICEO2_IOTOF_CHIP_H_ +#define ALICEO2_IOTOF_CHIP_H_ + +#include +#include +#include +#include +#include +#include +#include "MathUtils/Cartesian.h" + +namespace o2::iotof +{ + +/// @class Chip +/// @brief Container for similated points connected to a given TOF Chip +/// This will be used in order to allow a more efficient clusterization +/// that can happen only between digits that belong to the same Chip +/// + +class Chip +{ + + public: + /// Default constructor + Chip() = default; + + /// Destructor + ~Chip() = default; + + /// Main constructor + /// @param Chipindex Index of the Chip + /// @param mat Transformation matrix + Chip(Int_t index); + + /// Copy constructor + /// @param ref Reference for the copy + Chip(const Chip& ref) = default; + + /// Empties the point container + /// @param option unused + void clear() { mDigits.clear(); } + + std::map& getDigits() { return mDigits; } + bool isEmpty() const { return mDigits.empty(); } + + void setChipIndex(Int_t index) { mChipIndex = index; } + Int_t getChipIndex() const { return mChipIndex; } + + void disable(bool disable) { mDisabled = disable; } + bool isDisabled() const { return mDisabled; } + + /// Get the number of point assigned to the chip + /// @return Number of points assigned to the chip + Int_t getNumberOfDigits() const { return mDigits.size(); } + + /// reset points container + o2::iotof::LabeledDigit* findDigit(ULong64_t key); + + void addDigit(UShort_t row, UShort_t col, Int_t charge, double time); + + protected: + Int_t mChipIndex = -1; ///< Chip ID + bool mDisabled = false; ///< Flag to indicate if the chip is disabled (e.g. due to dead channels) + std::map mDigits; ///< Map of fired digits, possibly in multiple frames + + ClassDefNV(Chip, 1); +}; + +inline o2::iotof::LabeledDigit* Chip::findDigit(ULong64_t key) +{ + // finds the digit corresponding to global key + auto digitentry = mDigits.find(key); + return digitentry != mDigits.end() ? &(digitentry->second) : nullptr; +} + +} // namespace o2::iotof + +#endif /* defined(ALICEO2_IOTOF_CHIP_H_) */ \ No newline at end of file diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h new file mode 100644 index 0000000000000..5e4313a64dd5b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/DPLDigitizerParam.h @@ -0,0 +1,69 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_TF3DPLDIGITIZERPARAM_H_ +#define ALICEO2_TF3DPLDIGITIZERPARAM_H_ + +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include + +namespace o2 +{ +namespace iotof +{ +template +struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelper> { + static_assert(N == o2::detectors::DetID::TF3, "only DetID::TF3 is allowed"); + + static constexpr std::string_view getParamName() + { + return ParamName[0]; + } + + bool continuous = true; ///< flag for continuous simulation + float noisePerPixel = DEFNoisePerPixel(); ///< ALPIDE Noise per channel + float strobeFlatTop = 20.; ///< strobe shape flat top + float strobeMaxRiseTime = 0.; ///< strobe max rise time + float strobeQRiseTime0 = 0.; ///< q @ which strobe rise time is 0 + + double timeOffset = 0.; ///< time offset (in seconds!) to calculate ROFrame from hit time + int chargeThreshold = 75; ///< charge threshold in Nelectrons + int minChargeToAccount = 7; ///< minimum charge contribution to account + int nSimSteps = 475; ///< number of steps in response simulation + float energyToNElectrons = 1. / 3.6e-9; // conversion of eloss to Nelectrons + + float Vbb = 0.0; ///< back bias absolute value for MFT (in Volt) + float IBVbb = 0.0; ///< back bias absolute value for ITS Inner Barrel (in Volt) + float OBVbb = 0.0; ///< back bias absolute value for ITS Outter Barrel (in Volt) + + std::string noiseFilePath{}; ///< optional noise masks file path. FIXME to be removed once switch to CCDBFetcher + + // boilerplate stuff + make principal key + O2ParamDef(DPLDigitizerParam, getParamName().data()); + + private: + static constexpr float DEFNoisePerPixel() + { + return 1e-8; // ITS/MFT values here!! + } + + static constexpr std::string_view ParamName[1] = {"TF3DigitizerParam"}; +}; + +template +DPLDigitizerParam DPLDigitizerParam::sInstance; + +} // namespace iotof +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h index aae989248f07e..a0c55f62eb3a4 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Digitizer.h @@ -19,9 +19,16 @@ #ifndef ALICEO2_IOTOF_DIGITIZER_H #define ALICEO2_IOTOF_DIGITIZER_H +#include +#include +#include + +#include "Rtypes.h" // for Digitizer::Class +#include "TObject.h" // for TObject + #include "ITSMFTSimulation/Hit.h" -#include "DataFormatsITSMFT/Digit.h" #include "DataFormatsIOTOF/Digit.h" +#include "IOTOFSimulation/Chip.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "CommonDataFormat/InteractionRecord.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -40,7 +47,7 @@ namespace o2::iotof /// - Converting energy loss to charge /// - Applying charge threshold /// - Managing readout frames (ROF) -class Digitizer +class Digitizer : public TObject { public: void setDigits(std::vector* dig) { mDigits = dig; } @@ -81,6 +88,10 @@ class Digitizer /// Process a single hit void processHit(const o2::itsmft::Hit& hit, int evID, int srcID); + /// Register digits in a given chip + void registerDigits(Chip& chip, uint32_t roFrame, float timeInitROF, int nROF, + uint16_t row, uint16_t col, int nElectrons, o2::MCCompLabel& label); + /// Apply time smearing to simulate detector resolution double smearTime(double time) const; @@ -90,10 +101,27 @@ class Digitizer /// Check if the hit passes efficiency cut bool isEfficient() const; + std::vector* getExtraLabelBuffer(uint32_t roFrame) + { + // if (mROFrameMin > roFrame) { + // return nullptr; // nothing to do + // } + // int index = roFrame - mROFrameMin; + + int index = roFrame; + while (index >= int(mExtraLabelBuffer.size())) { + mExtraLabelBuffer.emplace_back(std::make_unique>()); + } + return mExtraLabelBuffer[index].get(); + } + static constexpr float sec2ns = 1e9f; ///< seconds to nanoseconds conversion const o2::iotof::GeometryTGeo* mGeometry = nullptr; ///< IOTOF geometry + std::vector mChips; //! Chips in the detector, indexed by chip ID + std::deque>> mExtraLabelBuffer; //! buffer for multiple mc labels to the same pixel + std::vector* mDigits = nullptr; //! output digits std::vector* mROFRecords = nullptr; //! output ROF records o2::dataformats::MCTruthContainer* mMCLabels = nullptr; //! output labels @@ -108,6 +136,8 @@ class Digitizer float mEnergyToCharge = 3.6e-9f; ///< energy loss to electrons conversion (3.6 eV per e-h pair in Si) static o2::iotof::Segmentation* sSegmentation; ///< IOTOF segmentation instance (singleton) + + ClassDefNV(Digitizer, 1); }; } // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Chip.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Chip.cxx new file mode 100644 index 0000000000000..3b737d35ac359 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Chip.cxx @@ -0,0 +1,39 @@ +// 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. + +// +// Chip.cxx: structure to store the TOF digits in Chips - useful +// for clusterization purposes +// ALICEO2 +// +#include +#include + +#include +#include + +#include "IOTOFSimulation/Chip.h" + +using namespace o2::iotof; + +ClassImp(o2::iotof::Chip); + +//_______________________________________________________________________ +Chip::Chip(Int_t index) + : mChipIndex(index) +{ +} +//_______________________________________________________________________ +void Chip::addDigit(UShort_t row, UShort_t col, Int_t charge, double time) +{ + ULong64_t key = Digit::getOrderingKey(mChipIndex, row, col); + mDigits.emplace(std::make_pair(key, LabeledDigit(mChipIndex, row, col, charge, time))); +} diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/DPLDigitizerParam.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/DPLDigitizerParam.cxx new file mode 100644 index 0000000000000..c5fc19430174e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/DPLDigitizerParam.cxx @@ -0,0 +1,22 @@ +// 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 "IOTOFSimulation/DPLDigitizerParam.h" + +namespace o2 +{ +namespace iotof +{ +// this makes sure that the constructor of the parameters is statically called +// so that these params are part of the parameter database +static auto& sDigitizerParamTF3 = o2::iotof::DPLDigitizerParam::Instance(); +} // namespace iotof +} // namespace o2 diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx index 8e5e74dd1f0ca..9e11e8e981ad4 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Digitizer.cxx @@ -34,6 +34,22 @@ o2::iotof::Segmentation* Digitizer::sSegmentation = nullptr; //_______________________________________________________________________ void Digitizer::init() { + const int numberOfChips = mGeometry->getSize(); + mChips.resize(numberOfChips); + for (int i = numberOfChips; i--;) { + mChips[i].setChipIndex(i); + /// Noise map to be implemented + /// if (mNoiseMap) { + /// mChips[i].setNoiseMap(mNoiseMap); + /// } + + /// Dead channel map to be implemented + /// if (mDeadChanMap) { + /// mChips[i].disable(mDeadChanMap->isFullChipMasked(i)); + /// mChips[i].setDeadChanMap(mDeadChanMap); + /// } + } + LOG(info) << "Initializing IOTOF digitizer"; LOG(info) << " Time resolution: " << mTimeResolution * 1e3 << " ps"; LOG(info) << " Charge threshold: " << mChargeThreshold << " electrons"; @@ -67,6 +83,7 @@ void Digitizer::process(const std::vector* hits, int evID, int // In triggered mode, flush output after each event if (!mContinuous) { + LOG(debug) << "Inner flushing for non-continuous mode"; fillOutputContainer(); } } @@ -83,7 +100,12 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) } // Get detector element ID - int detID = hit.GetDetectorID(); + int chipID = hit.GetDetectorID(); + auto& chip = mChips[chipID]; + if (chip.isDisabled()) { + LOG(debug) << "Hit rejected because chip " << chipID << " is disabled"; + return; + } // Convert energy loss to charge (number of electrons) float energyLoss = hit.GetEnergyLoss(); // in GeV @@ -104,10 +126,10 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) // For now, use simple row/col mapping from detector ID // TODO: Implement proper segmentation when geometry is finalized - uint16_t chipIndex = static_cast(detID); + uint16_t chipIndex = static_cast(chipID); - if (detID > mGeometry->getSize() || mGeometry->getSize() < 1) { - LOG(debug) << "Invalid detector ID: " << detID; + if (chipID > mGeometry->getSize() || mGeometry->getSize() < 1) { + LOG(debug) << "Invalid detector ID: " << chipID << ", geometry size: " << mGeometry->getSize(); return; // invalid detector ID } const auto& matrix = mGeometry->getMatrixL2G(hit.GetDetectorID()); @@ -118,23 +140,19 @@ void Digitizer::processHit(const o2::itsmft::Hit& hit, int evID, int srcID) int row = 0; // Will be determined from start hit position int col = 0; // Will be determined from start hit position - if (!sSegmentation->localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), row, col, mGeometry->getIOTOFLayer(detID))) { - LOG(debug) << "Hit position out of bounds for detector ID " << detID; + if (!sSegmentation->localToDetector(xyzPositionStart.X(), xyzPositionStart.Z(), row, col, mGeometry->getIOTOFLayer(chipID))) { + LOG(debug) << "Hit position out of bounds for detector ID " << chipID; return; // hit is outside the active area } // Create the digit with time information int digID = mDigits->size(); - mDigits->emplace_back(chipIndex, static_cast(row), static_cast(col), charge, smearedTime); - - LOG(debug) << "Created digit #" << digID << " chip=" << chipIndex - << " charge=" << charge << " time=" << smearedTime << " ns"; + o2::MCCompLabel label(hit.GetTrackID(), evID, srcID, false); + const int roFrameAbs = 0; // For now, we can set this to 0 or calculate based on time if needed + const int timeInitROF = 0; // For now, we can set this to 0 or calculate based on time if needed + const int nROF = 1; // For now, we can assume the signal is contained in one ROF, this can be extended to multiple ROFs based on the time - // Add MC truth label - if (mMCLabels) { - o2::MCCompLabel lbl(hit.GetTrackID(), evID, srcID, false); - mMCLabels->addElement(digID, lbl); - } + registerDigits(chip, roFrameAbs, timeInitROF, nROF, static_cast(row), static_cast(col), charge, label); } //_______________________________________________________________________ @@ -166,14 +184,74 @@ bool Digitizer::isEfficient() const //_______________________________________________________________________ void Digitizer::fillOutputContainer() { - // Create ROF record for the current event - if (mROFRecords && mDigits && !mDigits->empty()) { - o2::itsmft::ROFRecord rof; - rof.setFirstEntry(0); - rof.setNEntries(mDigits->size()); - rof.setBCData(mEventTime); - mROFRecords->push_back(rof); - LOG(debug) << "Created ROF record with " << mDigits->size() << " digits"; + LOG(info) << "Filling output container with digits from chips"; + LOG(debug) << "Number of chips: " << mChips.size(); + + o2::itsmft::ROFRecord rof; + rof.setFirstEntry(mDigits->size()); // index of the first digit + + auto& extraLabelBuffer = *(mExtraLabelBuffer.front().get()); // buffer for extra labels + for (auto& chip : mChips) { + + if (chip.isDisabled()) { + continue; + } + + /// chip.addNoise(...); // to be implemented + + if (chip.isEmpty()) { + continue; + } + + auto& chipDigits = chip.getDigits(); + for (const auto& [key, digit] : chipDigits) { + + /// Charge threshold not implemented yet + /// if (digit.getCharge() < mChargeThreshold) { + /// continue; // skip digits below threshold + /// } + + int digitID = mDigits->size(); + mDigits->emplace_back(digit.getChipIndex(), digit.getRow(), digit.getColumn(), digit.getCharge(), digit.getTime()); + mMCLabels->addElement(digitID, digit.getLabel().mLabel); + auto labelRef = digit.getLabel(); + + while (labelRef.mNext >= 0) { + labelRef = extraLabelBuffer[labelRef.mNext]; + mMCLabels->addElement(digitID, labelRef.mLabel); + } + } + chipDigits.clear(); // clear chip digits after copying to output + } + + rof.setNEntries(mDigits->size() - rof.getFirstEntry()); // number of digits + rof.setBCData(mEventTime); + mROFRecords->push_back(rof); + LOG(debug) << "Created ROF record with " << mDigits->size() << " digits"; + + // extraLabelBuffer.clear(); // clear buffer for extra labels + // mExtraLabelBuffer.emplace_back(mExtraLabelBuffer.front().release()); // move current buffer to the end + // mExtraLabelBuffer.pop_front(); +} + +void Digitizer::registerDigits(Chip& chip, uint32_t roFrame, float timeInitROF, int nROF, + uint16_t row, uint16_t col, int nElectrons, o2::MCCompLabel& label) +{ + + auto key = o2::iotof::Digit::getOrderingKey(roFrame, row, col); + o2::iotof::LabeledDigit* existingDigit = chip.findDigit(key); + if (!existingDigit) { + // No existing digit, create a new one + chip.addDigit(row, col, nElectrons, timeInitROF); // Last one should really just be time + } else { + // Digit already exists, update charge and labels + const int storedCharge = existingDigit->getCharge(); + existingDigit->setCharge(storedCharge + nElectrons); + if (existingDigit->getLabel().mLabel == label) { + return; // don't store the same label twice + } + std::vector* extra = getExtraLabelBuffer(roFrame); + extra->emplace_back(label); } } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h index f6f45ba5cda5f..6d507e2c112c2 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/IOTOFSimulationLinkDef.h @@ -22,4 +22,16 @@ #pragma link C++ class o2::iotof::Detector + ; #pragma link C++ class o2::base::DetImpl < o2::iotof::Detector> + ; +#pragma link C++ class o2::iotof::Digitizer + ; +#pragma link C++ class o2::iotof::DPLDigitizerParam < o2::detectors::DetID::TF3> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::iotof::DPLDigitizerParam < o2::detectors::DetID::TF3>> + ; + +#pragma link C++ class o2::iotof::ChipSpecifics + ; +#pragma link C++ class o2::iotof::ITOFChipSpecifics + ; +#pragma link C++ class o2::iotof::OTOFChipSpecifics + ; +#pragma link C++ class o2::iotof::ITOFChipSpecificParam + ; +#pragma link C++ class o2::conf::ConfigurableParamPromoter < o2::iotof::ITOFChipSpecificParam, o2::iotof::ITOFChipSpecifics> + ; +#pragma link C++ class o2::iotof::OTOFChipSpecificParam + ; +#pragma link C++ class o2::conf::ConfigurableParamPromoter < o2::iotof::OTOFChipSpecificParam, o2::iotof::OTOFChipSpecifics> + ; + #endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Segmentation.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Segmentation.cxx index bbfb60234210d..ea03b3d317cdc 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Segmentation.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Segmentation.cxx @@ -37,9 +37,10 @@ Segmentation::Segmentation() if (sInstance) { printf("Invalid use of public constructor: o2::iotof::Segmentation instance exists\n"); } else { - auto& iotofPars = IOTOFBaseParam::Instance(); - const ChipSpecifics& mITofChipPars = iotofPars.iTofChipSpecifics; - const ChipSpecifics& mOTofChipPars = iotofPars.oTofChipSpecifics; + auto& itofPars = ITOFChipSpecificParam::Instance(); + auto& otofPars = OTOFChipSpecificParam::Instance(); + const ChipSpecifics mITofChipPars(itofPars.NCols, itofPars.NRows, itofPars.PitchCol, itofPars.PitchRow, itofPars.PassiveEdgeReadOut, itofPars.PassiveEdgeTop, itofPars.PassiveEdgeSide, itofPars.SensorLayerThicknessEff, itofPars.SensorLayerThickness); + const ChipSpecifics mOTofChipPars(otofPars.NCols, otofPars.NRows, otofPars.PitchCol, otofPars.PitchRow, otofPars.PassiveEdgeReadOut, otofPars.PassiveEdgeTop, otofPars.PassiveEdgeSide, otofPars.SensorLayerThicknessEff, otofPars.SensorLayerThickness); configChip(mITofChipPars, 0 /* subDetectorID for iTOF */); configChip(mOTofChipPars, 1 /* subDetectorID for oTOF */); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/workflow/CMakeLists.txt new file mode 100644 index 0000000000000..73ae6edd15cf4 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/CMakeLists.txt @@ -0,0 +1,28 @@ +# 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(IOTOFWorkflow + TARGETVARNAME targetName + SOURCES src/DigitReaderSpec.cxx + src/DigitWriterSpec.cxx + src/RecoWorkflow.cxx + PUBLIC_LINK_LIBRARIES O2::Framework + O2::DataFormatsIOTOF + O2::DataFormatsITSMFT + O2::DPLUtils + ) + +o2_add_executable(reco-workflow + SOURCES src/iotof-reco-workflow.cxx + COMPONENT_NAME alice3-iotof + PUBLIC_LINK_LIBRARIES O2::IOTOFWorkflow + O2::IOTOFSimulation + ) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/DigitReaderSpec.h b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/DigitReaderSpec.h new file mode 100644 index 0000000000000..1309bfc711d23 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/DigitReaderSpec.h @@ -0,0 +1,69 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_IOTOF_DIGITREADER +#define O2_IOTOF_DIGITREADER + +#include "TFile.h" +#include "TTree.h" +#include "DataFormatsIOTOF/Digit.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "Headers/DataHeader.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DetectorsCommonDataFormats/DetID.h" + +namespace o2::iotof +{ + +class TF3DigitReader : public o2::framework::Task +{ + public: + TF3DigitReader() = delete; + TF3DigitReader(o2::detectors::DetID id, bool useMC, bool useCalib); + ~TF3DigitReader() override = default; + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + protected: + void connectTree(const std::string& filename); + + std::vector mDigits, *mDigitsPtr = &mDigits; + std::vector mDigROFRec, *mDigROFRecPtr = &mDigROFRec; + std::vector mDigMC2ROFs, *mDigMC2ROFsPtr = &mDigMC2ROFs; + o2::header::DataOrigin mOrigin = o2::header::gDataOriginTF3; + + std::unique_ptr mFile; + std::unique_ptr mTree; + + bool mUseMC = true; // use MC truth + bool mUseCalib = true; // send calib data + + std::string mDetName = ""; + std::string mDetNameLC = ""; + std::string mFileName = ""; + std::string mDigTreeName = "o2sim"; + std::string mDigitBranchName = "Digit"; + std::string mDigROFBranchName = "DigitROF"; + std::string mCalibBranchName = "Calib"; + + std::string mDigtMCTruthBranchName = "DigitMCTruth"; + std::string mDigtMC2ROFBranchName = "DigitMC2ROF"; + // static constexpr o2::detectors::DetID mDetID = o2::header::gDataOriginTF3; +}; + +/// create a processor spec +/// read ITS/MFT Digit data from a root file +o2::framework::DataProcessorSpec getIOTOFDigitReaderSpec(bool useMC = true, bool useCalib = false, std::string defname = "iotofdigits.root"); + +} // namespace o2::iotof + +#endif /* O2_IOTOF_DigitREADER */ diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/DigitWriterSpec.h b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/DigitWriterSpec.h new file mode 100644 index 0000000000000..7fff4fd000c2d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/DigitWriterSpec.h @@ -0,0 +1,26 @@ +// 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_TF3DIGITWRITER_H_ +#define STEER_TF3DIGITWRITER_H_ + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace iotof +{ + +o2::framework::DataProcessorSpec getIOTOFDigitWriterSpec(bool mctruth = true, bool dec = false, bool calib = false); +} // namespace iotof +} // end namespace o2 + +#endif /* STEER_TF3DIGITWRITER_H_ */ diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/RecoWorkflow.h new file mode 100644 index 0000000000000..6310ec6348a8e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/include/IOTOFWorkflow/RecoWorkflow.h @@ -0,0 +1,32 @@ +// 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_TF3_RECOWORKFLOW_H +#define O2_TF3_RECOWORKFLOW_H + +#include "Framework/WorkflowSpec.h" +#include + +namespace o2::iotof +{ +namespace reco_workflow +{ + +o2::framework::WorkflowSpec getWorkflow(bool useMC, + // const std::string& hitRecoConfig, + bool upstreamDigits = false, + bool upstreamClusters = false, + bool disableRootOutput = false); +} + +} // namespace o2::iotof + +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/DigitReaderSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/DigitReaderSpec.cxx new file mode 100644 index 0000000000000..9ff4213a951b8 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/DigitReaderSpec.cxx @@ -0,0 +1,134 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include + +#include "TTree.h" + +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "IOTOFWorkflow/DigitReaderSpec.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" + +using namespace o2::framework; + +namespace o2::iotof +{ + +TF3DigitReader::TF3DigitReader(o2::detectors::DetID id, bool useMC, bool useCalib) +{ + assert(id == o2::detectors::DetID::TF3); + mDetNameLC = mDetName = id.getName(); + mDigTreeName = "o2sim"; + + mDigitBranchName = mDetName + mDigitBranchName; + mDigROFBranchName = mDetName + mDigROFBranchName; + mCalibBranchName = mDetName + mCalibBranchName; + + mDigtMCTruthBranchName = mDetName + mDigtMCTruthBranchName; + mDigtMC2ROFBranchName = mDetName + mDigtMC2ROFBranchName; + + mUseMC = useMC; + mUseCalib = useCalib; + std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); +} + +void TF3DigitReader::init(o2::framework::InitContext& ic) +{ + mFileName = ic.options().get((mDetNameLC + "-digit-infile").c_str()); + connectTree(mFileName); +} + +void TF3DigitReader::run(o2::framework::ProcessingContext& pc) +{ + auto ent = mTree->GetReadEntry() + 1; + assert(ent < mTree->GetEntries()); // this should not happen + + o2::dataformats::IOMCTruthContainerView* plabels = nullptr; + if (mUseMC) { + mTree->SetBranchAddress(mDigtMCTruthBranchName.c_str(), &plabels); + } + mTree->GetEntry(ent); + LOG(info) << mDetName << "TF3DigitReader pushes " << mDigROFRec.size() << " ROFRecords, " + << mDigits.size() << " digits 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, "DIGITSROF", 0}, mDigROFRec); + pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); + if (mUseCalib) { + // pc.outputs().snapshot(Output{mOrigin, "GBTCALIB", 0}, mCalib); + } + + if (mUseMC) { + auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); + plabels->copyandflatten(sharedlabels); + delete plabels; + pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, mDigMC2ROFs); + } + + if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } +} + +void TF3DigitReader::connectTree(const std::string& filename) +{ + mTree.reset(nullptr); // in case it was already loaded + mFile.reset(TFile::Open(filename.c_str())); + assert(mFile && !mFile->IsZombie()); + mTree.reset((TTree*)mFile->Get(mDigTreeName.c_str())); + assert(mTree); + + mTree->SetBranchAddress(mDigROFBranchName.c_str(), &mDigROFRecPtr); + mTree->SetBranchAddress(mDigitBranchName.c_str(), &mDigitsPtr); + if (mUseCalib) { + if (!mTree->GetBranch(mCalibBranchName.c_str())) { + throw std::runtime_error("GBT calibration data requested but not found in the tree"); + } + // mTree->SetBranchAddress(mCalibBranchName.c_str(), &mCalibPtr); + } + if (mUseMC) { + if (!mTree->GetBranch(mDigtMCTruthBranchName.c_str())) { + throw std::runtime_error("MC data requested but not found in the tree"); + } + mTree->SetBranchAddress(mDigtMC2ROFBranchName.c_str(), &mDigMC2ROFsPtr); + } + LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; +} + +DataProcessorSpec getIOTOFDigitReaderSpec(bool useMC, bool useCalib, std::string defname) +{ + std::vector outputSpec; + outputSpec.emplace_back("TF3", "DIGITS", 0, Lifetime::Timeframe); + outputSpec.emplace_back("TF3", "DIGITSROF", 0, Lifetime::Timeframe); + if (useCalib) { + // outputSpec.emplace_back("TF3", "GBTCALIB", 0, Lifetime::Timeframe); + } + if (useMC) { + outputSpec.emplace_back("TF3", "DIGITSMCTR", 0, Lifetime::Timeframe); + outputSpec.emplace_back("TF3", "DIGITSMC2ROF", 0, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + "iotof-digit-reader", + Inputs{}, + outputSpec, + AlgorithmSpec{adaptFromTask(o2::detectors::DetID::TF3, useMC, useCalib)}, + Options{ + {"tf3-digit-infile", VariantType::String, defname, {"Name of the input digit file"}}}}; +} + +} // namespace o2::iotof diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/DigitWriterSpec.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/DigitWriterSpec.cxx new file mode 100644 index 0000000000000..5a145e966781e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/DigitWriterSpec.cxx @@ -0,0 +1,110 @@ +// 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. + +/// @brief Processor spec for a ROOT file writer for ITSMFT digits + +#include "IOTOFWorkflow/DigitWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsIOTOF/Digit.h" +#include "DataFormatsITSMFT/GBTCalibData.h" +#include "Headers/DataHeader.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/IOMCTruthContainerView.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include +#include +#include + +using namespace o2::framework; +using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; + +namespace o2 +{ +namespace iotof +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using MCCont = o2::dataformats::ConstMCTruthContainer; + +/// create the processor spec +/// describing a processor receiving digits for ITS/MFT and writing them to file +DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib, o2::header::DataOrigin detOrig, o2::detectors::DetID detId) +{ + std::string detStr = o2::detectors::DetID::getName(detId); + std::string detStrL = dec ? "o2_" : ""; // for decoded digits prepend by o2 + detStrL += detStr; + std::transform(detStrL.begin(), detStrL.end(), detStrL.begin(), ::tolower); + auto logger = [](std::vector const& inDigits) { + LOG(info) << "RECEIVED DIGITS SIZE " << inDigits.size(); + }; + + // the callback to be set as hook for custom action when the writer is closed + auto finishWriting = [](TFile* outputfile, TTree* outputtree) { + const auto* brArr = outputtree->GetListOfBranches(); + int64_t nent = 0; + for (const auto* brc : *brArr) { + int64_t n = ((const TBranch*)brc)->GetEntries(); + if (nent && (nent != n)) { + LOG(error) << "Branches have different number of entries"; + } + nent = n; + } + outputtree->SetEntries(nent); + outputtree->Write("", TObject::kOverwrite); + outputfile->Close(); + }; + + // handler for labels + // This is necessary since we can't store the original label buffer in a ROOT entry -- as is -- if it exceeds a certain size. + // We therefore convert it to a special split class. + auto fillLabels = [](TBranch& branch, std::vector const& labelbuffer, DataRef const& /*ref*/) { + o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); + LOG(info) << "WRITING " << labels.getNElements() << " LABELS "; + + o2::dataformats::IOMCTruthContainerView outputcontainer; + auto ptr = &outputcontainer; + auto br = framework::RootTreeWriter::remapBranch(branch, &ptr); + outputcontainer.adopt(labelbuffer); + br->Fill(); + br->ResetAddress(); + }; + + return MakeRootTreeWriterSpec((detStr + "DigitWriter" + (dec ? "_dec" : "")).c_str(), + (detStrL + "digits.root").c_str(), + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Digits tree"}, + MakeRootTreeWriterSpec::CustomClose(finishWriting), + // in case of labels we first read them as std::vector and process them correctly in the fillLabels hook + BranchDefinition>{InputSpec{"digitsMCTR", detOrig, "DIGITSMCTR", 0}, + (detStr + "DigitMCTruth").c_str(), + (mctruth ? 1 : 0), fillLabels}, + BranchDefinition>{InputSpec{"digitsMC2ROF", detOrig, "DIGITSMC2ROF", 0}, + (detStr + "DigitMC2ROF").c_str(), + (mctruth ? 1 : 0)}, + BranchDefinition>{InputSpec{"digits", detOrig, "DIGITS", 0}, + (detStr + "Digit").c_str(), + logger}, + // BranchDefinition>{InputSpec{"calib", detOrig, "GBTCALIB", 0}, + // (detStr + "Calib").c_str(), + // (calib ? 1 : 0)}, + BranchDefinition>{InputSpec{"digitsROF", detOrig, "DIGITSROF", 0}, + (detStr + "DigitROF").c_str()})(); +} + +DataProcessorSpec getIOTOFDigitWriterSpec(bool mctruth, bool dec, bool calib) +{ + return getDigitWriterSpec(mctruth, dec, calib, o2::header::gDataOriginTF3, o2::detectors::DetID::TF3); +} + +} // end namespace iotof +} // end namespace o2 diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx new file mode 100644 index 0000000000000..b9be173842d7d --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/RecoWorkflow.cxx @@ -0,0 +1,43 @@ +// 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 "IOTOFWorkflow/RecoWorkflow.h" +#include "IOTOFWorkflow/DigitReaderSpec.h" +#include "Framework/CCDBParamSpec.h" + +#include + +namespace o2::iotof::reco_workflow +{ + +framework::WorkflowSpec getWorkflow(bool useMC, + // const std::string& hitRecoConfig, + bool upstreamDigits, + bool upstreamClusters, + bool disableRootOutput) +{ + framework::WorkflowSpec specs; + + if (!(upstreamDigits || upstreamClusters)) { + specs.emplace_back(o2::iotof::getIOTOFDigitReaderSpec(useMC, false, "tf3digits.root")); + } + if (!upstreamClusters) { + // specs.emplace_back(o2::iotof::getClustererSpec(useMC)); + } + + if (!disableRootOutput) { + // specs.emplace_back(o2::iotof::getClusterWriterSpec(useMC)); + } + + return specs; +} + +} // namespace o2::iotof::reco_workflow diff --git a/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx new file mode 100644 index 0000000000000..ef0f48d4c2099 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/workflow/src/iotof-reco-workflow.cxx @@ -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. + +// 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 "IOTOFWorkflow/RecoWorkflow.h" +#include "CommonUtils/ConfigurableParam.h" +// #include "ITStracking/TrackingConfigParam.h" +// #include "ITStracking/Configuration.h" + +#include "Framework/CallbacksPolicy.h" +#include "Framework/ConfigContext.h" +#include "Framework/CompletionPolicyHelpers.h" + +#include + +using namespace o2::framework; + +void customize(std::vector& policies) +{ + // o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} + +void customize(std::vector& policies) +{ + // ordered policies for the writers + policies.push_back(CompletionPolicyHelpers::consumeWhenAllOrdered(".*(?:TF3|iotof).*[W,w]riter.*")); +} + +void customize(std::vector& workflowOptions) +{ + // option allowing to set parameters + std::vector options{ + {"digits-from-upstream", VariantType::Bool, false, {"digits will be provided from upstream, skip digits reader"}}, + {"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)"}}, + // {"gpu-device", VariantType::Int, 1, {"use gpu device: CPU=1,CUDA=2,HIP=3 (default: CPU)"}} + }; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" +#include "Framework/Logger.h" + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::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"); + auto extClusters = configcontext.options().get("clusters-from-upstream"); + auto disableRootOutput = configcontext.options().get("disable-root-output"); + o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); + + // write the configuration used for the reco workflow + o2::conf::ConfigurableParam::writeINI("o2itsrecoflow_configuration.ini"); + + return o2::iotof::reco_workflow::getWorkflow(useMC, /*hitRecoConfig,*/ extDigits, extClusters, disableRootOutput/*, useGpuWF, gpuDevice*/); +} diff --git a/Steer/DigitizerWorkflow/CMakeLists.txt b/Steer/DigitizerWorkflow/CMakeLists.txt index 10e8dc2b13995..9a4ac7c7fb8f4 100644 --- a/Steer/DigitizerWorkflow/CMakeLists.txt +++ b/Steer/DigitizerWorkflow/CMakeLists.txt @@ -29,6 +29,7 @@ o2_add_executable(digitizer-workflow src/TOFDigitizerSpec.cxx $<$:src/ITS3DigitizerSpec.cxx> $<$:src/TRKDigitizerSpec.cxx> + $<$:src/IOTOFDigitizerSpec.cxx> PUBLIC_LINK_LIBRARIES O2::Framework O2::Steer O2::CommonConstants @@ -69,7 +70,10 @@ o2_add_executable(digitizer-workflow $<$:O2::ITS3Simulation> $<$:O2::ITS3Workflow> $<$:O2::TRKSimulation> - $<$:O2::TRKWorkflow>) + $<$:O2::TRKWorkflow> + $<$:O2::IOTOFSimulation> + $<$:O2::IOTOFWorkflow> + ) o2_add_executable(mctruth-testworkflow diff --git a/Steer/DigitizerWorkflow/src/IOTOFDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/IOTOFDigitizerSpec.cxx new file mode 100644 index 0000000000000..11dd371c1de04 --- /dev/null +++ b/Steer/DigitizerWorkflow/src/IOTOFDigitizerSpec.cxx @@ -0,0 +1,209 @@ +// 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 "IOTOFDigitizerSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/DataRefUtils.h" +#include "Framework/Lifetime.h" +#include "Framework/Task.h" +#include "Steer/HitProcessingManager.h" +#include "DataFormatsITSMFT/Digit.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "DetectorsBase/BaseDPLDigitizer.h" +#include "DetectorsRaw/HBFUtils.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsCommonDataFormats/SimTraits.h" +#include "DataFormatsParameters/GRPObject.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "IOTOFSimulation/Digitizer.h" +#include "Headers/DataHeader.h" +#include "IOTOFBase/GeometryTGeo.h" +#include "IOTOFBase/IOTOFBaseParam.h" + +#include +#include + +#include +#include +#include + +using namespace o2::framework; + +namespace o2::iotof +{ + +class IOTOFDPLDigitizerTask : o2::base::BaseDPLDigitizer +{ + public: + using BaseDPLDigitizer::init; + + IOTOFDPLDigitizerTask(bool mctruth = true) : BaseDPLDigitizer(o2::base::InitServices::FIELD | o2::base::InitServices::GEOM), + mWithMCTruth(mctruth) {} + + void initDigitizerTask(framework::InitContext& ic) override + { + mDisableQED = ic.options().get("disable-qed"); + + auto geom = GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); // make sure L2G matrices are loaded + + mDigitizer.setGeometry(geom); + mDigitizer.setChargeThreshold(-1000.f); + mDigitizer.init(); + } + + void run(framework::ProcessingContext& pc) + { + if (mFinished) { + return; + } + mFirstOrbitTF = pc.services().get().firstTForbit; + const o2::InteractionRecord firstIR(0, mFirstOrbitTF); + + // read collision context from input + auto context = pc.inputs().get("collisioncontext"); + context->initSimChains(mID, mSimChains); + const bool withQED = context->isQEDProvided() && !mDisableQED; + auto& timesview = context->getEventRecords(); + LOG(info) << "GOT " << timesview.size() << " COLLISION TIMES"; + LOG(info) << "SIMCHAINS " << mSimChains.size(); + + // if there is nothing to do ... return + if (timesview.empty()) { + return; + } + + TStopwatch timer; + timer.Start(); + LOG(info) << " CALLING TF3 DIGITIZATION "; + + mDigitizer.setDigits(&mDigits); + mDigitizer.setROFRecords(&mROFRecords); + if (mWithMCTruth) { + mDigitizer.setMCLabels(&mLabels); + } + + auto& eventParts = context->getEventParts(withQED); + // loop over all composite collisions given from context + // (aka loop over all the interaction records) + // o2::InteractionTimeRecord firstorbit(o2::InteractionRecord(0, o2::raw::HBFUtils::Instance().orbitFirstSampled), 0.0); + for (int collID = 0; collID < timesview.size(); ++collID) { + o2::InteractionTimeRecord orbit(timesview[collID]); + // orbit += firstorbit + mDigitizer.setEventTime(orbit); + + // for each collision, loop over the constituents event and source IDs + // (background signal merging is basically taking place here) + for (const auto& part : eventParts[collID]) { + + // get the hits for this event and this source + mHits.clear(); + context->retrieveHits(mSimChains, o2::detectors::SimTraits::DETECTORBRANCHNAMES[mID][0].c_str(), part.sourceID, part.entryID, &mHits); + + if (mHits.size() > 0) { + mDigits.clear(); + if (mWithMCTruth) { + mLabels.clear(); + } + + LOG(debug) << "For collision " << collID << " eventID " << part.entryID << " found " << mHits.size() << " hits "; + mDigitizer.process(&mHits, part.entryID, part.sourceID); // call actual digitization procedure + } + } + } + if (mDigitizer.isContinuous()) { + LOG(debug) << "Number of digits before final flush: " << mDigits.size(); + mDigits.clear(); + if (mWithMCTruth) { + mLabels.clear(); + } + LOG(debug) << "Final flushing for continuous mode"; + mDigitizer.fillOutputContainer(); + LOG(debug) << "Number of digits after final flush: " << mDigits.size(); + } + + // here we have all digits and we can send them to consumer (aka snapshot it onto output) + LOG(debug) << "Digitization finished with " << mDigits.size() << " digits and " << mROFRecords.size() << " ROF records"; + pc.outputs().snapshot(Output{mOrigin, "DIGITS", 0}, mDigits); + pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, mROFRecords); + if (mWithMCTruth) { + auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); + mLabelsAccum.flatten_to(sharedlabels); + // free space of existing label containers + mLabels.clear_andfreememory(); + mLabelsAccum.clear_andfreememory(); + + // write dummy MC2ROF vector to keep writer/readers backward compatible + // NOTE: Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx also uses dummy MC2ROF + static std::vector dummyMC2ROF; + pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, dummyMC2ROF); + } + + timer.Stop(); + LOG(info) << "Digitization took " << timer.CpuTime() << "s"; + + // we should be only called once; tell DPL that this process is ready to exit + pc.services().get().readyToQuit(QuitRequest::Me); + + mFinished = true; + } + + private: + bool mDisableQED = false; + bool mWithMCTruth{true}; + bool mFinished{false}; + unsigned long mFirstOrbitTF = 0x0; + const o2::detectors::DetID mID{o2::detectors::DetID::TF3}; + const o2::header::DataOrigin mOrigin{o2::header::gDataOriginTF3}; + o2::iotof::Digitizer mDigitizer{}; + std::vector mDigits{}; + std::vector mROFRecords{}; + std::vector mHits{}; + std::vector* mHitsP{&mHits}; + o2::dataformats::MCTruthContainer mLabels{}; + o2::dataformats::MCTruthContainer mLabelsAccum{}; + std::vector mSimChains{}; + o2::parameters::GRPObject::ROMode mROMode = o2::parameters::GRPObject::PRESENT; // readout mode +}; + +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth) +{ + std::vector outputs; + outputs.emplace_back(detOrig, "DIGITS", o2::framework::Lifetime::Timeframe); + outputs.emplace_back(detOrig, "DIGITSROF", o2::framework::Lifetime::Timeframe); + if (mctruth) { + outputs.emplace_back(detOrig, "DIGITSMC2ROF", o2::framework::Lifetime::Timeframe); + outputs.emplace_back(detOrig, "DIGITSMCTR", o2::framework::Lifetime::Timeframe); + } + outputs.emplace_back(detOrig, "ROMode", 0, o2::framework::Lifetime::Timeframe); + return outputs; +} + +o2::framework::DataProcessorSpec getIOTOFDigitizerSpec(int channel, bool mctruth) +{ + std::vector inputs; + inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), o2::framework::Lifetime::Timeframe); + inputs.emplace_back("IOTOF_aptsresp", "TF3", "APTSRESP", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("IT3/Calib/APTSResponse")); + + const std::string detStr = o2::detectors::DetID::getName(o2::detectors::DetID::TF3); + return o2::framework::DataProcessorSpec{detStr + "Digitizer", + inputs, + makeOutChannels(o2::header::gDataOriginTF3, mctruth), + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(mctruth)}, + o2::framework::Options{ + {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}, + {"local-response-file", o2::framework::VariantType::String, "", {"use response file saved locally at this path/filename"}}}}; +} + +} // namespace o2::iotof diff --git a/Steer/DigitizerWorkflow/src/IOTOFDigitizerSpec.h b/Steer/DigitizerWorkflow/src/IOTOFDigitizerSpec.h new file mode 100644 index 0000000000000..cebc698e4ec41 --- /dev/null +++ b/Steer/DigitizerWorkflow/src/IOTOFDigitizerSpec.h @@ -0,0 +1,22 @@ +// 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_IOTOFDIGITIZER_H_ +#define STEER_DIGITIZERWORKFLOW_IOTOFDIGITIZER_H_ + +#include "Framework/DataProcessorSpec.h" + +namespace o2::iotof +{ +o2::framework::DataProcessorSpec getIOTOFDigitizerSpec(int channel, bool mctruth = true); +} // namespace o2::iotof + +#endif diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index 3b7bf3088a8f9..bcc9a4ecddc37 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -49,6 +49,10 @@ // for alice 3 TRK #include "TRKDigitizerSpec.h" #include "TRKWorkflow/DigitWriterSpec.h" + +// for alice 3 TF3 +#include "IOTOFDigitizerSpec.h" +#include "IOTOFWorkflow/DigitWriterSpec.h" #endif // for TOF @@ -665,6 +669,15 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // connect the ALICE 3 TRK digit writer specs.emplace_back(o2::trk::getTRKDigitWriterSpec(mctruth)); } + + // the ALICE 3 IOTOF part + if (isEnabled(o2::detectors::DetID::TF3)) { + detList.emplace_back(o2::detectors::DetID::TF3); + // connect the ALICE 3 IOTOF digitization + specs.emplace_back(o2::iotof::getIOTOFDigitizerSpec(fanoutsize++, mctruth)); + // connect the ALICE 3 IOTOF digit writer + specs.emplace_back(o2::iotof::getIOTOFDigitWriterSpec(mctruth)); + } #endif // the MFT part