Skip to content

Commit 9eb6de9

Browse files
mvishiu11andreasmolander
authored andcommitted
feat: add aging laser post proc task
1 parent 4b6d7c9 commit 9eb6de9

4 files changed

Lines changed: 240 additions & 0 deletions

File tree

Modules/FIT/FT0/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
add_library(O2QcFT0)
44

55
target_sources(O2QcFT0 PRIVATE src/AgingLaserTask.cxx
6+
src/AgingLaserPostProcTask.cxx
67
src/DigitQcTask.cxx
78
src/GenericCheck.cxx
89
src/CFDEffCheck.cxx
@@ -34,6 +35,7 @@ install(TARGETS O2QcFT0
3435

3536
add_root_dictionary(O2QcFT0
3637
HEADERS include/FT0/AgingLaserTask.h
38+
include/FT0/AgingLaserPostProcTask.h
3739
include/FT0/DigitQcTask.h
3840
include/FT0/PostProcTask.h
3941
include/FT0/CFDEffCheck.h
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
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+
/// \brief Post-processing task that derives a per-channel
15+
/// weighted-mean amplitude, normalised to reference channels.
16+
17+
#ifndef QC_MODULE_FT0_AGINGLASERPOSTPROC_H
18+
#define QC_MODULE_FT0_AGINGLASERPOSTPROC_H
19+
20+
#include "QualityControl/PostProcessingInterface.h"
21+
#include <FT0Base/Constants.h>
22+
23+
#include <TH1F.h>
24+
#include <memory>
25+
#include <vector>
26+
27+
namespace o2::quality_control_modules::ft0
28+
{
29+
30+
/// \brief Post-processing task that derives a per-channel
31+
/// weighted-mean amplitude, normalised to reference channels.
32+
class AgingLaserPostProcTask final : public quality_control::postprocessing::PostProcessingInterface
33+
{
34+
public:
35+
AgingLaserPostProcTask() = default;
36+
~AgingLaserPostProcTask() override;
37+
38+
void initialize(quality_control::postprocessing::Trigger, framework::ServiceRegistryRef) override;
39+
void update(quality_control::postprocessing::Trigger, framework::ServiceRegistryRef) override;
40+
void finalize(quality_control::postprocessing::Trigger, framework::ServiceRegistryRef) override;
41+
42+
private:
43+
static constexpr std::size_t sNCHANNELS_PM = o2::ft0::Constants::sNCHANNELS_PM;
44+
45+
std::vector<uint8_t> mDetectorChIDs; ///< Detector (target) channels
46+
std::vector<uint8_t> mReferenceChIDs; ///< Reference channels
47+
48+
double mADCSearchMin = 150.; ///< lower edge of peak-search window (ADC) for ref channels
49+
double mADCSearchMax = 600.; ///< upper edge of peak-search window (ADC) for ref channels
50+
double mFracWindowA = 0.25; ///< low fractional window parameter a
51+
double mFracWindowB = 0.25; ///< high fractional window parameter b
52+
53+
std::unique_ptr<TH1F> mAmpVsChNormWeightedMeanA;
54+
std::unique_ptr<TH1F> mAmpVsChNormWeightedMeanC;
55+
};
56+
57+
} // namespace o2::quality_control_modules::ft0
58+
59+
#endif // QC_MODULE_FT0_AGINGLASERPOSTPROC_H

Modules/FIT/FT0/include/FT0/LinkDef.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@
1414
#pragma link C++ class o2::quality_control_modules::ft0::RecPointsQcTask + ;
1515

1616
#pragma link C++ class o2::quality_control_modules::ft0::AgingLaserTask + ;
17+
#pragma link C++ class o2::quality_control_modules::ft0::AgingLaserPostProcTask + ;
1718

1819
#endif
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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

Comments
 (0)