diff --git a/Detectors/MUON/MCH/DigitFiltering/CMakeLists.txt b/Detectors/MUON/MCH/DigitFiltering/CMakeLists.txt index f6d7fbd03701d..14e920debd441 100644 --- a/Detectors/MUON/MCH/DigitFiltering/CMakeLists.txt +++ b/Detectors/MUON/MCH/DigitFiltering/CMakeLists.txt @@ -13,6 +13,8 @@ o2_add_library(MCHDigitFiltering SOURCES src/DigitFilter.cxx src/DigitFilterParam.cxx + src/DigitModifier.cxx + src/DigitModifierParam.cxx src/DigitFilteringSpec.cxx PUBLIC_LINK_LIBRARIES O2::Framework @@ -27,4 +29,4 @@ o2_add_executable( COMPONENT_NAME mch PUBLIC_LINK_LIBRARIES O2::MCHDigitFiltering) -o2_target_root_dictionary(MCHDigitFiltering HEADERS include/MCHDigitFiltering/DigitFilterParam.h) +o2_target_root_dictionary(MCHDigitFiltering HEADERS include/MCHDigitFiltering/DigitFilterParam.h include/MCHDigitFiltering/DigitModifierParam.h) diff --git a/Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifier.h b/Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifier.h new file mode 100644 index 0000000000000..0177ea134ab1d --- /dev/null +++ b/Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifier.h @@ -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. + +#ifndef O2_MCH_DIGITFILTERING_DIGITMODIFIER_H_ +#define O2_MCH_DIGITFILTERING_DIGITMODIFIER_H_ + +#include "DataFormatsMCH/Digit.h" +#include + +namespace o2::mch +{ +typedef std::function DigitModifier; + +DigitModifier createDigitModifier(int runNumber, + bool updateST1, + bool updateST2); + +} // namespace o2::mch + +#endif diff --git a/Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifierParam.h b/Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifierParam.h new file mode 100644 index 0000000000000..dc95396835f33 --- /dev/null +++ b/Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifierParam.h @@ -0,0 +1,35 @@ +// 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_MCH_DIGITFILTERING_DIGIT_MODIFIER_PARAM_H_ +#define O2_MCH_DIGITFILTERING_DIGIT_MODIFIER_PARAM_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2::mch +{ + +/** + * @class DigitModifierParam + * @brief Configurable parameters for the digit updating + */ +struct DigitModifierParam : public o2::conf::ConfigurableParamHelper { + + bool updateST1 = false; ///< whether or not to modify ST1 digits + bool updateST2 = false; ///< whether or not to modify ST2 digits + + O2ParamDef(DigitModifierParam, "MCHDigitModifier"); +}; + +} // namespace o2::mch + +#endif diff --git a/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx b/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx index fe40659bc9265..f43b04369bc6e 100644 --- a/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx +++ b/Detectors/MUON/MCH/DigitFiltering/src/DigitFilteringSpec.cxx @@ -23,6 +23,8 @@ #include "MCHStatus/StatusMap.h" #include "MCHDigitFiltering/DigitFilter.h" #include "MCHDigitFiltering/DigitFilterParam.h" +#include "MCHDigitFiltering/DigitModifier.h" +#include "MCHDigitFiltering/DigitModifierParam.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" #include @@ -48,6 +50,10 @@ class DigitFilteringTask mRejectBackground = DigitFilterParam::Instance().rejectBackground; mStatusMask = DigitFilterParam::Instance().statusMask; mTimeCalib = DigitFilterParam::Instance().timeOffset; + + mUpdateDigitsST1 = DigitModifierParam::Instance().updateST1; + mUpdateDigitsST2 = DigitModifierParam::Instance().updateST2; + auto stop = [this]() { LOG(info) << "digit filtering duration = " << std::chrono::duration(mElapsedTime).count() << " ms"; @@ -82,6 +88,11 @@ class DigitFilteringTask auto tStart = std::chrono::high_resolution_clock::now(); + const auto& tinfo = pc.services().get(); + if (tinfo.runNumber != 0) { + mRunNumber = tinfo.runNumber; + } + if (mSanityCheck) { LOGP(info, "performing sanity checks"); auto error = sanityCheck(iRofs, iDigits); @@ -101,8 +112,12 @@ class DigitFilteringTask auto oLabels = mUseMC ? &pc.outputs().make>(OutputRef{"labels"}) : nullptr; if (!abort) { - bool selectSignal = false; + mDigitModifier = createDigitModifier(mRunNumber, + mUpdateDigitsST1, + mUpdateDigitsST2); + + bool selectSignal = false; mIsGoodDigit = createDigitFilter(mMinADC, mRejectBackground, selectSignal, @@ -114,20 +129,29 @@ class DigitFilteringTask // the clustering resolution will suffer. // That's why we only apply the "reject background" filter, which // is a loose background cut that does not penalize the signal + int cursor{0}; for (const auto& irof : iRofs) { const auto digits = iDigits.subspan(irof.getFirstIdx(), irof.getNEntries()); // filter the digits from the current ROF for (auto i = 0; i < digits.size(); i++) { - const auto& d = digits[i]; - if (mIsGoodDigit(d)) { - oDigits.emplace_back(d); + auto digit = digits[i]; + + // modify the digit if needed + if (mDigitModifier) { + mDigitModifier(digit); + } + + // check the digit quality + if (mIsGoodDigit(digit)) { + oDigits.emplace_back(digit); if (iLabels) { oLabels->addElements(oLabels->getIndexedSize(), iLabels->getLabels(i + irof.getFirstIdx())); } } } + int nofGoodDigits = oDigits.size() - cursor; if (nofGoodDigits > 0) { // we create an ouput ROF only if at least one digit from @@ -160,6 +184,7 @@ class DigitFilteringTask } private: + int mRunNumber{0}; bool mRejectBackground{false}; bool mSanityCheck{false}; bool mUseMC{false}; @@ -167,7 +192,10 @@ class DigitFilteringTask int mMinADC{1}; int32_t mTimeCalib{0}; uint32_t mStatusMask{0}; + bool mUpdateDigitsST1{false}; + bool mUpdateDigitsST2{false}; DigitFilter mIsGoodDigit; + DigitModifier mDigitModifier; std::chrono::duration mElapsedTime{}; }; diff --git a/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx new file mode 100644 index 0000000000000..0aa885e053dc9 --- /dev/null +++ b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.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. + +#include "MCHDigitFiltering/DigitModifier.h" + +#include "DataFormatsMCH/Digit.h" +#include "MCHMappingInterface/Segmentation.h" +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +using PadRemappingTable = std::unordered_map; +using PadRemappingTableWithLimits = std::pair>; +using PadRemappingTablesForDE = std::vector; +using PadRemappingTables = std::unordered_map; + +// utility function that updates a digit with a given pad remapping table +bool updateDigitMapping(o2::mch::Digit& digit, const PadRemappingTables& padsRemapping) +{ + int deId = digit.getDetID(); + // check if the current DE is included in the pad remapping table + auto padsRemappingForDE = padsRemapping.find(deId); + if (padsRemappingForDE == padsRemapping.end()) { + return false; + } + + // find the remapping table that contains this padId, if existing + int padId = digit.getPadID(); + for (auto& padsRemappingForDS : padsRemappingForDE->second) { + if (padId < padsRemappingForDS.second.first || padId > padsRemappingForDS.second.second) { + continue; + } + + auto padIDRemapped = padsRemappingForDS.first.find(digit.getPadID()); + if (padIDRemapped == padsRemappingForDS.first.end()) { + continue; + } + + // update the digit + digit.setPadID(padIDRemapped->second); + return true; + } + return false; +} + +/** Initialization of the pad remapping table for Station 1 DEs + * See https://its.cern.ch/jira/browse/MCH-4 for detals + */ +void initST1PadsRemappingTable(PadRemappingTables& fullTable) +{ + std::array deToRemap{100, 101, 102, 103, 200, 201, 202, 203}; + std::array dsToRemap{1, 27, 53, 79, 105, 131, 157}; + + std::vector newToOld(64); + newToOld[0] = 55; + newToOld[1] = 1; + newToOld[2] = 11; + newToOld[3] = 48; + newToOld[4] = 4; + newToOld[5] = 52; + newToOld[6] = 12; + newToOld[7] = 61; + newToOld[8] = 59; + newToOld[9] = 9; + newToOld[10] = 10; + newToOld[11] = 17; + newToOld[12] = 5; + newToOld[13] = 36; + newToOld[14] = 57; + newToOld[15] = 13; + newToOld[16] = 21; + newToOld[17] = 23; + newToOld[18] = 34; + newToOld[19] = 58; + newToOld[20] = 20; + newToOld[21] = 62; + newToOld[22] = 43; + newToOld[23] = 24; + newToOld[24] = 38; + newToOld[25] = 49; + newToOld[26] = 26; + newToOld[27] = 47; + newToOld[28] = 50; + newToOld[29] = 41; + newToOld[30] = 31; + newToOld[31] = 53; + newToOld[32] = 32; + newToOld[33] = 15; + newToOld[34] = 33; + newToOld[35] = 42; + newToOld[36] = 3; + newToOld[37] = 18; + newToOld[38] = 37; + newToOld[39] = 40; + newToOld[40] = 30; + newToOld[41] = 39; + newToOld[42] = 46; + newToOld[43] = 22; + newToOld[44] = 35; + newToOld[45] = 45; + newToOld[46] = 0; + newToOld[47] = 25; + newToOld[48] = 51; + newToOld[49] = 27; + newToOld[50] = 28; + newToOld[51] = 44; + newToOld[52] = 6; + newToOld[53] = 29; + newToOld[54] = 2; + newToOld[55] = 56; + newToOld[56] = 19; + newToOld[57] = 60; + newToOld[58] = 54; + newToOld[59] = 16; + newToOld[60] = 8; + newToOld[61] = 14; + newToOld[62] = 7; + newToOld[63] = 63; + + for (auto deId : deToRemap) { + + // create an empty table, or reset the existing one + fullTable[deId] = PadRemappingTablesForDE(); + // get a reference to the table for the current DE + auto& tableForDE = fullTable[deId]; + + const o2::mch::mapping::Segmentation& segment = o2::mch::mapping::segmentation(deId); + for (auto dsId : dsToRemap) { + // add an empty table for the currend DS board + auto& tableForDSWithLimits = tableForDE.emplace_back(); + auto& tableForDS = tableForDSWithLimits.first; + + int padIdMin = std::numeric_limits::max(); + int padIdMax = -1; + for (int channel = 0; channel < 64; channel++) { + // get the pad ID associated to the channel in the new mapping + // this IS NOT the pad that originally fired + int padId = segment.findPadByFEE(dsId, channel); + // get the corresponding channel number in the old mapping + // this IS the electronic channel that originally fired + int channelInOldMapping = newToOld[channel]; + // get the pad ID associated to the fired channel in the new mapping + int padIdRemapped = segment.findPadByFEE(dsId, channelInOldMapping); + // update the pad remapping table + tableForDS[padId] = padIdRemapped; + + padIdMin = std::min(padIdMin, padId); + padIdMax = std::max(padIdMax, padId); + } + + tableForDSWithLimits.second.first = padIdMin; + tableForDSWithLimits.second.second = padIdMax; + } + } +} + +o2::mch::DigitModifier createST1MappingCorrector(int runNumber) +{ + static PadRemappingTables padsRemapping; + + constexpr int lastRunToBeFixed = 560402; + // ST2 mapping needs to be corrected only for data collected up to the end of 2024 Pb-Pb + if (runNumber > lastRunToBeFixed) { + // do not modify digits collected after 2024 Pb-Pb + return {}; + } + + if (padsRemapping.empty()) { + initST1PadsRemappingTable(padsRemapping); + } + + return [](o2::mch::Digit& digit) { + updateDigitMapping(digit, padsRemapping); + }; +} + +/** Initialization of the pad remapping table for Station 2 DEs + * See https://its.cern.ch/jira/browse/MCH-5 for details + */ +void initST2PadsRemappingTable(PadRemappingTables& fullTable) +{ + // Remapping of ST2 DS boards near the rounded part + std::array deToRemap{300, 301, 302, 303, 400, 401, 402, 403}; + std::array dsToRemap{99, 100, 101, 102, 103}; + + for (auto deId : deToRemap) { + + // create an empty table, or reset the existing one + fullTable[deId] = PadRemappingTablesForDE(); + // get a reference to the table for the current DE + auto& tableForDE = fullTable[deId]; + + const o2::mch::mapping::Segmentation& segment = o2::mch::mapping::segmentation(deId); + for (auto dsId : dsToRemap) { + + auto& tableForDSWithLimits = tableForDE.emplace_back(); + auto& tableForDS = tableForDSWithLimits.first; + + // double loop on DS channels + // 1. find the minimum pad index of the DS board + int padIdMin = -1; + int channelForPadIdMin = -1; + for (int channel = 0; channel < 64; channel++) { + auto padId = segment.findPadByFEE(dsId, int(channel)); + if (padId < 0) { + // this should never occur in this specific case, as all channels of this group of boards + // is connected to pads, hence we rise an exception + throw std::out_of_range(fmt::format("Unknown padId for DE{} DS{} channel {}", deId, dsId, channel)); + } + if (padIdMin < 0 || padId < padIdMin) { + padIdMin = padId; + channelForPadIdMin = channel; + } + } + + int padIdMax = -1; + // 2. build the re-mapping table + for (int channel = 0; channel < 64; channel++) { + auto padId = segment.findPadByFEE(dsId, int(channel)); + if (padId < padIdMin) { + // something is wrong here... + continue; + } + + // update maximum padId value + padIdMax = std::max(padIdMax, padId); + + int padIdInDS = padId - padIdMin; + int padColumn = padIdInDS / 16; + int padRow = padIdInDS % 16; + + int padIdRemapped = -1; + + switch (padColumn) { + case 0: + // shift right by 3 columns + padIdRemapped = padId + 16 * 3; + break; + case 1: + // shift right by 1 column + padIdRemapped = padId + 16; + break; + case 2: + // shift left by 1 column + padIdRemapped = padId - 16; + break; + case 3: + // shift left by 3 columns + padIdRemapped = padId - 16 * 3; + break; + } + + // padsRemapping[deId][padId] = padIdRemapped; + tableForDS[padId] = padIdRemapped; + } + + tableForDSWithLimits.second.first = padIdMin; + tableForDSWithLimits.second.second = padIdMax; + } + } +} + +o2::mch::DigitModifier createST2MappingCorrector(int runNumber) +{ + // static std::unordered_map> padsRemapping; + static PadRemappingTables padsRemapping; + + constexpr int lastRunToBeFixed = 560402; + // ST2 mapping needs to be corrected only for data collected up to the end of 2024 Pb-Pb + if (runNumber > lastRunToBeFixed) { + // do not modify digits collected after 2024 Pb-Pb + return {}; + } + + if (padsRemapping.empty()) { + initST2PadsRemappingTable(padsRemapping); + } + + return [](o2::mch::Digit& digit) { + updateDigitMapping(digit, padsRemapping); + }; +} +} // namespace + +namespace o2::mch +{ +DigitModifier createDigitModifier(int runNumber, + bool updateST1, + bool updateST2) +{ + DigitModifier modifierST1 = updateST1 ? createST1MappingCorrector(runNumber) : DigitModifier{}; + DigitModifier modifierST2 = updateST2 ? createST2MappingCorrector(runNumber) : DigitModifier{}; + + if (modifierST1 || modifierST2) { + return [modifierST1, modifierST2](Digit& digit) { + // the ST1/ST2 modifiers are mutually exclusive, depending on the DeID associated to the digit + auto detID = digit.getDetID(); + if (modifierST1 && detID >= 100 && detID < 300) { + modifierST1(digit); + } + if (modifierST2 && detID >= 300 && detID < 500) { + modifierST2(digit); + } + }; + } else { + // return an empty function if none of the modifiers is set + return {}; + } +} + +} // namespace o2::mch diff --git a/Detectors/MUON/MCH/DigitFiltering/src/DigitModifierParam.cxx b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifierParam.cxx new file mode 100644 index 0000000000000..c10a8a87d6bd7 --- /dev/null +++ b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifierParam.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "MCHDigitFiltering/DigitModifierParam.h" +#include "CommonUtils/ConfigurableParam.h" + +O2ParamImpl(o2::mch::DigitModifierParam) diff --git a/Detectors/MUON/MCH/DigitFiltering/src/MCHDigitFilteringLinkDef.h b/Detectors/MUON/MCH/DigitFiltering/src/MCHDigitFilteringLinkDef.h index 1182d37654c37..c4de20393fbe0 100644 --- a/Detectors/MUON/MCH/DigitFiltering/src/MCHDigitFilteringLinkDef.h +++ b/Detectors/MUON/MCH/DigitFiltering/src/MCHDigitFilteringLinkDef.h @@ -18,4 +18,7 @@ #pragma link C++ class o2::mch::DigitFilterParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::mch::DigitFilterParam> + ; +#pragma link C++ class o2::mch::DigitModifierParam + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::mch::DigitModifierParam> + ; + #endif