R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BNeulandHitCalibrationBar.cxx
Go to the documentation of this file.
1/******************************************************************************
2 * Copyright (C) 2019 GSI Helmholtzzentrum für Schwerionenforschung GmbH *
3 * Copyright (C) 2019-2025 Members of R3B Collaboration *
4 * *
5 * This software is distributed under the terms of the *
6 * GNU General Public Licence (GPL) version 3, *
7 * copied verbatim in the file "LICENSE". *
8 * *
9 * In applying this license GSI does not waive the privileges and immunities *
10 * granted to it by virtue of its status as an Intergovernmental Organization *
11 * or submit itself to any jurisdiction. *
12 ******************************************************************************/
13
15#include "R3BLogger.h"
16#include "R3BNeulandCommon.h"
18#include "TCanvas.h"
19#include "TDirectory.h"
20#include "TGaxis.h"
21#include "TGraph.h"
22#include "TPad.h"
23#include <Rtypes.h>
24#include <RtypesCore.h>
25#include <TGraphErrors.h>
26#include <TH2.h>
27#include <TString.h>
28#include <TVirtualPad.h>
29#include <algorithm>
30#include <array>
31#include <cmath>
32#include <fairlogger/Logger.h>
33#include <fmt/core.h>
34#include <numeric>
35#include <vector>
36
37using DPair = std::array<Double_t, 2>;
38
39// FIXME namespace Neuland::Calibration { with c++17
40namespace R3B::Neuland // NOLINT
41{
42 namespace Calibration
43 {
44 bool GetJumps(double* data,
45 unsigned int size,
46 double_t threshold,
47 unsigned int averageSize,
48 std::vector<unsigned int>* output)
49 {
50 if (size < 2 * averageSize)
51 return false;
52
53 const auto scale = 1. / averageSize;
54
55 auto lastAverage = std::accumulate(data, data + averageSize, 0.) * scale;
56 const auto last = data + size;
57 auto currentPos = data + averageSize;
58
59 while (currentPos + averageSize < last)
60 {
61 const auto currentAverage = std::accumulate(currentPos, currentPos + averageSize, 0.) * scale;
62 if (fabs(currentAverage - lastAverage) > threshold)
63 {
64 if (output == nullptr)
65 return true;
66 output->push_back(currentPos - data);
67 }
68 lastAverage = currentAverage;
69 currentPos += averageSize;
70 }
71
72 return false;
73 }
74
75 template <Int_t iterations = 8>
76 const double FastExp(const double val)
77 {
78 auto exp = 1. + val / (1 << iterations);
79 for (auto i = 0; i < iterations; ++i)
80 {
81 exp *= exp;
82 }
83 return exp;
84 }
85
86 const double GetLightAtPMT(const double energyLoss,
87 const double position,
88 const double invLightAttenuationLength,
89 const int side)
90 {
91 return energyLoss *
92 FastExp(-invLightAttenuationLength * (TotalBarLength * 0.5 + (1 - 2 * side) * position));
93 }
94
95 constexpr auto RightSide = 0;
96 constexpr auto LeftSide = 1;
97
98 constexpr auto RightSideHitBit = RightSide;
99 constexpr auto LeftSideHitBit = LeftSide;
100 constexpr auto PosCalibrationBit = 2;
101 constexpr auto TSyncCalibrationBit = 3;
102 constexpr auto EnergyCalibrationBit = 4;
103 constexpr auto ThresholdCalibrationBit = 5;
104 constexpr auto PedestalCalibrationBit = 6;
105 constexpr auto TimeJumpedBit = 20;
106 constexpr auto LogCompleteBit = 30;
107 constexpr auto StrangeBit = 31;
108
109 constexpr auto PositionCalibrationSize = 1024;
110 constexpr auto EnergyCalibrationSize = 4 * 1024;
111 constexpr auto ThresholdCalibrationSize = 1024;
112 constexpr auto CalibrationLogSize = 16 * 1024;
113 constexpr auto LogInitialSize = 128;
114
115 constexpr auto MinEnergyDeposit = 1.; // MeV
116 constexpr auto MaxTSyncError = 0.05; // ns
117 constexpr auto TimeJumpThreshold = 0.075; // ns
118 constexpr auto MaxFastTDiffError = 0.05;
119
120 constexpr auto MaxNumberOfFails = 10U;
121
122 const void SetStatus(Int_t& var, Int_t statusBit) { var |= (1 << statusBit); }
123 const void ClearStatus(Int_t& var, Int_t statusBit) { var &= ~(1 << statusBit); }
124 const bool IsStatus(Int_t var, Int_t statusBit) { return (var & (1 << statusBit)); }
125
126 const char* HitCalibrationBar::CalibrationStatusDescription[] = { "No Data recorded",
127 "Calibration Failed",
128 "Calibration was Successful",
129 "Energy Calibration Failed",
130 "Time Synchronisation Failed",
131 "Time Synchronisation Error large",
132 "Parameters look strange",
133 "Time Jump" };
134 const char* HitCalibrationBar::CalibrationStatusAbbreviation[] = { "XX", "FA", " ", "EC",
135 "TS", "SE", "??", "TJ" };
136
138 : ID(id)
139 , Validity(0)
140 , FailCounter(0)
141 , LastEventNumber(0)
144 , Gain({ NaN, NaN })
145 , InvLightAttenuationLength(NaN)
146 , Pedestal({ 0, 0 })
147 , Saturation({ 0., 0. })
148 , TimeSync({ NaN, NaN })
149 , Threshold({ NaN, NaN })
150 {
151
152 Reset();
153 LastHits.reserve(CalibrationLogSize);
154
155 Log.TimeDifference.Expand(LogInitialSize);
156 Log.TimeDifference.SetTitle("Time Difference Parameter;Event Number;Parameter Value / ns");
157
158 Log.EffectiveSpeed.Expand(LogInitialSize);
159 Log.EffectiveSpeed.SetTitle("Effective Speed Parameter;Event Number;Parameter Value / cm/ns");
160
161 Log.LightAttenuationLength.Expand(LogInitialSize);
162 Log.LightAttenuationLength.SetTitle("Attenuation Length Parameter;Event Number;Parameter Value / cm");
163
164 for (auto side = 0; side < 2; ++side)
165 {
166 Log.Gain[side].Expand(LogInitialSize);
167 Log.Gain[side].SetTitle("Gain Parameter;Event Number;Parameter Value / ns/MeVle");
168
169 Log.TotalHits[side] = TH1F("", "Total Hits; Light at PMT / MeVle; #Hits", 64, 0., 32.);
170 Log.TotalHits[side].SetDirectory(nullptr);
171 Log.TotalHits[side].SetStats(false);
172
173 Log.MissedHits[side] = TH1F("", "Missed Hits; Light at PMT / MeVle; #Hits", 64, 0., 32.);
174 Log.MissedHits[side].SetDirectory(nullptr);
175 Log.MissedHits[side].SetStats(false);
176 Log.MissedHits[side].SetLineColor(kGreen);
177
178 Log.Energy[side] = TH1F("", "Energy Calibration; Gain / ns/MeVle; Counts", 100, 0., 30.);
179 Log.Energy[side].SetDirectory(nullptr);
180 Log.Energy[side].SetStats(false);
181
182 Log.Saturation[side] =
183 TH2F("", "Saturation; Light at PMT / MeVle; QDC / ns", 128, 0., 128., 256, 0., 512.);
184 Log.Saturation[side].SetDirectory(nullptr);
185 Log.Saturation[side].SetStats(false);
186
187 Log.Pedestal[side] = TH1F("", "Pedestal; QDC / ns", 512, 0., 512.);
188 Log.Pedestal[side].SetDirectory(nullptr);
189 Log.Pedestal[side].SetStats(false);
190 }
191
192 FitGraph.Expand(PositionCalibrationSize);
193 }
194
196
198 {
199 if (!std::isnan(par->GetTimeOffset(1)))
200 {
201 TimeDifference = par->GetTDiff();
204 }
205
206 if (!std::isnan(par->GetEnergyGain(1)))
207 {
208 Gain[0] = par->GetEnergyGain(1);
209 Gain[1] = par->GetEnergyGain(2);
210
212
214 }
215
216 if (!std::isnan(par->GetPMTThreshold(1)))
217 {
218 Threshold[0] = par->GetPMTThreshold(1);
219 Threshold[1] = par->GetPMTThreshold(2);
221 }
222
223 if (!std::isnan(par->GetPMTSaturation(1)))
224 {
225 Saturation[0] = par->GetPMTSaturation(1);
226 Saturation[1] = par->GetPMTSaturation(2);
227 }
228
229 if (!std::isnan(par->GetPedestal(1)))
230 {
231 Pedestal[0] = par->GetPedestal(1);
232 Pedestal[1] = par->GetPedestal(2);
234 }
235
236 LOG(debug) << "Loaded Parameters for Bar " << ID;
237 return;
238 }
239
240 void HitCalibrationBar::Set(const Int_t side, const Double_t time, const Int_t qdc)
241 {
242 SetStatus(Validity, side);
243 R3BLOG(debug1, fmt::format("side: {}, time: {} qdc: {}", side, time, qdc));
244 CurrentHit.Time[side] = time;
245 CurrentHit.QDC[side] = std::max(qdc - Pedestal[side], 1);
246 Log.Pedestal[side].Fill(qdc);
247
249 {
250 if (fabs(CurrentHit.Time[1] - CurrentHit.Time[0]) > 0.5 * MaxCalTime)
251 {
252 if (CurrentHit.Time[1] < CurrentHit.Time[0])
253 CurrentHit.Time[1] += MaxCalTime;
254 else
255 CurrentHit.Time[0] += MaxCalTime;
256 }
257
259 {
260 CurrentHit.EntryPosition =
261 (CurrentHit.Time[1] - CurrentHit.Time[0] - TimeDifference) * EffectiveSpeed;
262
263 if (fabs(CurrentHit.EntryPosition) > TotalBarLength)
264 {
265 ++FailCounter;
266 if (FailCounter > 10)
267 {
268 LOG(debug) << "Bar " << ID << ":"
269 << " Positions were too often too strange. Might be a Time-Jump in one PMT. "
270 "Removing Position-Calibration.";
271 FailCounter = 0;
273 }
274 }
275 else
276 {
277 FailCounter = 0;
278 }
279 }
280 }
281 }
282 void HitCalibrationBar::SetGlobalTSync(const Double_t value, const Double_t error)
283 {
284 TimeSync = { value, error };
285 if (std::isnan(value))
287 else
289 }
290
292 {
293 if (LastHits.size() == 0 && !IsStatus(Validity, LogCompleteBit))
294 return CalibrationStatus::noData;
296 return CalibrationStatus::completeFail;
298 return CalibrationStatus::energyGainFail;
300 return CalibrationStatus::timeSyncFail;
302 return CalibrationStatus::timeJump;
303 if (TimeSync[1] > MaxTSyncError)
304 return CalibrationStatus::timeSyncError;
306 return CalibrationStatus::strangeParameters;
307 return CalibrationStatus::success;
308 }
309
310 Bool_t HitCalibrationBar::Add(const Double_t timeOffset,
311 const Double_t entryPosition,
312 const Double_t exitPosition,
313 const Double_t energy,
314 const UInt_t eventNumber)
315 {
316
317 // If we have already a gain calibration, we can log the hits.
318 // This way we might get some information about strange behaviour.
320 {
321 const auto centerPosition = (entryPosition + exitPosition) * 0.5;
322 const DPair lightAtPMT = { GetLightAtPMT(energy, centerPosition, InvLightAttenuationLength, RightSide),
323 GetLightAtPMT(energy, centerPosition, InvLightAttenuationLength, LeftSide) };
324
326 {
327 Log.MissedHits[RightSide].Fill(lightAtPMT[RightSide]);
328 }
330 {
331 Log.MissedHits[LeftSide].Fill(lightAtPMT[LeftSide]);
332 }
333
334 for (auto side = 0; side < 2; ++side)
335 {
336 Log.TotalHits[side].Fill(lightAtPMT[side]);
337 // std::cout << "???????????????lightAtPMT" << lightAtPMT[side] << " \n";
338 }
340 {
341 for (auto side = 0; side < 2; ++side)
342 {
343 Log.Saturation[side].Fill(lightAtPMT[side], CurrentHit.QDC[side]);
344 const auto gain = CurrentHit.QDC[side] /
345 (lightAtPMT[side] * (1. - CurrentHit.QDC[side] * SaturationCoefficient));
346 Log.Energy[side].Fill(gain);
347 }
348 }
349 }
350
351 // We require both sides and reasonable energy deposit.
353 energy < MinEnergyDeposit)
354 return false;
355
356 // Dont take the bar, if the cosmic was somewhere close to the lightguide:
357 // Strange things happen there ;).
358 const auto maxPos = BarLength * 0.5 - 1.;
359 if (fabs(entryPosition) > maxPos || fabs(exitPosition) > maxPos)
360 return false;
361
362 LastEventNumber = eventNumber;
363
364 for (auto side = 0; side < 2; ++side)
365 CurrentHit.Time[side] -= timeOffset * 0.5;
366
367 CurrentHit.EntryPosition = entryPosition;
368 CurrentHit.ExitPosition = exitPosition;
369 CurrentHit.Energy = energy;
370
371 LastHits.push_back(CurrentHit);
372
373 if (LastHits.size() % PositionCalibrationSize == 0)
375
376 if (LastHits.size() % EnergyCalibrationSize == 0)
378
379 // If we reached our limit, clear the vector.
380 // The content itself is still there. We will set a (hacky) bit,
381 // so we know when the vector actually contains more data than its size.
382 if (LastHits.size() == CalibrationLogSize)
383 {
385 LastHits.clear();
386 }
387
388 // We can say only that this was a somewhat good event, if we have a position calibration
389 // and the position of the track matches with the one from the calibration
391 fabs(0.5 * (entryPosition + exitPosition) - GetPosition()) > 25.)
392 return true;
393
394 return true;
395 }
396
403
404 void HitCalibrationBar::Calibrate(TDirectory* histogramDir)
405 {
406 const auto nHits = (IsStatus(Validity, LogCompleteBit) ? CalibrationLogSize : LastHits.size());
407
408 // Check if we have any Hits at all.
409 // If not, this bar is either not connected or has some problems.
410 // In this case do not bother creating plots and just return.
411 if (nHits == 0)
412 return;
413
414 // Position Calibration
415 if (Log.TimeDifference.GetN() > 0)
416 {
417 TimeDifference = getMean(Log.TimeDifference);
418
419 EffectiveSpeed = getMean(Log.EffectiveSpeed);
420
421 // check for timejumps
422 if (GetJumps(Log.TimeDifference.GetY(), Log.TimeDifference.GetN(), TimeJumpThreshold, 3, nullptr))
424
426 }
427 else if (nHits > 0.5 * PositionCalibrationSize)
428 {
429 // There were not enough hits for the usual calibration.
430 // If we have at least half the chunk size, we can try to calibrate anyway
431 positionCalibration(0, nHits);
432 }
433 else
434 {
435 // We cannot have a reliable calibration.
436 // Give up at this point.
438 }
439
440 // Gain Calibration
441 if (Log.Gain[0].GetN() > 0 && Log.Gain[1].GetN() > 0)
442 {
443 for (auto side = 0; side < 2; ++side)
444 {
445 Gain[side] = getMean(Log.Gain[side], Gain[side]);
446 Saturation[side] = SaturationCoefficient * Gain[side];
447 }
448
449 InvLightAttenuationLength = 1. / getMean(Log.LightAttenuationLength, 1. / InvLightAttenuationLength);
450
452 }
453 else if (nHits > 0.5 * EnergyCalibrationSize)
454 {
455 // There were not enough hits for the usual calibration.
456 // If we have at least half the chunk size, we can try to calibrate anyway
457 energyCalibration(0, nHits);
458 }
459 else
460 {
461 // We cannot have a reliable calibration.
462 // Give up at this point.
464 }
465
466 // Threshold Calibration
468
469 createHistograms(histogramDir);
470 }
471
473 {
474 R3BNeulandHitModulePar parameter;
475 parameter.SetModuleId(ID);
476
477 parameter.SetTDiff(TimeDifference);
478 parameter.SetTSync(TimeSync[0]);
481
482 for (auto side = 0; side < 2; ++side)
483 {
484 parameter.SetEnergyGain(Gain[side], side + 1);
485 parameter.SetPMTThreshold(Threshold[side], side + 1);
486 parameter.SetPMTSaturation(Saturation[side], side + 1);
487 parameter.SetPedestal(Pedestal[side], side + 1);
488 }
489
490 LOG(debug) << ID << " Bar Parameters: ";
491 LOG(debug) << " Time Offset : " << parameter.GetTimeOffset(1) << " " << parameter.GetTimeOffset(2);
492 LOG(debug) << " Effective Speed : " << parameter.GetEffectiveSpeed();
493 LOG(debug) << " Energy Gain : " << parameter.GetEnergyGain(1) << " " << parameter.GetEnergyGain(2);
494 LOG(debug) << " Attenuationlength : " << parameter.GetLightAttenuationLength();
495 LOG(debug) << " Threshold : " << parameter.GetPMTThreshold(1) << " "
496 << parameter.GetEnergyGain(2);
497 LOG(debug) << " Pedestal : " << parameter.GetPedestal(1) << " " << parameter.GetPedestal(2);
498 LOG(debug) << " Saturation : " << parameter.GetPMTSaturation(1) << " "
499 << parameter.GetPMTSaturation(2);
500
501 return parameter;
502 }
503
508
509 void HitCalibrationBar::positionCalibration(int firstHit, int nHits)
510 {
511 FitGraph.Set(0);
512 const auto loopSize = (LastHits.size() >= (firstHit + nHits)) ? nHits : (LastHits.size() - firstHit);
513 for (auto index = 0; index < loopSize; ++index)
514 {
515 const auto& hit = LastHits[firstHit + index];
516
517 const auto meanPosition = 0.5 * (hit.EntryPosition + hit.ExitPosition);
518 const auto tdiff = hit.Time[1] - hit.Time[0];
519 FitGraph.SetPoint(index, meanPosition, tdiff);
520 }
521
522 TF1 linearFit("", "pol1", -TotalBarLength * 0.5, TotalBarLength * 0.5);
523 FitGraph.Fit(&linearFit, "NQ");
524 if (linearFit.GetParError(0) > MaxFastTDiffError)
525 cleanupFit(FitGraph, linearFit, 2.5);
526
527 // Use this calibration
528 TimeDifference = linearFit.GetParameter(0);
529 EffectiveSpeed = 1. / linearFit.GetParameter(1);
530
531 // Write parameters to the Log.
532 const auto nPoints = Log.TimeDifference.GetN();
533 Log.TimeDifference.SetPoint(nPoints, LastEventNumber, TimeDifference);
534 Log.TimeDifference.SetPointError(nPoints, 0, linearFit.GetParError(0));
535 Log.EffectiveSpeed.SetPoint(nPoints, LastEventNumber, EffectiveSpeed);
536 Log.EffectiveSpeed.SetPointError(nPoints, 0, linearFit.GetParError(1) * Sqr(EffectiveSpeed));
537
539 }
540
541 void HitCalibrationBar::energyCalibration(int firstHit, int nHits)
542 {
543 const auto loopSize = (LastHits.size() >= (firstHit + nHits)) ? nHits : (LastHits.size() - firstHit);
545 {
546 // This is the first calibration
547 // if we do not have pedestals yet, get them and subtract them from the hits
548
550 for (auto h_index = 0; h_index < loopSize; ++h_index)
551 {
552 auto& hit = LastHits[firstHit + h_index];
553 for (auto side = 0; side < 2; ++side)
554 {
555 hit.QDC[side] = std::max(hit.QDC[side] - Pedestal[side], 1);
556 }
557 }
558 }
559
560 FitGraph.Set(0);
561 for (auto h_index = 0; h_index < loopSize; ++h_index)
562 {
563 const auto& hit = LastHits[firstHit + h_index];
564 const auto centerPosition = (hit.EntryPosition + hit.ExitPosition) * 0.5;
565 FitGraph.SetPoint(h_index, centerPosition, log(hit.QDC[1] * 1. / hit.QDC[0]));
566 }
567
568 TF1 linearFit("", "pol1", -TotalBarLength * 0.5, TotalBarLength * 0.5);
569 // this has usually always outliers, so do not bother doing a normal fit first.
570 cleanupFit(FitGraph, linearFit, 1.);
571
572 InvLightAttenuationLength = 0.5 * linearFit.GetParameter(1);
573
574 const auto logLightAttLenPoints = Log.LightAttenuationLength.GetN();
575 Log.LightAttenuationLength.SetPoint(logLightAttLenPoints, LastEventNumber, 1. / InvLightAttenuationLength);
576 Log.LightAttenuationLength.SetPointError(
577 logLightAttLenPoints, 0, 0.5 * linearFit.GetParError(1) / Sqr(InvLightAttenuationLength));
578
579 for (auto h_index = 0; h_index < loopSize; ++h_index)
580 {
581 const auto& hit = LastHits[firstHit + h_index];
582 const auto centerPosition = (hit.EntryPosition + hit.ExitPosition) * 0.5;
583 for (auto side = 0; side < 2; ++side)
584 {
585 const auto lightAtPMT = GetLightAtPMT(hit.Energy, centerPosition, InvLightAttenuationLength, side);
586 const auto gain = hit.QDC[side] / (lightAtPMT * (1. - hit.QDC[side] * SaturationCoefficient));
587 Log.Energy[side].Fill(gain);
588 }
589 }
590
591 std::array<TH1F, 2> hGainCal = { TH1F("", "", 200, 0., 40.), TH1F("", "", 200, 0., 40.) };
592
593 for (auto h_index = 0; h_index < loopSize; ++h_index)
594 {
595 const auto& hit = LastHits[firstHit + h_index];
596 const auto centerPosition = (hit.EntryPosition + hit.ExitPosition) * 0.5;
597 for (auto side = 0; side < 2; ++side)
598 {
599 const auto lightAtPMT = GetLightAtPMT(hit.Energy, centerPosition, InvLightAttenuationLength, side);
600 const auto distance = 0.5 * TotalBarLength + (1 - 2 * side) * centerPosition;
601 const auto gain = hit.QDC[side] / (lightAtPMT * (1. - hit.QDC[side] * SaturationCoefficient));
602 hGainCal[side].Fill(gain);
603 }
604 }
605
606 TF1 gausFit("", "gaus", 0., 30.);
607 for (auto side = 0; side < 2; ++side)
608 {
609 const auto maxPos = hGainCal[side].GetBinCenter(hGainCal[side].GetMaximumBin());
610 gausFit.SetParameter(0, hGainCal[side].GetMaximum());
611 gausFit.SetParameter(1, maxPos);
612 hGainCal[side].Fit(&gausFit, "NQ", "", maxPos - 1.5, maxPos + 1.5);
613
614 Gain[side] = gausFit.GetParameter(1);
615
616 const auto nPoints = Log.Gain[side].GetN();
617 Log.Gain[side].SetPoint(nPoints, LastEventNumber, Gain[side]);
618 Log.Gain[side].SetPointError(nPoints, 0, gausFit.GetParError(1));
619 }
620
622 }
623
625 {
626 for (auto side = 0; side < 2; ++side)
627 for (auto bin = 0; bin < Log.Pedestal[side].GetNbinsX(); ++bin)
628 {
629 if (Log.Pedestal[side].GetBinContent(bin + 1) > 3.)
630 {
631 Pedestal[side] = bin;
632 break;
633 }
634 }
635
637 }
638
640 {
641 if (Log.TotalHits[0].GetEntries() < ThresholdCalibrationSize ||
642 Log.TotalHits[1].GetEntries() < ThresholdCalibrationSize)
643 return;
644
645 // We have 50% of the maximum at x=[1]
646 // For the bar at the edges it is hard to check if the cosmic was stopped.
647 // Therefore we usually have [2] != 0.
648 TF1 missFit("missFit", "(1. - [2]) * 0.5 * (1. - TMath::Erf([0] * (x - [1]))) + [2]", 0., 20.);
649
650 for (auto side = 0; side < 2; ++side)
651 {
652 const auto nPoints = Log.TotalHits[side].GetNbinsX();
653 Log.MissRatio[side].Expand(nPoints);
654 Log.MissRatio[side].Set(0);
655 for (auto bin = 1; bin <= nPoints; ++bin)
656 {
657 if (Log.MissedHits[side].GetBinContent(bin) > 0)
658 {
659 const auto nextPoint = Log.MissRatio[side].GetN();
660 const auto binCenter = Log.MissedHits[side].GetBinCenter(bin);
661 const auto ratio =
662 Log.MissedHits[side].GetBinContent(bin) / Log.TotalHits[side].GetBinContent(bin);
663 Log.MissRatio[side].SetPoint(nextPoint, binCenter, ratio);
664 }
665 }
666
667 if (Log.MissRatio[side].GetN() > 5)
668 {
669 missFit.SetParameter(0, 0.25);
670 missFit.SetParameter(1, AvgThreshold);
671 missFit.SetParLimits(1, 0., 10.);
672 missFit.SetParameter(2, 0.);
673 missFit.SetParLimits(2, 0., 0.5);
674 Log.MissRatio[side].Fit(&missFit, "QNE");
675 Threshold[side] = missFit.GetParameter(1);
676 }
677 }
679 }
680
681 Int_t HitCalibrationBar::cleanupFit(TGraph& graph, TF1& fit, Double_t maxDifference) const
682 {
683 std::array<Int_t, 256> remove;
684 auto totalRemoved = 0;
685
686 graph.Fit(&fit, "QN ROB=0.90");
687
688 auto removePosition = 0;
689 auto x = graph.GetX();
690 auto y = graph.GetY();
691 auto nPoints = graph.GetN();
692 for (auto p = 0; p < nPoints; ++p)
693 {
694 if (fabs(y[p] - fit.Eval(x[p])) < maxDifference)
695 continue;
696
697 remove[removePosition] = p;
698 if (++removePosition == remove.size())
699 {
700 removePoints(&remove[0], removePosition, graph);
701 totalRemoved += removePosition;
702 nPoints -= removePosition;
703 p -= removePosition;
704 removePosition = 0;
705
706 x = graph.GetX();
707 y = graph.GetY();
708 }
709 }
710
711 removePoints(&remove[0], removePosition, graph);
712 totalRemoved += removePosition;
713
714 graph.Fit(&fit, "QN");
715
716 return totalRemoved;
717 }
718
719 void HitCalibrationBar::removePoints(Int_t* points, Int_t nPoints, TGraph& graph) const
720 {
721 if (nPoints == 0)
722 return;
723
724 auto offset = 1;
725 const auto totalPoints = graph.GetN() - nPoints;
726 const auto x = graph.GetX();
727 const auto y = graph.GetY();
728
729 for (auto p = points[0]; p < totalPoints; ++p)
730 {
731
732 if (offset < nPoints && p + offset == points[offset])
733 {
734 ++offset;
735 --p;
736 continue;
737 }
738
739 x[p] = x[p + offset];
740 y[p] = y[p + offset];
741 }
742
743 graph.Set(totalPoints);
744 }
745
746 void HitCalibrationBar::createHistograms(TDirectory* histogramDir)
747 {
748 if (!histogramDir)
749 return;
750
751 // First creat all the histograms we do not have yet.
752 const auto nHits = (IsStatus(Validity, LogCompleteBit) ? CalibrationLogSize : LastHits.size());
753
754 TGraph posCal;
755 posCal.SetTitle("Position Calibration;Position / cm;Time Difference / ns");
756 posCal.Expand(nHits);
757
758 const auto loopSize = (LastHits.size() > nHits) ? nHits : LastHits.size();
759 for (auto h_index = 0; h_index < loopSize; ++h_index)
760 {
761 const auto& hit = LastHits[h_index];
762 const auto tdiff = hit.Time[1] - hit.Time[0];
763 posCal.SetPoint(posCal.GetN(), hit.EntryPosition, tdiff);
764 }
765
766 std::array<TF1, 2> fSatCal = { TF1("", "[0]*x/(1+[1]*x)", 0., TotalBarLength),
767 TF1("", "[0]*x/(1+[1]*x)", 0., TotalBarLength) };
768
769 for (auto side = 0; side < 2; ++side)
770 {
771 fSatCal[side].SetParameter(0, Gain[side]);
772 fSatCal[side].FixParameter(1, Saturation[side]);
773 }
774
775 // Go into the directory, create a Canvas, fill it with the plots and write it into the directory.
776 histogramDir->cd();
777 const auto canvname = TString::Format("Bar %d", ID);
778 TCanvas Canvas(canvname, canvname);
779 Canvas.SetWindowSize(1920, 1200);
780 Canvas.Divide(1, 3);
781 auto row = 0;
782 TVirtualPad* currentPad;
783
784 currentPad = Canvas.cd(++row);
785 currentPad->Divide(3, 1);
786
787 currentPad->cd(1);
788 if (posCal.GetN() > 0)
789 posCal.Draw("AP");
790
791 currentPad->cd(2);
792 if (Log.TimeDifference.GetN() > 0)
793 Log.TimeDifference.Draw("A*");
794
795 currentPad->cd(3);
796 if (Log.EffectiveSpeed.GetN() > 0)
797 Log.EffectiveSpeed.Draw("A*");
798
799 currentPad = Canvas.cd(++row);
800 currentPad->Divide(4, 1);
801
802 currentPad->cd(1);
803 Log.Energy[0].Draw("");
804 Log.Energy[1].SetLineColor(kRed);
805 Log.Energy[1].Draw("SAME");
806
807 currentPad->cd(2);
808 if (Log.LightAttenuationLength.GetN() > 0)
809 Log.LightAttenuationLength.Draw("A*");
810
811 currentPad->cd(3);
812 if (Log.Gain[0].GetN() > 0)
813 Log.Gain[0].Draw("A*");
814
815 currentPad->cd(4);
816 if (Log.Gain[1].GetN() > 0)
817 Log.Gain[1].Draw("A*");
818
819 currentPad = Canvas.cd(++row);
820 currentPad->Divide(4, 1);
821
822 TPad* mainPads[2];
823 TPad* overlayPads[2];
824 TGaxis* overlayAxis[2];
825 for (auto side = 0; side < 2; ++side)
826 {
827 // Threshold
828 auto pad = currentPad->cd(1 + side);
829 mainPads[side] = new TPad("", "", 0, 0, 1, 1);
830 mainPads[side]->Draw();
831 mainPads[side]->cd();
832 Log.TotalHits[side].Draw();
833 Log.MissedHits[side].Draw("SAME");
834 mainPads[side]->Update();
835 pad->cd();
836
837 overlayPads[side] = new TPad("", "", 0, 0, 1, 1);
838 overlayPads[side]->SetFillStyle(4000);
839 double ymin = 0.;
840 double ymax = 1.;
841 double dy = (ymax - ymin) / 0.8; // 10 per cent margins top and bottom
842 double xmin = mainPads[side]->GetUxmin();
843 double xmax = mainPads[side]->GetUxmax();
844 double dx = (xmax - xmin) / 0.8; // 10 per cent margins left and right
845 overlayPads[side]->Range(xmin - 0.1 * dx, ymin - 0.1 * dy, xmax + 0.1 * dx, ymax + 0.1 * dy);
846 overlayPads[side]->Draw();
847 overlayPads[side]->cd();
848
849 if (Log.MissRatio[side].GetN() > 0)
850 {
851 Log.MissRatio[side].SetLineColor(kRed);
852 Log.MissRatio[side].Draw("IL");
853 }
854 overlayPads[side]->Update();
855 overlayAxis[side] = new TGaxis(xmax, ymin, xmax, ymax, ymin, ymax, 50510, "+L");
856 overlayAxis[side]->SetLabelColor(kRed);
857 overlayAxis[side]->Draw();
858
859 overlayPads[side]->Update();
860
861 pad->Modified();
862
863 // Saturation
864
865 currentPad->cd(3 + side);
866 Log.Saturation[side].Draw("COLZ");
867 fSatCal[side].Draw("SAME");
868 }
869
870 Canvas.Write();
871
872 for (auto side = 0; side < 2; ++side)
873 {
874 delete mainPads[side];
875 delete overlayPads[side];
876 delete overlayAxis[side];
877 }
878 }
879
880 Double_t HitCalibrationBar::getMean(const TGraphErrors& graph, Double_t expectedValue)
881 {
882 const auto nPoints = graph.GetN();
883 if (nPoints == 0)
884 return NaN;
885
886 if (nPoints == 1)
887 return graph.GetY()[0];
888
889 TF1 constantFit("", "pol0", 0, nPoints);
890
891 FitGraph.Set(0);
892 for (auto point = 0; point < nPoints; ++point)
893 {
894 FitGraph.SetPoint(point, point, graph.GetY()[point]);
895 FitGraph.SetPointError(point, 0, graph.GetEY()[point]);
896 }
897 constantFit.SetParameter(0, expectedValue);
898 FitGraph.Fit(&constantFit, "QN");
899 return constantFit.GetParameter(0);
900 }
901 } // namespace Calibration
902} // namespace R3B::Neuland
#define R3BLOG(severity, x)
Definition R3BLogger.h:32
std::array< Double_t, 2 > DPair
void removePoints(Int_t *points, Int_t nPoints, TGraph &graph) const
removes points from a graph in an efficient way
Bool_t Add(const Double_t timeOffset, const Double_t entryPosition, const Double_t exitPosition, const Double_t energy, const UInt_t eventNumber)
Int_t cleanupFit(TGraph &graph, TF1 &fit, Double_t maxDifference) const
struct R3B::Neuland::Calibration::HitCalibrationBar::@127153345345016036324103015046211310221211341120 Log
void SetGlobalTSync(const Double_t value, const Double_t error)
Double_t getMean(const TGraphErrors &graph, Double_t expectedValue=0.)
void Set(const Int_t side, const Double_t time, const Int_t qdc)
void createHistograms(TDirectory *histogramDir)
created and writes histograms to the directory
struct R3B::Neuland::Calibration::HitCalibrationBar::Hit CurrentHit
Double_t GetPMTSaturation(const Int_t side) const
void SetModuleId(const Int_t i)
void SetPMTSaturation(const Double_t val, const Int_t side)
Int_t GetPedestal(const Int_t side) const
void SetTDiff(const Double_t val)
Double_t GetLightAttenuationLength() const
Double_t GetTimeOffset(const Int_t side) const
void SetTSync(const Double_t val)
void SetPedestal(const Int_t val, const Int_t side)
void SetEnergyGain(const Double_t val, const Int_t side)
void SetPMTThreshold(const Double_t val, const Int_t side)
void SetEffectiveSpeed(const Double_t val)
Double_t GetPMTThreshold(const Int_t side) const
void SetLightAttenuationLength(const Double_t val)
Double_t GetEnergyGain(const Int_t side) const
const double GetLightAtPMT(const double energyLoss, const double position, const double invLightAttenuationLength, const int side)
const bool IsStatus(Int_t var, Int_t statusBit)
bool GetJumps(double *data, unsigned int size, double_t threshold, unsigned int averageSize, std::vector< unsigned int > *output)
const void SetStatus(Int_t &var, Int_t statusBit)
const double FastExp(const double val)
const void ClearStatus(Int_t &var, Int_t statusBit)
Simulation of NeuLAND Bar/Paddle.
constexpr auto SaturationCoefficient
constexpr auto BarLength
constexpr auto TotalBarLength
constexpr auto NaN
constexpr auto AvgThreshold
constexpr auto MaxCalTime
constexpr auto Sqr(const T val) -> T