From 6e7b6ee7c667c134db4392d4bb3d9f267c031042 Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Fri, 21 Feb 2025 19:48:30 +0100 Subject: [PATCH 1/3] [MCH] introduce digit modifier in filtering workflow The digit modifier allows to change the contents of the digits in the filtering step. It is introduced in order to correct some mapping issues in the CTFs already collected, but the interface is general, and in the future it might be used for any kind of digit manipulation, if needed. --- .../MUON/MCH/DigitFiltering/CMakeLists.txt | 4 ++- .../include/MCHDigitFiltering/DigitModifier.h | 28 +++++++++++++++ .../MCHDigitFiltering/DigitModifierParam.h | 35 ++++++++++++++++++ .../DigitFiltering/src/DigitFilteringSpec.cxx | 36 ++++++++++++++++--- .../MCH/DigitFiltering/src/DigitModifier.cxx | 33 +++++++++++++++++ .../DigitFiltering/src/DigitModifierParam.cxx | 15 ++++++++ .../src/MCHDigitFilteringLinkDef.h | 3 ++ 7 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifier.h create mode 100644 Detectors/MUON/MCH/DigitFiltering/include/MCHDigitFiltering/DigitModifierParam.h create mode 100644 Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx create mode 100644 Detectors/MUON/MCH/DigitFiltering/src/DigitModifierParam.cxx 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..f20d7c789fda1 --- /dev/null +++ b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx @@ -0,0 +1,33 @@ +// 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 o2::mch +{ +DigitModifier createDigitModifier(int runNumber, + bool updateST1, + bool updateST2) +{ + 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 From b89e443ec9b6362e0d450e02a037e95700a3fe8f Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Fri, 21 Feb 2025 19:52:24 +0100 Subject: [PATCH 2/3] [MCH] added pad remapping function for ST1 The remapping function corrects the pads mapping for seven DS boards on the edge of the bending cathodes, all corresponding to the motif type "1BG". --- .../MCH/DigitFiltering/src/DigitModifier.cxx | 187 +++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) diff --git a/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx index f20d7c789fda1..138536bb0426a 100644 --- a/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx +++ b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx @@ -21,13 +21,198 @@ #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); + }; +} +} // namespace + namespace o2::mch { DigitModifier createDigitModifier(int runNumber, bool updateST1, bool updateST2) { - return {}; + DigitModifier modifierST1 = updateST1 ? createST1MappingCorrector(runNumber) : DigitModifier{}; + DigitModifier modifierST2{}; + + 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 From 4d454756a9bebc012cc64c5af18b3b6a638aed33 Mon Sep 17 00:00:00 2001 From: aferrero2707 Date: Fri, 21 Feb 2025 19:53:30 +0100 Subject: [PATCH 3/3] [MCH] added pad remapping function for ST2 The remapping function corrects the pads mapping for five consecutive motif types on the bending side of ST2 quadrants (types "2Bv1" to "2Bv5"). --- .../MCH/DigitFiltering/src/DigitModifier.cxx | 109 +++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx index 138536bb0426a..0aa885e053dc9 100644 --- a/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx +++ b/Detectors/MUON/MCH/DigitFiltering/src/DigitModifier.cxx @@ -187,6 +187,113 @@ o2::mch::DigitModifier createST1MappingCorrector(int runNumber) 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 @@ -196,7 +303,7 @@ DigitModifier createDigitModifier(int runNumber, bool updateST2) { DigitModifier modifierST1 = updateST1 ? createST1MappingCorrector(runNumber) : DigitModifier{}; - DigitModifier modifierST2{}; + DigitModifier modifierST2 = updateST2 ? createST2MappingCorrector(runNumber) : DigitModifier{}; if (modifierST1 || modifierST2) { return [modifierST1, modifierST2](Digit& digit) {