LDMX Software
HcalRecProducer.cxx
Go to the documentation of this file.
1
9
10#include "DetDescr/HcalDigiID.h"
12#include "DetDescr/HcalID.h"
13#include "Hcal/Event/HcalHit.h"
14#include "Hcal/HcalReconConditions.h"
17
18namespace hcal {
19
20HcalRecProducer::HcalRecProducer(const std::string& name,
21 framework::Process& process)
22 : Producer(name, process) {}
23
25 // collection names
26 input_coll_name_ = ps.get<std::string>("input_coll_name");
27 input_pass_name_ = ps.get<std::string>("input_pass_name");
28 sim_hit_coll_name_ = ps.get<std::string>("sim_hit_coll_name");
29 sim_hit_pass_name_ = ps.get<std::string>("sim_hit_pass_name");
30 rec_hit_coll_name_ = ps.get<std::string>("rec_hit_coll_name");
31 // parameters
32 mip_energy_ = ps.get<double>("mip_energy");
33 pe_per_mip_ = ps.get<double>("pe_per_mip");
34 clock_cycle_ = ps.get<double>("clock_cycle");
35 voltage_per_mip_ = ps.get<double>("voltage_per_mip");
36 attlength_ = ps.get<double>("attenuation_length");
37 n_adcs_ = ps.get<int>("n_adcs");
38
39 // configuring corrections graphs derived on the fly
40 // TODO: maybe we should save these as a graph instead?
41 rate_up_slope_ = ps.get<double>("rate_up_slope");
42 time_up_slope_ = ps.get<double>("time_up_slope");
43 rate_dn_slope_ = ps.get<double>("rate_dn_slope");
44 time_dn_slope_ = ps.get<double>("time_dn_slope");
45 time_peak_ = ps.get<double>("time_peak");
47 TF1("pulseFunc",
48 "[0]*((1.0+exp([1]*(-[2]+[3])))*(1.0+exp([5]*(-[6]+[3]))))/"
49 "((1.0+exp([1]*(x-[2]+[3]-[4])))*(1.0+exp([5]*(x-[6]+[3]-[4]))))",
50 (double)n_adcs_ * clock_cycle_ * -1, (double)n_adcs_ * clock_cycle_);
51 pulse_func_.FixParameter(1, rate_up_slope_);
52 pulse_func_.FixParameter(2, time_up_slope_);
53 pulse_func_.FixParameter(3, time_peak_);
54 pulse_func_.FixParameter(5, rate_dn_slope_);
55 pulse_func_.FixParameter(6, time_dn_slope_);
56 pulse_func_.FixParameter(4, 0);
57 pulse_func_.FixParameter(0, 1);
58
59 // build amplitude correction (Ampl[t-1]/Ampl[t]) with pulse-shape
60 int n = 0;
61 for (double t = -clock_cycle_; t < clock_cycle_; t += 0.01) {
62 double ampl_t = pulse_func_.Eval(t);
63 double ampl_tm1 = pulse_func_.Eval(t - clock_cycle_);
64 if (ampl_tm1 > ampl_t) continue;
65 correction_ampl_.SetPoint(n, ampl_tm1 / ampl_t, ampl_t);
66 if (n == 0) min_ampl_fraction_ = ampl_tm1 / ampl_t;
67 n++;
68 }
69
70 // build TOA timewalk correction with pulse-shape
71 double toa_threshold = ps.get<double>("avg_toa_threshold");
72 double gain = ps.get<double>("avg_gain");
73 double pedestal = ps.get<double>("avg_pedestal");
74 n = 0;
75 for (double ampl = toa_threshold + 0.1; ampl < 10000; ampl += 0.01) {
76 pulse_func_.FixParameter(0, ampl);
77 double ampl_t = gain * pedestal + pulse_func_.Eval(0);
78 double toa = fabs(pulse_func_.GetX(toa_threshold,
79 (double)n_adcs_ * clock_cycle_ * -1,
80 (double)n_adcs_ * clock_cycle_));
81 correction_toa_.SetPoint(n, ampl_t, toa);
82 if (n == 0) min_ampl_ = ampl_t;
83 n++;
84 }
85 correction_toa_.SetBit(TGraph::kIsSortedX);
86}
87
89 const ldmx::HgcrocDigiCollection::HgcrocDigi digi, double pedestal,
90 unsigned int iSOI) const {
91 // get toa relative to the startBX
92 double toa_rel_start_bx(0.), max_meas{0.};
93 int toa_sample(0), max_sample(0), i_adc(0);
94 for (int i_sample{0}; i_sample < digi.size(); i_sample++) {
95 auto sample{digi.at(i_sample)};
96 if (sample.toa() > 0) {
97 toa_rel_start_bx = sample.toa() * (clock_cycle_ / 1024); // ns
98 // find in which ADC sample the TOA was taken
99 toa_sample = i_adc;
100 }
101 if ((sample.adcT() - pedestal) > max_meas) {
102 max_meas = (sample.adcT() - pedestal);
103 max_sample = i_adc;
104 }
105 i_adc++;
106 }
107
108 // time w.r.t to the peak
109 double toa = (max_sample - toa_sample) * clock_cycle_ - toa_rel_start_bx;
110
111 // time w.r.t to the SOI
112 toa += (static_cast<int>(iSOI) - max_sample) * clock_cycle_;
113
114 return toa;
115}
116
118 // get the Hcal Geometry
119 const auto& hcal_geometry = getCondition<ldmx::HcalGeometry>(
121
122 // get the reconstruction parameters
123 const auto& the_conditions{
125
126 std::vector<ldmx::HcalHit> hcal_rec_hits;
127 auto hcal_digis = event.getObject<ldmx::HgcrocDigiCollection>(
129 int num_digi_hits = hcal_digis.getNumDigis();
130
131 // get sample of interest index
132 unsigned int i_soi = hcal_digis.getSampleOfInterestIndex();
133
134 // loop through digis
135 int i_digi = 0;
136 while (i_digi < num_digi_hits) {
137 auto digi_posend = hcal_digis.getDigi(i_digi);
138
139 // track readout mode for this hit (1 = ADC, 0 = TOT)
140 bool is_adc_mode = false;
141
142 // ID from first digi sample (which should be in positive end)
143 ldmx::HcalDigiID id_posend(digi_posend.id());
144 ldmx::HcalID id(id_posend.section(), id_posend.layer(), id_posend.strip());
145
146 // position from ID
147 auto position = hcal_geometry.getStripCenterPosition(id);
148 double half_total_width =
149 hcal_geometry.getHalfTotalWidth(id.section(), id.layer());
150 double ecal_dx = hcal_geometry.getEcalDx();
151 double ecal_dy = hcal_geometry.getEcalDy();
152
153 // compute distance to the end of the bar
154 // for back Hcal, we take the half of the bar
155 // for side Hcal, we take the length of the bar (2*half-width)-Ecal_dxy as
156 // an approximation
157 float distance_posend, distance_negend, distance_ecal;
158 if (id.section() == ldmx::HcalID::HcalSection::BACK) {
159 distance_posend = half_total_width;
160 distance_negend = half_total_width;
161 } else {
162 if ((id.section() == ldmx::HcalID::HcalSection::TOP) ||
163 (id.section() == ldmx::HcalID::HcalSection::BOTTOM)) {
164 distance_ecal = ecal_dx;
165 } else {
166 distance_ecal = ecal_dy;
167 }
168 distance_posend = 2 * half_total_width - distance_ecal / 2.;
169 distance_negend = distance_ecal / 2.;
170 }
171
172 // get the estimated voltage and time from digi samples
173 double voltage(0.);
174 double voltage_min(0.);
175 double hit_time(0.);
176
177 double ampl_t(0.);
178 double ampl_t_posend(0.), ampl_tm1_posend(0.);
179 double ampl_t_negend(0.), ampl_tm1_negend(0.);
180
181 // Check if the bar is oriented in X or Y
182 const auto orientation{hcal_geometry.getScintillatorOrientation(id)};
183 int orientation_int = static_cast<int>(orientation);
184
185 // double readout
186 if (id.section() == ldmx::HcalID::HcalSection::BACK) {
187 auto digi_negend = hcal_digis.getDigi(i_digi + 1);
188 ldmx::HcalDigiID id_negend(digi_negend.id());
189
190 double voltage_posend, voltage_negend;
191 // Check if in TOT mode
192 if (digi_posend.isTOT()) {
193 is_adc_mode = false;
194 voltage_posend =
195 (digi_posend.tot() - the_conditions.totCalib(id_posend, 0)) *
196 the_conditions.totCalib(id_posend, 1);
197 voltage_negend =
198 (digi_negend.tot() - the_conditions.totCalib(id_negend, 0)) *
199 the_conditions.totCalib(id_negend, 1);
200 } // end TOT mode, go to ADC mode
201 else {
202 is_adc_mode = true;
203 ampl_t_posend =
204 digi_posend.soi().adcT() - the_conditions.adcPedestal(id_posend);
205 ampl_tm1_posend =
206 digi_posend.soi().adcTm1() - the_conditions.adcPedestal(id_posend);
207 ampl_t_negend =
208 digi_negend.soi().adcT() - the_conditions.adcPedestal(id_negend);
209 ampl_tm1_negend =
210 digi_negend.soi().adcTm1() - the_conditions.adcPedestal(id_negend);
211
212 // correct amplitude (amplitude fractions from both ends need to be
213 // above the boundary of the correction)
214 if (ampl_tm1_posend / ampl_t_posend > min_ampl_fraction_ &&
215 ampl_tm1_negend / ampl_t_negend > min_ampl_fraction_) {
216 ampl_t_posend *=
217 correction_ampl_.Eval(ampl_tm1_posend / ampl_t_posend);
218 ampl_t_negend *=
219 correction_ampl_.Eval(ampl_tm1_negend / ampl_t_negend);
220 }
221
222 // set voltage
223 voltage_posend = ampl_t_posend * the_conditions.adcGain(id_posend, 0);
224 voltage_negend = ampl_t_negend * the_conditions.adcGain(id_negend, 0);
225 } // end ADC mode
226
227 // get TOA
228 double toa_posend =
229 getTOA(digi_posend, the_conditions.adcPedestal(id_posend), i_soi);
230 double toa_negend =
231 getTOA(digi_negend, the_conditions.adcPedestal(id_negend), i_soi);
232
233 // get sign of position along the bar
234 int position_bar_sign = (toa_posend - toa_negend) > 0 ? 1 : -1;
235
236 // correct TOA
237 // amplitudes from both ends need to be above the boundary of the
238 // correction otherwise, one TOA gets corrected and the other does not,
239 // which results in a large TOA difference and an out-of-bounds position
240 if (ampl_t_posend > min_ampl_ && ampl_t_negend > min_ampl_) {
241 toa_posend = correction_toa_.Eval(ampl_t_posend) - toa_posend;
242 toa_negend = correction_toa_.Eval(ampl_t_negend) - toa_negend;
243 }
244
245 // get x(y) coordinate from TOA measurement = (dt*v/2)
246 // if time_posend < time_negend: position is positive
247 // velocity of light in polystyrene, n = 1.6 = c/v
248 double v = 299.792 / 1.6;
249 double position_bar =
250 position_bar_sign * fabs(toa_posend - toa_negend) * v / 2;
251
252 // reverse voltage attenuation
253 // if position along the bar is positive, then the positive end will have
254 // less attenuation than the negative end
255 // NOTE: For now, reverse attenuation is not applied to the energy
256 // deposited since both ends of the bar are summed.
257 double att_posend =
258 exp(-1. * ((distance_posend - position_bar) / 1000.) / attlength_);
259 double att_negend =
260 exp(-1. * ((distance_negend + position_bar) / 1000.) / attlength_);
261
262 // set voltage as the sum of both bars
263 voltage = (voltage_posend + voltage_negend);
264 voltage_min = std::min(voltage_posend, voltage_negend);
265
266 // set amplitude as the average of both bars (reverse attenuated)
267 ampl_t = (ampl_t_posend / att_posend + ampl_t_negend / att_negend) / 2;
268
269 // set position along the bar
270 if (orientation ==
271 ldmx::HcalGeometry::ScintillatorOrientation::horizontal) {
272 position.SetX(position_bar);
273 } else {
274 position.SetY(position_bar);
275 }
276
277 // set hit time
278 // TODO: does this need to revert shift because of propagation of light in
279 // polysterene?
280 hit_time = fabs(toa_posend + toa_negend) / 2; // ns
281
282 i_digi += 2;
283 } // end double readout loop
284 else { // single readout
285 double voltage_i;
286 // Check if in TOT mode (for single-ended readout)
287 if (digi_posend.isTOT()) {
288 is_adc_mode = false;
289 // TOT - number of clock ticks that pulse was over threshold
290 // this is related to the amplitude of the pulse approximately through a
291 // linear drain rate the amplitude of the pulse is related to the energy
292 // deposited
293
294 // convert the time over threshold into a total energy deposited in the
295 // bar (time over threshold [ns] - pedestal) * gain
296
297 voltage_i = (digi_posend.tot() - the_conditions.totCalib(id_posend)) *
298 the_conditions.totCalib(id_posend);
299
300 } // end TOT mode, go to ADC mode (for single-ended readout)
301 else {
302 is_adc_mode = true;
303 // ADC mode of readout
304 // ADC - voltage measurement at a specific time of the pulse
305 ampl_t_posend =
306 digi_posend.soi().adcT() - the_conditions.adcPedestal(id_posend);
307 ampl_tm1_posend =
308 digi_posend.soi().adcTm1() - the_conditions.adcPedestal(id_posend);
309 voltage_i = ampl_t_posend * the_conditions.adcGain(id_posend);
310 }
311
312 // reverse voltage attenuation
313 // for now, assume that position along the bar is the half_total_width
314 double distance_end =
315 id_posend.isNegativeEnd() ? distance_negend : distance_posend;
316 double att = exp(-1. * ((distance_end - fabs(half_total_width)) / 1000.) /
317 attlength_);
318
319 // set voltage
320 voltage = voltage_i;
321 voltage_min = voltage_i;
322
323 // set amplitude (reverse attenuated)
324 ampl_t = ampl_t_posend / att;
325
326 // get TOA
327 double toa =
328 getTOA(digi_posend, the_conditions.adcPedestal(id_posend), i_soi);
329
330 // correct TOA
331 toa = correction_toa_.Eval(ampl_t) - toa;
332
333 // set hit time
334 hit_time = toa; // ns
335
336 i_digi++;
337 } // end single readout loop
338
339 double num_mips_equivalent = voltage / voltage_per_mip_;
340 double energy_deposited = num_mips_equivalent * mip_energy_;
341
342 // reconstructed energy in the layer (approximate)
343 // TODO: need to incorporate corrections if necessary
360 double reconstructed_energy = energy_deposited;
361
362 int pe_s = num_mips_equivalent * pe_per_mip_;
363 int min_pe_s = (voltage_min / voltage_per_mip_) * pe_per_mip_;
364
365 // copy over information to rec hit structure in new collection
366 ldmx::HcalHit rec_hit;
367 rec_hit.setID(id.raw());
368 rec_hit.setXPos(position.X());
369 rec_hit.setYPos(position.Y());
370 rec_hit.setZPos(position.Z());
371 rec_hit.setSection(id.section());
372 rec_hit.setStrip(id.strip());
373 rec_hit.setLayer(id.layer());
374 rec_hit.setPE(pe_s);
375 rec_hit.setMinPE(min_pe_s);
376 rec_hit.setIsADC(is_adc_mode ? 1 : 0);
377 rec_hit.setAmplitude((ampl_t / voltage_per_mip_) * mip_energy_);
378 rec_hit.setEnergy(reconstructed_energy);
379 rec_hit.setTime(hit_time);
380 rec_hit.setOrientation(orientation_int);
381 hcal_rec_hits.push_back(rec_hit);
382 } // end of digi loop
383
384 // mark noise hits if sim hits are available
386 // hcal sim hits_ exist ==> label which hits_ are real and which are pure
387 // noise
388 auto hcal_sim_hits{event.getCollection<ldmx::SimCalorimeterHit>(
390 std::set<int> real_hits;
391 for (auto const& sim_hit : hcal_sim_hits) real_hits.insert(sim_hit.getID());
392 for (auto& hit : hcal_rec_hits)
393 hit.setNoise(real_hits.find(hit.getID()) == real_hits.end());
394 }
395
396 // add collection to event bus
397 event.add(rec_hit_coll_name_, hcal_rec_hits);
398}
399
400} // namespace hcal
401
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that translates HCal ID into positions of strip hits.
Class that stores Stores reconstructed hit information from the HCAL.
Class that defines an HCal sensitive detector.
Class that performs basic HCal digitization.
Class that represents a digitized hit in a calorimeter cell readout by an HGCROC.
Class which stores simulated calorimeter hit information.
const T & getCondition(const std::string &condition_name)
Access a conditions object for the current event.
Implements an event buffer system for storing event data.
Definition Event.h:40
bool exists(const std::string &name, const std::string &passName, bool unique=true) const
Check for the existence of an object or collection with the given name and pass name in the event.
Definition Event.cxx:107
Class which represents the process under execution.
Definition Process.h:34
Class encapsulating parameters for configuring a processor.
Definition Parameters.h:26
const T & get(const std::string &name) const
Retrieve the parameter of the given name.
Definition Parameters.h:75
Performs basic HCal reconstruction.
double min_ampl_
Minimum amplitude to apply TOA correction.
double rate_dn_slope_
Rate of Down Slope in Pulse Shape [1/ns].
double time_dn_slope_
Time of Down Slope relative to Pulse Shape Fit [ns].
double clock_cycle_
Length of clock cycle [ns].
double voltage_per_mip_
Voltage by average MIP.
std::string rec_hit_coll_name_
output hit collection name
void configure(framework::config::Parameters &) override
Grabs configure parameters from the python config file.
double time_peak_
Time of Peak relative to pulse shape fit [ns].
TGraph correction_toa_
Correction to the measured TOA relative to the peak.
int n_adcs_
Depth of ADC buffer.
std::string input_pass_name_
Digi Pass Name to use as input.
HcalRecProducer(const std::string &name, framework::Process &process)
Constructor.
std::string sim_hit_coll_name_
simhit collection name
double rate_up_slope_
Rate of Up Slope in Pulse Shape [1/ns].
double mip_energy_
Energy [MeV] deposited by a MIP.
double attlength_
Strip attenuation length [m].
TGraph correction_ampl_
Correction to the pulse's measured amplitude at the peak.
void produce(framework::Event &event) override
Produce HcalHits and put them into the event bus using the HcalDigis as input.
std::string input_coll_name_
Digi Collection Name to use as input.
double time_up_slope_
Time of Up Slope relative to Pulse Shape Fit [ns].
TF1 pulse_func_
Pulse function.
double pe_per_mip_
PEs per MIP.
std::string sim_hit_pass_name_
simhit pass name
double min_ampl_fraction_
Minimum amplitude fraction to apply amplitude correction.
double getTOA(const ldmx::HgcrocDigiCollection::HgcrocDigi digi, double pedestal, unsigned int iSOI) const
Gets Time of Arrival with respect to the SOI.
static const std::string CONDITIONS_NAME
the name of the HcalReconConditions table (must match python registration name)
void setYPos(float ypos)
Set the Y position of the hit [mm].
void setID(int id)
Set the detector ID.
void setZPos(float zpos)
Set the Z position of the hit [mm].
void setXPos(float xpos)
Set the X position of the hit [mm].
void setTime(float time)
Set the time of the hit [ns].
void setAmplitude(float amplitude)
Set the amplitude of the hit, which is proportional to the signal in the calorimeter cell without sam...
void setEnergy(float energy)
Set the calorimetric energy of the hit, corrected for sampling factors [MeV].
Extension of HcalAbstractID providing access to HCal digi information.
Definition HcalDigiID.h:13
int strip() const
Get the value of the 'strip' field from the ID.
Definition HcalDigiID.h:99
bool isNegativeEnd() const
Get whether the 'end' field from the ID is negative.
Definition HcalDigiID.h:111
int section() const
Get the value of the 'section' field from the ID.
Definition HcalDigiID.h:75
int layer() const
Get the value of the layer field from the ID.
Definition HcalDigiID.h:81
static constexpr const char * CONDITIONS_OBJECT_NAME
Conditions object: The name of the python configuration calling this class (Hcal/python/HcalGeometry....
Stores reconstructed hit information from the HCAL.
Definition HcalHit.h:24
void setSection(int section)
Set the section for this hit.
Definition HcalHit.h:166
void setIsADC(int isADC)
Set if the hit is reconstructed using ADC.
Definition HcalHit.h:190
void setMinPE(float minpe)
Set the minimum number of photoelectrons estimated for this hit.
Definition HcalHit.h:160
void setOrientation(int orientation)
Set if the bar is orientied in X / Y / Z meanig 0 / 1 / 2, respectively.
Definition HcalHit.h:234
void setStrip(int strip)
Set the strip for this hit.
Definition HcalHit.h:178
void setLayer(int layer)
Set the layer for this hit.
Definition HcalHit.h:172
void setPE(float pe)
Set the number of photoelectrons estimated for this hit.
Definition HcalHit.h:153
Implements detector ids for HCal subdetector.
Definition HcalID.h:19
One DIGI signal coming from the HGC ROC.
HgcrocDigiCollection::Sample at(unsigned int i_sample) const
get the sample at a specific index in the digi
Represents a collection of the digi hits readout by an HGCROC.
unsigned int getNumDigis() const
Get total number of digis.
Stores simulated calorimeter hit information.