|
| 1 | +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. |
| 2 | +// See https://alice-o2.web.cern.ch/copyright for details. |
| 3 | +// All rights not expressly granted are reserved. |
| 4 | +// |
| 5 | +// This software is distributed under the terms of the GNU General |
| 6 | +// Public License v3 (GPL Version 3), copied verbatim in the file "COPYING". |
| 7 | +// |
| 8 | +// In applying this license CERN does not waive the privileges and immunities |
| 9 | +// granted to it by virtue of its status as an Intergovernmental Organization |
| 10 | +// or submit itself to any jurisdiction. |
| 11 | + |
| 12 | +/// \file AgingLaserPostProcTask.h |
| 13 | +/// \author Andreas Molander <andreas.molander@cern.ch>, Jakub Muszyński <jakub.milosz.muszynski@cern.ch> |
| 14 | + |
| 15 | +#include "FT0/AgingLaserPostProcTask.h" |
| 16 | + |
| 17 | +#include "Common/Utils.h" |
| 18 | +#include "FITCommon/HelperHist.h" |
| 19 | +#include "QualityControl/DatabaseInterface.h" |
| 20 | +#include "QualityControl/QcInfoLogger.h" |
| 21 | + |
| 22 | +#include <TH2.h> |
| 23 | +#include <TF1.h> |
| 24 | +#include <TMath.h> |
| 25 | + |
| 26 | +#include <algorithm> |
| 27 | +#include <numeric> |
| 28 | +#include <string> |
| 29 | + |
| 30 | +using namespace o2::quality_control::postprocessing; |
| 31 | + |
| 32 | +namespace o2::quality_control_modules::ft0 |
| 33 | +{ |
| 34 | + |
| 35 | +AgingLaserPostProcTask::~AgingLaserPostProcTask() = default; |
| 36 | + |
| 37 | +void AgingLaserPostProcTask::initialize(Trigger, framework::ServiceRegistryRef) |
| 38 | +{ |
| 39 | + ILOG(Info) << "initialize AgingLaserPostProcTask" << ENDM; |
| 40 | + |
| 41 | + const std::string detChs = o2::quality_control_modules::common::getFromConfig<std::string>( |
| 42 | + mCustomParameters, "detectorChannelIDs", ""); |
| 43 | + if (!detChs.empty()) { |
| 44 | + mDetectorChIDs = fit::helper::parseParameters<uint8_t>(detChs, ","); |
| 45 | + } else { // default: all detector channels 0–207 |
| 46 | + mDetectorChIDs.resize(208); |
| 47 | + std::iota(mDetectorChIDs.begin(), mDetectorChIDs.end(), 0); |
| 48 | + } |
| 49 | + |
| 50 | + const std::string refChs = o2::quality_control_modules::common::getFromConfig<std::string>( |
| 51 | + mCustomParameters, "referenceChannelIDs", ""); |
| 52 | + if (!refChs.empty()) { |
| 53 | + mReferenceChIDs = fit::helper::parseParameters<uint8_t>(refChs, ","); |
| 54 | + } else { // default: 208–210 |
| 55 | + for (uint8_t ch = 208; ch < 211; ++ch) |
| 56 | + mReferenceChIDs.push_back(ch); |
| 57 | + } |
| 58 | + |
| 59 | + mADCSearchMin = o2::quality_control_modules::common::getFromConfig<double>( |
| 60 | + mCustomParameters, "adcSearchMin", mADCSearchMin); |
| 61 | + mADCSearchMax = o2::quality_control_modules::common::getFromConfig<double>( |
| 62 | + mCustomParameters, "adcSearchMax", mADCSearchMax); |
| 63 | + mFracWindowA = o2::quality_control_modules::common::getFromConfig<double>( |
| 64 | + mCustomParameters, "fracWindowA", mFracWindowA); |
| 65 | + mFracWindowB = o2::quality_control_modules::common::getFromConfig<double>( |
| 66 | + mCustomParameters, "fracWindowB", mFracWindowB); |
| 67 | + |
| 68 | + ILOG(Info) << "detector channels : " << detChs << ENDM; |
| 69 | + ILOG(Info) << "reference channels : " << refChs << ENDM; |
| 70 | + ILOG(Info) << "ADC search window : [" << mADCSearchMin << ", " << mADCSearchMax << "]" << ENDM; |
| 71 | + ILOG(Info) << "fractional window : a=" << mFracWindowA << " b=" << mFracWindowB << ENDM; |
| 72 | + |
| 73 | + mAmpVsChNormWeightedMeanA = fit::helper::registerHist<TH1F>( |
| 74 | + getObjectsManager(), |
| 75 | + quality_control::core::PublicationPolicy::ThroughStop, |
| 76 | + "", "AmpPerChannelNormWeightedMeanA", "AmpPerChannelNormWeightedMeanA", |
| 77 | + 96, 0, 96); |
| 78 | + |
| 79 | + mAmpVsChNormWeightedMeanC = fit::helper::registerHist<TH1F>( |
| 80 | + getObjectsManager(), |
| 81 | + quality_control::core::PublicationPolicy::ThroughStop, |
| 82 | + "", "AmpPerChannelNormWeightedMeanC", "AmpPerChannelNormWeightedMeanC", |
| 83 | + 112, 96, 208); |
| 84 | +} |
| 85 | + |
| 86 | +void AgingLaserPostProcTask::update(Trigger t, framework::ServiceRegistryRef srv) |
| 87 | +{ |
| 88 | + mAmpVsChNormWeightedMeanA->Reset(); |
| 89 | + mAmpVsChNormWeightedMeanC->Reset(); |
| 90 | + |
| 91 | + /* ---- fetch source histogram ---- */ |
| 92 | + auto& qcdb = srv.get<quality_control::repository::DatabaseInterface>(); |
| 93 | + auto moIn = qcdb.retrieveMO("FT0/MO/AgingLaser", "AmpPerChannel", t.timestamp, t.activity); |
| 94 | + TH2* h2amp = moIn ? dynamic_cast<TH2*>(moIn->getObject()) : nullptr; |
| 95 | + |
| 96 | + if (!h2amp) { |
| 97 | + ILOG(Error) << "Could not retrieve FT0/MO/AgingLaser/AmpPerChannel for timestamp " |
| 98 | + << t.timestamp << ENDM; |
| 99 | + return; |
| 100 | + } |
| 101 | + |
| 102 | + /* ---- 1. Reference-channel Gaussian fits ---- */ |
| 103 | + std::vector<double> refMus; |
| 104 | + |
| 105 | + for (auto chId : mReferenceChIDs) { |
| 106 | + auto h1 = std::unique_ptr<TH1>(h2amp->ProjectionY( |
| 107 | + Form("ref_%d", chId), chId + 1, chId + 1)); |
| 108 | + |
| 109 | + const int binLow = h1->FindBin(mADCSearchMin); |
| 110 | + const int binHigh = h1->FindBin(mADCSearchMax); |
| 111 | + |
| 112 | + int binMax = binLow; |
| 113 | + for (int b = binLow + 1; b <= binHigh; ++b) { |
| 114 | + if (h1->GetBinContent(b) > h1->GetBinContent(binMax)) { |
| 115 | + binMax = b; |
| 116 | + } |
| 117 | + } |
| 118 | + const double xMax = h1->GetBinCenter(binMax); |
| 119 | + const double winLo = TMath::Max(0., (1. - mFracWindowA) * xMax); |
| 120 | + const double winHi = (1. + mFracWindowB) * xMax; |
| 121 | + |
| 122 | + TF1 g("g", "gaus", winLo, winHi); |
| 123 | + if (h1->Fit(&g, "QNRS") == 0) { // 0 = fit OK |
| 124 | + refMus.push_back(g.GetParameter(1)); |
| 125 | + } else { |
| 126 | + ILOG(Warning) << "Gaussian fit failed for reference channel " << int(chId) << ENDM; |
| 127 | + } |
| 128 | + } |
| 129 | + |
| 130 | + const unsigned nRef = refMus.size(); |
| 131 | + if (nRef == 0) { |
| 132 | + ILOG(Error) << "No successful reference fits – cannot normalise." << ENDM; |
| 133 | + return; |
| 134 | + } |
| 135 | + const double norm = std::accumulate(refMus.begin(), refMus.end(), 0.) / nRef; |
| 136 | + |
| 137 | + /* ---- 2. Loop over ALL channels ---- */ |
| 138 | + auto processChannel = [&](uint8_t chId) { |
| 139 | + auto h1 = std::unique_ptr<TH1>(h2amp->ProjectionY( |
| 140 | + Form("proj_%d", chId), chId + 1, chId + 1)); |
| 141 | + |
| 142 | + // global maximum |
| 143 | + const int binMax = h1->GetMaximumBin(); |
| 144 | + const double xMax = h1->GetBinCenter(binMax); |
| 145 | + const double winLo = TMath::Max(0., (1. - mFracWindowA) * xMax); |
| 146 | + const double winHi = (1. + mFracWindowB) * xMax; |
| 147 | + |
| 148 | + double num = 0., den = 0.; |
| 149 | + const int binLo = h1->FindBin(winLo); |
| 150 | + const int binHi = h1->FindBin(winHi); |
| 151 | + for (int b = binLo; b <= binHi; ++b) { |
| 152 | + const double w = h1->GetBinContent(b); |
| 153 | + const double x = h1->GetBinCenter(b); |
| 154 | + num += w * x; |
| 155 | + den += w; |
| 156 | + } |
| 157 | + |
| 158 | + const double wMean = (den > 0.) ? num / den : 0.; |
| 159 | + const double val = (norm > 0.) ? wMean / norm : 0.; |
| 160 | + if (chId < 96) { |
| 161 | + mAmpVsChNormWeightedMeanA->SetBinContent(chId + 1, val); |
| 162 | + } else if (chId >= 96 && chId <= 208) { |
| 163 | + mAmpVsChNormWeightedMeanC->SetBinContent(chId - 95, val); |
| 164 | + } |
| 165 | + }; |
| 166 | + |
| 167 | + for (uint8_t ch = 0; ch < sNCHANNELS_PM; ++ch) |
| 168 | + processChannel(ch); |
| 169 | + |
| 170 | + ILOG(Info) << "update done – " << nRef << " reference fits, norm=" << norm << ENDM; |
| 171 | +} |
| 172 | + |
| 173 | +void AgingLaserPostProcTask::finalize(Trigger, framework::ServiceRegistryRef) |
| 174 | +{ |
| 175 | + ILOG(Info) << "finalize AgingLaserPostProcTask" << ENDM; |
| 176 | +} |
| 177 | + |
| 178 | +} // namespace o2::quality_control_modules::ft0 |
0 commit comments