BmnRoot 26.10.0-18-g4e3414f9
Software framework of the BM@N experiment
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#include "UniDetectorParameter.h"
13
14#include <boost/algorithm/string/classification.hpp>
15#include <boost/algorithm/string/split.hpp>
16#include <boost/lexical_cast.hpp>
17#include <boost/tokenizer.hpp>
18#include <nlohmann/json.hpp>
19
21{
22 LOG(info) << Form("%s", GetName().Data());
23}
24
26 : WfmProcessor()
27 , fSaturation(std::numeric_limits<int16_t>::max())
28 , fDoRLdeconvolve(false)
29 , fRLthresold(0)
30 , fR2converge(0.01)
31 , fMaxIters(10)
32 , fMaxPeaks(2)
33 , fworkTime_real(0.0)
34 , fworkTime_cpu(0.0)
35{}
36
38 int run,
39 const std::string& mappingFile,
40 const std::string& calibrationFile)
41 : WfmProcessor()
42 , fSaturation(std::numeric_limits<int16_t>::max())
43 , fDoRLdeconvolve(false)
44 , fRLthresold(0)
45 , fR2converge(0.01)
46 , fMaxIters(10)
47 , fMaxPeaks(2)
48 , fworkTime_real(0.0)
49 , fworkTime_cpu(0.0)
50{
51 auto logIfError = [this](const char* what, BmnStatus st) {
52 if (st == kBMNERROR)
53 LOG(error) << GetName() << ": " << what << " was not loaded, decoder may misbehave!";
54 };
55
56 LOG(info) << Form("%s : Config file: %s", GetName().Data(), mappingFile.c_str());
57 logIfError("mapping", ParseConfig(mappingFile));
58
59 BmnStatus calibStatus;
60 if (!calibrationFile.empty()) {
61 LOG(info) << Form("%s : Hand-set Calibration file: %s", GetName().Data(), calibrationFile.c_str());
62 calibStatus = ParseCalibration(calibrationFile);
63 } else {
64 calibStatus = LoadCalibration(period, run);
65 }
66 logIfError("calibration", calibStatus);
67}
68
70{
71 std::string fullPath = std::string(getenv("VMCWORKDIR")) + "/parameters/hodo/" + file;
72 std::ifstream config_file(fullPath);
73 if (!config_file.is_open()) {
74 LOG(error) << GetName() << "::ParseConfig: Cannot open config file: " << fullPath;
75 return kBMNERROR;
76 }
77
79 try {
80 config_file >> j;
81 } catch (const std::exception& e) {
82 LOG(error) << GetName() << "::ParseConfig: Failed to parse JSON: " << e.what();
83 return kBMNERROR;
84 }
85
86 LOG(debug) << GetName() << "::ParseConfig. Version: " << j.value("version", "unknown");
87 LOG(debug) << GetName() << "::ParseConfig. Comment: " << j.value("comment", "none");
88
89 fuoChannelMap.clear();
90 for (const auto& entry : j["mapping"]) {
91 std::string serial = entry["serial"];
92 int channel = entry["channel"];
93 int strip = entry["strip"];
94 int side = entry["side"];
95 int gain = entry["gain"];
96
97 auto key = std::make_pair(std::stoul(serial, nullptr, 16), channel);
98 fuoChannelMap[key] = BmnHodoAddress::GetAddress(strip, side, gain);
99 }
100
101 return kBMNSUCCESS;
102}
103
105{
107 bool gotFromDb = false;
108
109 UniDetectorParameter* pParam = UniDetectorParameter::GetDetectorParameter("HODO", "hodo_calibration", period, run);
110 if (pParam != nullptr) {
111 JsonValue* pValue = (JsonValue*)pParam->GetValue();
112 if (pValue != nullptr) {
113 j = pValue->value;
114 gotFromDb = true;
115 delete pValue;
116 }
117 delete pParam;
118 }
119
120 if (gotFromDb) {
121 LOG(info) << GetName() << " : Loaded calibration from DB for period " << period << ", run " << run;
122
123 return ParseCalibrationJson(j);
124 } else {
125 const std::string fallbackFile = Form("HODO_Q_calibration_period%d.json", period);
126
127 LOG(warn) << GetName() << " : No calibration in DB for period " << period << ", run " << run
128 << ". Falling back to file " << fallbackFile;
129
130 return ParseCalibration(fallbackFile);
131 }
132}
133
135{
136 std::string fullPath = std::string(getenv("VMCWORKDIR")) + "/parameters/hodo/" + file;
137 std::ifstream config_file(fullPath);
138 if (!config_file.is_open()) {
139 LOG(error) << GetName() << "::ParseCalibration: Cannot open config file: " << fullPath;
140 return kBMNERROR;
141 }
142
144 try {
145 config_file >> j;
146 } catch (const std::exception& e) {
147 LOG(error) << GetName() << "::ParseCalibration: Failed to parse JSON: " << e.what();
148 return kBMNERROR;
149 }
150
151 return ParseCalibrationJson(j);
152}
153
155{
156 LOG(debug) << GetName() << "::ParseCalibrationJson. Version: " << j.value("version", "unknown");
157 LOG(debug) << GetName() << "::ParseCalibrationJson. Comment: " << j.value("comment", "none");
158
159 try {
160
161 const auto& par = j.at("parameters");
162
163 fdigiPars.isWriteWfm = par.value("isWriteWfm", false);
164 fdigiPars.gateBegin = par.at("gateBegin");
165 fdigiPars.gateEnd = par.at("gateEnd");
166 fdigiPars.threshold = par.at("threshold");
167 fSaturation = par.at("saturation");
168 fdigiPars.doInvert = par.value("doInvert", false);
169 bool applyINL = par.value("applyINLcorr", true);
170 if (applyINL)
172
173 fuoCalibMap.clear();
174 for (const auto& ch : j.at("calibration")) {
175 int strip = ch.at("strip");
176 int side = ch.at("side");
177 int gain = ch.at("gain");
178 float calib = ch.at("ampl");
179 float integ = ch.at("integral");
180
181 fuoCalibMap[BmnHodoAddress::GetAddress(strip, side, gain)] = std::make_pair(calib, integ);
182 }
183
184 if (j.contains("deconvParameters")) {
185 const auto& decpar = j.at("deconvParameters");
186 fEthalon.clear();
187 fDoRLdeconvolve = decpar.value("isdec", false);
188 fRLthresold = decpar.value("threshold", 0);
189 fR2converge = decpar.value("R2converge", 0.f);
190 fMaxIters = decpar.value("iterations", 0);
191 fMaxPeaks = decpar.value("peaks", 0);
192 if (decpar.contains("ethalon") && decpar["ethalon"].is_array()) {
193 auto tmp = decpar["ethalon"].get<std::vector<float>>();
194 fEthalon.insert(fEthalon.end(), tmp.begin(), tmp.end());
195 }
196 }
197
198 if (j.contains("fitParameters")) {
199 const auto& fitpar = j.at("fitParameters");
200 fdigiPars.isfit = fitpar.value("isfit", false);
201 fdigiPars.harmonics.clear();
202 for (const auto& pair : fitpar.at("harmonics")) {
203 float re = pair.at(0).get<float>();
204 float im = pair.at(1).get<float>();
205 fdigiPars.harmonics.emplace_back(re, im);
206 }
207
208 if (fdigiPars.isfit) {
209 int model_order = fdigiPars.harmonics.size() + 1;
210 fSignalLength = fdigiPars.gateEnd - fdigiPars.gateBegin + 1;
211 auto maxElement = std::max_element(fdigiPars.harmonics.begin(), fdigiPars.harmonics.end(),
212 [](const std::complex<float>& a, const std::complex<float>& b) {
213 return std::abs(a) < std::abs(b);
214 });
215 if (maxElement != fdigiPars.harmonics.end()) {
216 // set signal length as 5 * harmonic length
217 int maxElementLength = floor(5. / (-log(real(*maxElement))));
218 if (maxElementLength < fSignalLength)
219 fSignalLength = maxElementLength;
220 }
221
222 fAZik = new std::complex<float>*[model_order];
223 for (int i = 0; i < model_order; i++) {
224 fAZik[i] = new std::complex<float>[model_order];
225 for (int k = 0; k < model_order; k++)
226 fAZik[i][k] = {0., 0.};
227 }
229 Pfitter.Initialize(fdigiPars.harmonics.size(), fdigiPars.harmonics.size(), fdigiPars.gateBegin,
230 fdigiPars.gateEnd);
231 Pfitter.SetExternalHarmonics(fdigiPars.harmonics);
233 }
234 }
235 } catch (const std::exception& e) {
236 LOG(error) << GetName() << "::ParseCalibrationJson: error while parsing: " << e.what();
237 return kBMNERROR;
238 }
239
240 return kBMNSUCCESS;
241}
242
244{
245 std::string path = std::string(getenv("VMCWORKDIR")) + "/parameters/hodo/INLcorrections/";
246 LOG(info) << Form("%s : Loading INL corrections from directory: %s", GetName().Data(), path.c_str());
247
248 boost::property_tree::ptree pt;
249 std::string header = "TQDC16VS";
250 std::string iniheader = header + "_E";
251 std::string postfix = "inl_corr";
252 fINLcorrMap.clear();
253
254 for (auto& it : fuoChannelMap) {
255 auto boardSerial = (unsigned int)it.first.first;
256 std::string corrFileName = Form("%s%s-%04X-%04X.ini", path.c_str(), header.c_str(),
257 ((boardSerial & 0xffff0000) >> 16), (boardSerial & 0xffff));
258 boost::property_tree::ini_parser::read_ini(corrFileName.c_str(), pt);
259 std::string thisHeader = Form("%s-%08x-%s", iniheader.c_str(), boardSerial, postfix.c_str());
260 for (UInt_t chan_iter = 0; chan_iter < TQDC16_CHANNEL_COUNT; chan_iter++) {
261 auto correction_string = pt.get<std::string>(Form("%s.%d", thisHeader.c_str(), chan_iter));
262 istringstream ss(correction_string);
263
264 std::string token;
265 std::vector<float> corrV;
266 while (std::getline(ss, token, ','))
267 corrV.push_back(stof(token));
268 assert(corrV.size() == 1024);
269
270 auto key = std::make_pair(boardSerial, chan_iter);
271 fINLcorrMap[key] = corrV;
272 } // channels loop
273 } // serials loop
274
275 return kBMNSUCCESS;
276}
277
278std::optional<uint32_t> BmnHodoRaw2Digit::GetAddressFromBoard(std::pair<size_t, size_t> key)
279{
280 try {
281 return fuoChannelMap.at(key);
282 } catch (const std::out_of_range& e) {
283 LOG(debug) << GetName() << ":: GetAddressFromBoard. Board serial " << std::hex << key.first << std::dec
284 << " channel " << key.second << " is not connected. Skipping it";
285 return std::nullopt;
286 }
287}
288
289uint32_t BmnHodoRaw2Digit::correctINL(uint32_t time, std::pair<size_t, size_t> key)
290{
291 try {
292 return time + fINLcorrMap.at(key).at(time & 1023);
293 } catch (const std::out_of_range& e) {
294 LOG(warning) << GetName() << "::correctINL. Board serial " << std::hex << key.first << std::dec << " channel "
295 << key.second << " is not connected. Leave without INL correction";
296 return time;
297 }
298}
299
300std::optional<std::pair<float, float>> BmnHodoRaw2Digit::GetCalibPairFromAddress(uint32_t address)
301{
302 try {
303 return fuoCalibMap.at(address);
304 } catch (const std::out_of_range& e) {
305 LOG(debug) << GetName() << ":: GetCalibPairFromAddress " << std::bitset<32>(address)
306 << " is marked badly in calibration file.";
307 return std::nullopt;
308 }
309}
310
312{
313
314 LOG(debug) << GetName() << "::fillEvent";
315 TStopwatch timer;
316 timer.Start();
317
318 for (int iAdc = 0; iAdc < adc->GetEntriesFast(); iAdc++) {
320 // Double_t adcTimestamp = adcDig->GetAdcTimestamp() * ADC_CLOCK_TQDC16VS;
321 // Double_t trgTimestamp = adcDig->GetTrigTimestamp() * ADC_CLOCK_TQDC16VS;
322 auto key = std::make_pair(adcDig->GetSerial(), adcDig->GetChannel());
323 auto catch_address = GetAddressFromBoard(key);
324 if (!catch_address.has_value())
325 continue; // Not connected lines
326 auto address = catch_address.value();
327
328 auto catch_calib = GetCalibPairFromAddress(address);
329 if (!catch_calib.has_value())
330 continue; // Strip side gain is not involved in the analysis
331 auto calib_pair = catch_calib.value();
332
333 double time = -1.0;
334 for (int iTdc = 0; iTdc < tdc->GetEntriesFast(); iTdc++) {
335 BmnTDCDigit* tdcDig = (BmnTDCDigit*)tdc->At(iTdc);
336 if (tdcDig->GetSerial() != adcDig->GetSerial() || tdcDig->GetSlot() != adcDig->GetSlot())
337 continue;
338 if (tdcDig->GetChannel() != adcDig->GetChannel())
339 continue;
340 time = correctINL(tdcDig->GetValue(), key);
341 time *= TDC_CLOCK / TDC_BIN_COUNT;
342 }
343
344 std::vector<float> wfm(adcDig->GetValue().begin(), adcDig->GetValue().end());
345 std::vector<BmnHodoDigi> digi_vector;
346 BmnHodoDigi ThisDigi; // whole wfm as one digi
347 ThisDigi.reset();
348 ThisDigi.SetAddress(address);
349 ProcessWfm(wfm, &ThisDigi); // invert, remove ZL, precalc as only one signal per waveform
350 ThisDigi.SetSignal((float)ThisDigi.fAmpl * calib_pair.first);
351 if (ThisDigi.GetSignal() < fdigiPars.threshold)
352 continue;
353 ThisDigi.fIntegral = (int)std::accumulate(wfm.begin(), wfm.end(), 0.0); // common for all recovered signals
354 ThisDigi.fSignalIntegral = (float)ThisDigi.fIntegral * calib_pair.second;
355 if (ThisDigi.fSignalIntegral < fdigiPars.threshold)
356 continue;
357 ThisDigi.SetTime(time + ThisDigi.fTimeMax * ADC_CLOCK_TQDC16VS);
358 if (ThisDigi.fAmpl + ThisDigi.fZL >= fSaturation)
359 ThisDigi.SetIsSaturated(true);
360 ThisDigi.fCrosstalk = *std::min_element(wfm.begin() + fdigiPars.gateBegin, wfm.begin() + fdigiPars.gateEnd + 1);
361 digi_vector.push_back(ThisDigi);
362
363 if (fDoRLdeconvolve && ThisDigi.fSignalIntegral > fRLthresold) {
364 digi_vector.clear();
365
366 LOG(debug4) << GetName() << "::ProcessWfm RLDeconvolutor";
367 RLDeconvolutor deconvolution(wfm, fEthalon, fR2converge, fMaxIters);
368 /*bool converged = */ deconvolution.runDeconvolution();
369 const auto& decomposition = deconvolution.getSignalDecomposition();
370 const auto& positions = deconvolution.getSignalInfo();
371 assert(decomposition.size() == positions.size());
372 auto fitR2 = deconvolution.getR2().back(); // common for all recovered signals
373
374 // Create a vector of indices
375 std::vector<size_t> indices(positions.size());
376 std::iota(indices.begin(), indices.end(), 0);
377
378 // Sort the indices based on the corresponding amplitudes and extract the top fMaxPeaks indices
379 std::sort(indices.begin(), indices.end(),
380 [&positions](size_t a, size_t b) { return positions[a].second > positions[b].second; });
381 std::vector<size_t> topIndices = (fMaxPeaks <= indices.size())
382 ? std::vector<size_t>(indices.begin(), indices.begin() + fMaxPeaks)
383 : indices;
384
385 // Apply calibration
386 LOG(debug4) << GetName() << "::ProcessWfm Selection";
387 for (auto it : topIndices) {
388 auto time_max = positions.at(it).first;
389 auto ampl = positions.at(it).second;
390 auto sub_wfm = decomposition.at(it);
391
392 BmnHodoDigi SubDigi = ThisDigi; // common base for all
393 SubDigi.fTimeMax = time_max;
394 SubDigi.fFitTimeMax = time_max;
395 SubDigi.fFitAmpl = ampl;
396 SubDigi.fFitR2 = fitR2;
397 SubDigi.SetSignal(SubDigi.fFitAmpl * calib_pair.first);
398 if (SubDigi.GetSignal() < fdigiPars.threshold)
399 continue;
400
401 SubDigi.SetTime(time + SubDigi.fFitTimeMax * ADC_CLOCK_TQDC16VS);
402 if (SubDigi.fFitAmpl + SubDigi.fZL >= fSaturation)
403 SubDigi.SetIsSaturated(true);
404 SubDigi.fCrosstalk =
405 *std::min_element(sub_wfm.begin() + fdigiPars.gateBegin, sub_wfm.begin() + fdigiPars.gateEnd + 1);
406
407 if (fdigiPars.isWriteWfm) {
408 SubDigi.fWfm = wfm;
409 SubDigi.fFitWfm = sub_wfm;
410 SubDigi.fR2history = deconvolution.getR2();
411 }
412
413 digi_vector.push_back(SubDigi);
414 }
415 }
416
417 TClonesArray& ar_Hodo = *Hododigit;
418 for (auto& digi : digi_vector) {
419 digi.fNpeaks = digi_vector.size();
420 new (ar_Hodo[Hododigit->GetEntriesFast()]) BmnHodoDigi(digi);
421 }
422 }
423
424 timer.Stop();
425 fworkTime_cpu += timer.RealTime();
426 fworkTime_real += timer.CpuTime();
427}
428
430{
431 LOG(info) << Form("%10s: Real time %4.3f s, CPU time %4.3f s", GetName().Data(), fworkTime_real, fworkTime_cpu);
432}
int i
Definition P4_F32vec4.h:22
friend F32vec4 max(const F32vec4 &a, const F32vec4 &b)
Definition P4_F32vec4.h:31
unsigned int UInt_t
char * Form(const char *fmt,...)
BmnStatus
Define status code.
Definition BmnEnums.h:93
@ kBMNERROR
Error.
Definition BmnEnums.h:95
@ kBMNSUCCESS
Success.
Definition BmnEnums.h:94
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 FillEvent(TClonesArray *tdc_data, TClonesArray *adc_data, TClonesArray *Hododigit)
BmnStatus ParseConfig(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)
std::unordered_map< std::pair< size_t, size_t >, std::vector< float >, boost::hash< std::pair< size_t, size_t > > > fINLcorrMap
BmnStatus ParseINLcorrections()
BmnStatus LoadCalibration(int period, int run)
std::vector< float > fEthalon
BmnStatus ParseCalibrationJson(const nlohmann::json &j)
std::unordered_map< uint32_t, std::pair< float, float > > fuoCalibMap
BmnStatus ParseCalibration(const std::string &file)
std::unordered_map< std::pair< size_t, size_t >, uint32_t, boost::hash< std::pair< size_t, size_t > > > fuoChannelMap
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
Int_t GetEntriesFast() const
TObject * At(Int_t idx) const override
Double_t RealTime()
void Start(Bool_t reset=kTRUE)
Double_t CpuTime()
void Stop()
int GetValue(vector< UniValue * > &parameter_value)
get value of detector parameter presented by an array
static UniDetectorParameter * GetDetectorParameter(int value_id)
get detector parameter from the database
std::complex< float > ** fAZik
struct WfmProcessor::digiPars fdigiPars
void ProcessWfm(std::vector< float > &wfm, BmnDigiContainerTemplate *digi)
#define TDC_CLOCK
#define TQDC16_CHANNEL_COUNT
#define ADC_CLOCK_TQDC16VS
#define TDC_BIN_COUNT
RVec< PromoteType< T > > floor(const RVec< T > &v)
RVec< PromoteType< T > > log(const RVec< T > &v)
basic_json<> json
default specialization
Definition json.hpp:3365
Definition json.hpp:4446