BmnRoot
Loading...
Searching...
No Matches
BmnHodoRaw2Digit.cxx
Go to the documentation of this file.
1#include "BmnHodoRaw2Digit.h"
2
3#include "BmnADCDigit.h"
4#include "BmnSyncDigit.h"
5#include "BmnTDCDigit.h"
6#include "BmnTQDCADCDigit.h"
7#include "BmnTrigRaw2Digit.h"
8#include "FairLogger.h"
9#include "RLDeconvolutor.h"
10#include "TMath.h"
11#include "TSystem.h"
12
13#include <boost/algorithm/string/classification.hpp>
14#include <boost/algorithm/string/split.hpp>
15#include <boost/lexical_cast.hpp>
16#include <boost/tokenizer.hpp>
17#include <nlohmann/json.hpp>
18
20{
21 LOG(info) << Form("%s", GetName().Data());
22}
23
25 : WfmProcessor()
26 , fSaturation(std::numeric_limits<int16_t>::max())
27 , fDoRLdeconvolve(false)
28 , fRLthresold(0)
29 , fR2converge(0.01)
30 , fMaxIters(10)
31 , fMaxPeaks(2)
32 , fworkTime_real(0.0)
33 , fworkTime_cpu(0.0)
34{}
35
37 int run,
38 const std::string& mappingFile,
39 const std::string& calibrationFile)
40 : WfmProcessor()
41 , fSaturation(std::numeric_limits<int16_t>::max())
42 , fDoRLdeconvolve(false)
43 , fRLthresold(0)
44 , fR2converge(0.01)
45 , fMaxIters(10)
46 , fMaxPeaks(2)
47 , fworkTime_real(0.0)
48 , fworkTime_cpu(0.0)
49{
50 LOG(info) << Form("%s : Config file: %s", GetName().Data(), mappingFile.c_str());
51 LOG(info) << Form("%s : Calibration file: %s", GetName().Data(), calibrationFile.c_str());
52 ParseConfig(mappingFile);
53 ParseCalibration(calibrationFile);
54}
55
56void BmnHodoRaw2Digit::ParseConfig(const std::string& file)
57{
58 std::string fullPath = std::string(getenv("VMCWORKDIR")) + "/input/" + file;
59 std::ifstream config_file(fullPath);
60 if (!config_file.is_open()) {
61 LOG(error) << GetName() << "::ParseConfig: Cannot open config file: " << fullPath;
62 return;
63 }
64
66 try {
67 config_file >> j;
68 } catch (const std::exception& e) {
69 LOG(error) << GetName() << "::ParseConfig: Failed to parse JSON: " << e.what();
70 return;
71 }
72
73 LOG(debug) << GetName() << "::ParseConfig. Version: " << j.value("version", "unknown");
74 LOG(debug) << GetName() << "::ParseConfig. Comment: " << j.value("comment", "none");
75
76 fuoChannelMap.clear();
77 for (const auto& entry : j["mapping"]) {
78 std::string serial = entry["serial"];
79 int channel = entry["channel"];
80 int strip = entry["strip"];
81 int side = entry["side"];
82 int gain = entry["gain"];
83
84 auto key = std::make_pair(std::stoul(serial, nullptr, 16), channel);
85 fuoChannelMap[key] = BmnHodoAddress::GetAddress(strip, side, gain);
86 }
87}
88
89void BmnHodoRaw2Digit::ParseCalibration(const std::string& file)
90{
91 std::string fullPath = std::string(getenv("VMCWORKDIR")) + "/parameters/hodo/" + file;
92 std::ifstream config_file(fullPath);
93 if (!config_file.is_open()) {
94 LOG(error) << GetName() << "::ParseCalibration: Cannot open config file: " << fullPath;
95 return;
96 }
97
99 try {
100 config_file >> j;
101 } catch (const std::exception& e) {
102 LOG(error) << GetName() << "::ParseCalibration: Failed to parse JSON: " << e.what();
103 return;
104 }
105
106 LOG(debug) << GetName() << "::ParseCalibration. Version: " << j.value("version", "unknown");
107 LOG(debug) << GetName() << "::ParseCalibration. Comment: " << j.value("comment", "none");
108
109 const auto& par = j.at("parameters");
110
111 fdigiPars.isWriteWfm = par.value("isWriteWfm", false);
112 fdigiPars.gateBegin = par.at("gateBegin");
113 fdigiPars.gateEnd = par.at("gateEnd");
114 fdigiPars.threshold = par.at("threshold");
115 fSaturation = par.at("saturation");
116 fdigiPars.doInvert = par.value("doInvert", false);
117 bool applyINL = par.value("applyINLcorr", true);
118 if (applyINL)
120
121 fuoCalibMap.clear();
122 for (const auto& ch : j.at("calibration")) {
123 int strip = ch.at("strip");
124 int side = ch.at("side");
125 int gain = ch.at("gain");
126 float calib = ch.at("calib");
127 float integ = ch.at("calibIntegral");
128
129 fuoCalibMap[BmnHodoAddress::GetAddress(strip, side, gain)] = std::make_pair(calib, integ);
130 }
131
132 if (j.contains("deconvParameters")) {
133 const auto& decpar = j.at("deconvParameters");
134 fEthalon.clear();
135 fDoRLdeconvolve = decpar.value("isdec", false);
136 fRLthresold = decpar.value("threshold", 0);
137 fR2converge = decpar.value("R2converge", 0.f);
138 fMaxIters = decpar.value("iterations", 0);
139 fMaxPeaks = decpar.value("peaks", 0);
140 if (decpar.contains("ethalon") && decpar["ethalon"].is_array()) {
141 auto tmp = decpar["ethalon"].get<std::vector<float>>();
142 fEthalon.insert(fEthalon.end(), tmp.begin(), tmp.end());
143 }
144 }
145
146 if (j.contains("fitParameters")) {
147 const auto& fitpar = j.at("fitParameters");
148 fdigiPars.isfit = fitpar.value("isfit", false);
149 fdigiPars.harmonics.clear();
150 for (const auto& pair : fitpar.at("harmonics")) {
151 float re = pair.at(0).get<float>();
152 float im = pair.at(1).get<float>();
153 fdigiPars.harmonics.emplace_back(re, im);
154 }
155
156 if (fdigiPars.isfit) {
157 int model_order = fdigiPars.harmonics.size() + 1;
159 auto maxElement = std::max_element(
160 fdigiPars.harmonics.begin(), fdigiPars.harmonics.end(),
161 [](const std::complex<float>& a, const std::complex<float>& b) { return std::abs(a) < std::abs(b); });
162 if (maxElement != fdigiPars.harmonics.end()) {
163 // set signal length as 5 * harmonic length
164 int maxElementLength = floor(5. / (-log(real(*maxElement))));
165 if (maxElementLength < fSignalLength)
166 fSignalLength = maxElementLength;
167 }
168
169 fAZik = new std::complex<float>*[model_order];
170 for (int i = 0; i < model_order; i++) {
171 fAZik[i] = new std::complex<float>[model_order];
172 for (int k = 0; k < model_order; k++)
173 fAZik[i][k] = {0., 0.};
174 }
180 }
181 }
182}
183
185{
186 std::string path = std::string(getenv("VMCWORKDIR")) + "/parameters/hodo/INLcorrections/";
187 LOG(info) << Form("%s : Loading INL corrections from directory: %s", GetName().Data(), path.c_str());
188
189 boost::property_tree::ptree pt;
190 std::string header = "TQDC16VS";
191 std::string iniheader = header + "_E";
192 std::string postfix = "inl_corr";
193 fINLcorrMap.clear();
194
195 for (auto& it : fuoChannelMap) {
196 auto boardSerial = (unsigned int)it.first.first;
197 std::string corrFileName = Form("%s%s-%04X-%04X.ini", path.c_str(), header.c_str(),
198 ((boardSerial & 0xffff0000) >> 16), (boardSerial & 0xffff));
199 boost::property_tree::ini_parser::read_ini(corrFileName.c_str(), pt);
200 std::string thisHeader = Form("%s-%08x-%s", iniheader.c_str(), boardSerial, postfix.c_str());
201 for (UInt_t chan_iter = 0; chan_iter < TQDC16_CHANNEL_COUNT; chan_iter++) {
202 auto correction_string = pt.get<std::string>(Form("%s.%d", thisHeader.c_str(), chan_iter));
203 istringstream ss(correction_string);
204
205 std::string token;
206 std::vector<float> corrV;
207 while (std::getline(ss, token, ','))
208 corrV.push_back(stof(token));
209 assert(corrV.size() == 1024);
210
211 auto key = std::make_pair(boardSerial, chan_iter);
212 fINLcorrMap[key] = corrV;
213 } // channels loop
214 } // serials loop
215}
216
217std::optional<uint32_t> BmnHodoRaw2Digit::GetAddressFromBoard(std::pair<size_t, size_t> key)
218{
219 try {
220 return fuoChannelMap.at(key);
221 } catch (const std::out_of_range& e) {
222 LOG(debug) << GetName() << ":: GetAddressFromBoard. Board serial " << std::hex << key.first << std::dec
223 << " channel " << key.second << " is not connected. Skipping it";
224 return std::nullopt;
225 }
226}
227
228uint32_t BmnHodoRaw2Digit::correctINL(uint32_t time, std::pair<size_t, size_t> key)
229{
230 try {
231 return time + fINLcorrMap.at(key).at(time & 1023);
232 } catch (const std::out_of_range& e) {
233 LOG(warning) << GetName() << "::correctINL. Board serial " << std::hex << key.first << std::dec << " channel "
234 << key.second << " is not connected. Leave without INL correction";
235 return time;
236 }
237}
238
239std::optional<std::pair<float, float>> BmnHodoRaw2Digit::GetCalibPairFromAddress(uint32_t address)
240{
241 try {
242 return fuoCalibMap.at(address);
243 } catch (const std::out_of_range& e) {
244 LOG(debug) << GetName() << ":: GetCalibPairFromAddress " << std::bitset<32>(address)
245 << " is marked badly in calibration file.";
246 return std::nullopt;
247 }
248}
249
250void BmnHodoRaw2Digit::fillEvent(TClonesArray* tdc, TClonesArray* adc, TClonesArray* Hododigit)
251{
252
253 LOG(debug) << GetName() << "::fillEvent";
254 TStopwatch timer;
255 timer.Start();
256
257 for (int iAdc = 0; iAdc < adc->GetEntriesFast(); iAdc++) {
258 BmnTQDCADCDigit* adcDig = (BmnTQDCADCDigit*)adc->At(iAdc);
259 // Double_t adcTimestamp = adcDig->GetAdcTimestamp() * ADC_CLOCK_TQDC16VS;
260 // Double_t trgTimestamp = adcDig->GetTrigTimestamp() * ADC_CLOCK_TQDC16VS;
261 auto key = std::make_pair(adcDig->GetSerial(), adcDig->GetChannel());
262 auto catch_address = GetAddressFromBoard(key);
263 if (!catch_address.has_value())
264 continue; // Not connected lines
265 auto address = catch_address.value();
266
267 auto catch_calib = GetCalibPairFromAddress(address);
268 if (!catch_calib.has_value())
269 continue; // Strip side gain is not involved in the analysis
270 auto calib_pair = catch_calib.value();
271
272 double time = -1.0;
273 for (int iTdc = 0; iTdc < tdc->GetEntriesFast(); iTdc++) {
274 BmnTDCDigit* tdcDig = (BmnTDCDigit*)tdc->At(iTdc);
275 if (tdcDig->GetSerial() != adcDig->GetSerial() || tdcDig->GetSlot() != adcDig->GetSlot())
276 continue;
277 if (tdcDig->GetChannel() != adcDig->GetChannel())
278 continue;
279 time = correctINL(tdcDig->GetValue(), key);
280 time *= TDC_CLOCK / TDC_BIN_COUNT;
281 }
282
283 std::vector<float> wfm(adcDig->GetShortValue(), adcDig->GetShortValue() + adcDig->GetNSamples());
284 std::vector<BmnHodoDigi> digi_vector;
285 BmnHodoDigi ThisDigi; // whole wfm as one digi
286 ThisDigi.reset();
287 ThisDigi.SetAddress(address);
288 ProcessWfm(wfm, &ThisDigi); // invert, remove ZL, precalc as only one signal per waveform
289 ThisDigi.SetSignal((float)ThisDigi.fAmpl * calib_pair.first);
290 if (ThisDigi.GetSignal() < fdigiPars.threshold)
291 continue;
292 ThisDigi.fIntegral = (int)std::accumulate(wfm.begin(), wfm.end(), 0.0); // common for all recovered signals
293 ThisDigi.fSignalIntegral = (float)ThisDigi.fIntegral * calib_pair.second;
294 if (ThisDigi.fSignalIntegral < fdigiPars.threshold)
295 continue;
296 ThisDigi.SetTime(time + ThisDigi.fTimeMax * ADC_CLOCK_TQDC16VS);
297 if (ThisDigi.fAmpl + ThisDigi.fZL >= fSaturation)
298 ThisDigi.SetIsSaturated(true);
299 ThisDigi.fCrosstalk = *std::min_element(wfm.begin() + fdigiPars.gateBegin, wfm.begin() + fdigiPars.gateEnd + 1);
300 digi_vector.push_back(ThisDigi);
301
302 if (fDoRLdeconvolve && ThisDigi.fSignalIntegral > fRLthresold) {
303 digi_vector.clear();
304
305 LOG(debug4) << GetName() << "::ProcessWfm RLDeconvolutor";
306 RLDeconvolutor deconvolution(wfm, fEthalon, fR2converge, fMaxIters);
307 /*bool converged = */ deconvolution.runDeconvolution();
308 const auto& decomposition = deconvolution.getSignalDecomposition();
309 const auto& positions = deconvolution.getSignalInfo();
310 assert(decomposition.size() == positions.size());
311 auto fitR2 = deconvolution.getR2().back(); // common for all recovered signals
312
313 // Create a vector of indices
314 std::vector<size_t> indices(positions.size());
315 std::iota(indices.begin(), indices.end(), 0);
316
317 // Sort the indices based on the corresponding amplitudes and extract the top fMaxPeaks indices
318 std::sort(indices.begin(), indices.end(),
319 [&positions](size_t a, size_t b) { return positions[a].second > positions[b].second; });
320 std::vector<size_t> topIndices = (fMaxPeaks <= indices.size())
321 ? std::vector<size_t>(indices.begin(), indices.begin() + fMaxPeaks)
322 : indices;
323
324 // Apply calibration
325 LOG(debug4) << GetName() << "::ProcessWfm Selection";
326 for (auto it : topIndices) {
327 auto time_max = positions.at(it).first;
328 auto ampl = positions.at(it).second;
329 auto sub_wfm = decomposition.at(it);
330
331 BmnHodoDigi SubDigi = ThisDigi; // common base for all
332 SubDigi.fTimeMax = time_max;
333 SubDigi.fFitTimeMax = time_max;
334 SubDigi.fFitAmpl = ampl;
335 SubDigi.fFitR2 = fitR2;
336 SubDigi.SetSignal(SubDigi.fFitAmpl * calib_pair.first);
337 if (SubDigi.GetSignal() < fdigiPars.threshold)
338 continue;
339
340 SubDigi.SetTime(time + SubDigi.fFitTimeMax * ADC_CLOCK_TQDC16VS);
341 if (SubDigi.fFitAmpl + SubDigi.fZL >= fSaturation)
342 SubDigi.SetIsSaturated(true);
343 SubDigi.fCrosstalk =
344 *std::min_element(sub_wfm.begin() + fdigiPars.gateBegin, sub_wfm.begin() + fdigiPars.gateEnd + 1);
345
346 if (fdigiPars.isWriteWfm) {
347 SubDigi.fWfm = wfm;
348 SubDigi.fFitWfm = sub_wfm;
349 SubDigi.fR2history = deconvolution.getR2();
350 }
351
352 digi_vector.push_back(SubDigi);
353 }
354 }
355
356 TClonesArray& ar_Hodo = *Hododigit;
357 for (auto& digi : digi_vector) {
358 digi.fNpeaks = digi_vector.size();
359 new (ar_Hodo[Hododigit->GetEntriesFast()]) BmnHodoDigi(digi);
360 }
361 }
362
363 timer.Stop();
364 fworkTime_cpu += timer.RealTime();
365 fworkTime_real += timer.CpuTime();
366}
367
369{
370 LOG(info) << Form("%10s: Real time %4.3f s, CPU time %4.3f s", GetName().Data(), fworkTime_real, fworkTime_cpu);
371}
int i
Definition P4_F32vec4.h:22
friend F32vec4 log(const F32vec4 &a)
Definition P4_F32vec4.h:123
friend F32vec4 max(const F32vec4 &a, const F32vec4 &b)
Definition P4_F32vec4.h:31
UInt_t GetNSamples() const
Definition BmnADCDigit.h:35
UShort_t GetChannel() const
Definition BmnADCDigit.h:33
UInt_t GetSerial() const
Definition BmnADCDigit.h:31
Short_t * GetShortValue() const
Definition BmnADCDigit.h:39
void SetAddress(uint32_t address)
void SetTime(double time)
double GetSignal() const
void SetSignal(double signal)
std::vector< float > fWfm
Time of maximum in fit of waveform [adc samples].
float fFitTimeMax
Quality of waveform fit [] – good near 0.
float fFitR2
Energy deposition from fit of waveform [adc counts].
int fZL
Amplitude from waveform [adc counts].
float fFitAmpl
Time over threshold [adc samples].
int fTimeMax
Energy deposition from waveform [adc counts].
int fIntegral
ZeroLevel from waveform [adc counts].
static uint32_t GetAddress(uint32_t StripId, uint32_t StripSide, uint32_t Gain)
Return address from system ID, StripId, StripSide, Gain.
Data class for BmnHodo digital signal processing.
Definition BmnHodoDigi.h:25
void reset() override final
Definition BmnHodoDigi.h:42
float fSignalIntegral
Definition BmnHodoDigi.h:54
float fCrosstalk
Definition BmnHodoDigi.h:55
std::vector< float > fR2history
Definition BmnHodoDigi.h:53
void SetIsSaturated(bool satur)
void ParseConfig(const std::string &file)
void ParseCalibration(const std::string &file)
uint32_t correctINL(uint32_t time, std::pair< size_t, size_t > key)
std::optional< std::pair< float, float > > GetCalibPairFromAddress(uint32_t address)
void fillEvent(TClonesArray *tdc_data, TClonesArray *adc_data, TClonesArray *Hododigit)
std::optional< uint32_t > GetAddressFromBoard(std::pair< size_t, size_t > key)
UInt_t GetValue() const
Definition BmnTDCDigit.h:32
UInt_t GetSerial() const
Definition BmnTDCDigit.h:22
UChar_t GetSlot() const
Definition BmnTDCDigit.h:26
UChar_t GetChannel() const
Definition BmnTDCDigit.h:30
UChar_t GetSlot() const
void SetExternalHarmonics(std::vector< std::complex< float > > harmonics)
void Initialize(int model_order, int exponent_number, int gate_beg, int gate_end)
void MakeInvHarmoMatrix(int signal_length, std::complex< float > **output)
Class to apply Richardson-Lucy deconvolution.
const std::vector< float > & getR2() const
bool runDeconvolution()
const std::vector< std::pair< size_t, float > > & getSignalInfo() const
const std::vector< std::vector< float > > & getSignalDecomposition() const
std::complex< float > ** fAZik
struct WfmProcessor::digiPars fdigiPars
void ProcessWfm(std::vector< float > &wfm, BmnDigiContainerTemplate *digi)
a class to store JSON values
Definition json.hpp:17282
bool contains(KeyT &&key) const
check the existence of an element in a JSON object
Definition json.hpp:19640
ValueType value(const typename object_t::key_type &key, const ValueType &default_value) const
access specified object element with default value
Definition json.hpp:19319
reference at(size_type idx)
access specified array element with bounds checking
Definition json.hpp:19090
#define TDC_CLOCK
#define TQDC16_CHANNEL_COUNT
#define ADC_CLOCK_TQDC16VS
#define TDC_BIN_COUNT
-clang-format
STL namespace.
std::vector< std::complex< float > > harmonics