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