R3BROOT
R3B analysis software
Loading...
Searching...
No Matches
R3BDigitizingTacQuila.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
17#include "R3BShared.h"
18#include <RtypesCore.h>
19#include <TRandom3.h>
20#include <algorithm>
21#include <cmath>
22#include <memory>
23#include <vector>
24
26{
28 : fPMTThresh(1.) // [MeV]
29 , fSaturationCoefficient(0.012) //
31 , fTimeRes(0.15) // time + Gaus(0., fTimeRes) [ns]
32 , fEResRel(0.05) // Gaus(e, fEResRel * e) []
33 , fIntegrationTime(400.) // [ns]
34 , fRnd(new TRandom3())
35 {
36 }
37
39 Channel::Channel(Side side, const Params& para)
41 , par(para)
42 {
43 }
44
46 {
47 fPMTHits.emplace_back(newHit.time, newHit.intensity);
48 // NOTE: Sorting after every hit may not be efficient, but this way
49 // FindThresholdExeeding hit can be made const
50 std::sort(fPMTHits.begin(), fPMTHits.end());
51 cachedFirstHitOverThresh.invalidate();
52 }
53
54 auto Channel::HasFired() -> bool
55 {
56 if (!cachedFirstHitOverThresh.valid())
57 {
59 cachedQDC.invalidate();
60 cachedTDC.invalidate();
61 cachedEnergy.invalidate();
62 }
63 // If there is a hit that exceeded the threshold, the PMT fill fire
64 return cachedFirstHitOverThresh.get() != fPMTHits.end();
65 }
66
67 auto Channel::GetQDC() -> double
68 {
69 if (!cachedQDC.valid())
70 {
71 cachedQDC.set(BuildQDC());
72 }
73 return cachedQDC;
74 }
75
76 auto Channel::GetTDC() -> double
77 {
78 if (!cachedTDC.valid())
79 {
80 cachedTDC.set(BuildTDC());
81 }
82 return cachedTDC;
83 }
84
85 auto Channel::GetEnergy() -> double
86 {
87 if (!cachedEnergy.valid())
88 {
90 }
91 return cachedEnergy;
92 }
93
94 auto Channel::BuildQDC() -> double
95 {
96 if (HasFired())
97 {
98 // @mheil: Should that be calculated using an exponential with the
99 // prior accumulated light / exponential decay or just all the light that arrives?
100 // Maximum pulse height or just sum over all light = charge?
101 Double_t light = 0.;
102 for (auto hit_it = cachedFirstHitOverThresh.get(); hit_it != fPMTHits.end(); hit_it++)
103 {
104 const auto hit = *hit_it;
105 if (hit.time < (*cachedFirstHitOverThresh.get()).time + par.fIntegrationTime)
106 {
107 light += hit.intensity;
108 }
109 }
110 return light;
111 }
112
113 return 0.;
114 }
115
116 auto Channel::BuildTDC() -> double
117 {
118 if (HasFired())
119 {
120 return (*cachedFirstHitOverThresh.get()).time + par.fRnd->Gaus(0., par.fTimeRes);
121 }
122
123 return -1.;
124 }
125
126 auto Channel::BuildEnergy() -> double
127 {
128 Double_t energy = GetQDC();
129 // Apply reverse attenuation (TODO: Should be last?)
130 // e = e * exp((2. * (NeulandPaddle::gHalfLength)) * NeulandPaddle::gAttenuation / 2.);
131 // Apply saturation
132 energy = energy / (1. + par.fSaturationCoefficient * energy);
133 // Apply energy smearing
134 energy = par.fRnd->Gaus(energy, par.fEResRel * energy);
135 // Apply reverse saturation
136 if (par.fExperimentalDataIsCorrectedForSaturation)
137 {
138 energy = energy / (1. - par.fSaturationCoefficient * energy);
139 }
140 return energy;
141 }
142
143 auto Channel::FindThresholdExceedingHit() const -> std::vector<Channel::Signal>::const_iterator
144 {
145 // Note that this accumulated light is NOT used for the QDC Value
146 // @mheil: Is that actually correct?
147 Double_t currentHeightOfLightPulse = 0.;
148 Double_t previousTime = 0.;
149
150 // TODO: Could do this shorter with find_if?
151 for (auto hit_it = fPMTHits.begin(); hit_it != fPMTHits.end(); hit_it++)
152 {
153 auto hit = *hit_it;
154
155 // Until the light of this hit arrives at the pmt, the previous light pulses have decayed
156 currentHeightOfLightPulse *= exp(-DEFAULT_LAMBDA * (hit.time - previousTime));
157 previousTime = hit.time;
158
159 // Add the current light pulse
160 currentHeightOfLightPulse += hit.intensity;
161
162 // If the light pulse is higher than the threshold, this hit causes the pmt to fire
163 if (currentHeightOfLightPulse * exp(Paddle::DEFAULT_ATTENUATION * Paddle::HALF_BAR_LENGTH) > par.fPMTThresh)
164 {
165 return hit_it;
166 }
167 }
168
169 // Threshold was not exceeded
170 return fPMTHits.end();
171 }
172
174 {
175 fPMTHits.clear();
176 cachedFirstHitOverThresh.invalidate();
177 cachedQDC.invalidate();
178 cachedTDC.invalidate();
179 cachedEnergy.invalidate();
180 }
181
183 {
184 auto signal = Hit{};
185 signal.qdcUnSat = GetEnergy();
186 signal.qdc = GetQDC();
187 signal.tdc = GetTDC();
188 signal.side = this->GetSide();
189 signals.push_back(signal);
190 }
191} // namespace R3B::Digitizing::Neuland::TacQuila
AbstractChannel(R3B::Side side, bool has_cal_output=false)
Validated< std::vector< Signal >::const_iterator > cachedFirstHitOverThresh
auto FindThresholdExceedingHit() const -> std::vector< Signal >::const_iterator
double time
Time value of the channel signal.
double intensity
Intensity of the channel signal.