LDMX Software
EcalRecProducer.cxx
Go to the documentation of this file.
1
8
10#include "DetDescr/EcalID.h"
11#include "Ecal/EcalReconConditions.h"
12#include "Ecal/Event/EcalHit.h"
15
16namespace ecal {
17
18EcalRecProducer::EcalRecProducer(const std::string& name,
19 framework::Process& process)
20 : Producer(name, process) {}
21
23 // collection names
24 digi_coll_name_ = ps.get<std::string>("digi_coll_name");
25 digi_pass_name_ = ps.get<std::string>("digi_pass_name");
26 sim_hit_coll_name_ = ps.get<std::string>("sim_hit_coll_name");
27 sim_hit_pass_name_ = ps.get<std::string>("sim_hit_pass_name");
28 rec_hit_coll_name_ = ps.get<std::string>("rec_hit_coll_name");
29
30 layer_weights_ = ps.get<std::vector<double>>("layer_weights");
32 ps.get<double>("second_order_energy_correction");
33
34 mip_si_energy_ = ps.get<double>("mip_si_energy");
35 clock_cycle_ = ps.get<double>("clock_cycle");
36 charge_per_mip_ = ps.get<double>("charge_per_mip");
37}
38
40 // Get the Ecal Geometry
41 const auto& geometry = getCondition<ldmx::EcalGeometry>(
42 ldmx::EcalGeometry::CONDITIONS_OBJECT_NAME);
43
44 // Safety check: ensure layer_weights_ covers all geometry layers.
45 // A mismatch here typically means an outdated geometry configuration
46 // (e.g. v14) is being used with layer weights for a different geometry
47 // version (e.g. v15).
48 if (geometry.getNumLayers() > static_cast<int>(layer_weights_.size())) {
49 EXCEPTION_RAISE(
50 "InvalidConfig",
51 "The number of layers in the Ecal geometry (" +
52 std::to_string(geometry.getNumLayers()) +
53 ") exceeds the number of configured layer_weights (" +
54 std::to_string(layer_weights_.size()) +
55 "). This is likely caused by a mismatch between the geometry "
56 "version and the reconstruction configuration. Please ensure "
57 "that the correct geometry is being used.");
58 }
59
60 // Get the reconstruction parameters
61 EcalReconConditions the_conditions(
64
65 std::vector<ldmx::EcalHit> ecal_rec_hits;
66 auto ecal_digis = event.getObject<ldmx::HgcrocDigiCollection>(
68 const int i_soi = ecal_digis.getSampleOfInterestIndex();
69 // loop through digis
70 for (auto digi : ecal_digis) {
71 // ID from first digi sample
72 // assuming rest of samples have same ID
73 ldmx::EcalID id(digi.id());
74
75 // ID to real space position
76 auto [x_, y_, z_] = geometry.getPosition(id);
77
78 // the TOT measurement is in the sample where the pulse fell back below
79 // threshold, which is the SOI only for in-time hits (iss #1944)
80 int i_tot = digi.totSampleIndex();
81
82 // get the estimated charge deposited from digi samples
83 double charge(0.);
84 // TOA with respect to the 25ns clock window that measured it
85 double hit_time(0.);
86
87 if (i_tot >= 0) {
88 // TOT - number of clock ticks that pulse was over threshold
89 // this is related to the amplitude of the pulse approximately through a
90 // linear drain rate the amplitude of the pulse is related to the energy
91 // deposited
92 auto tot_sample = digi.at(i_tot);
93
94 // convert the time over threshold into a total energy deposited in the
95 // silicon
96 // (time over threshold [ns] - pedestal) * gain
97 charge = (tot_sample.tot() - the_conditions.totPedestal(id)) *
98 the_conditions.totGain(id);
99
100 // TOA is measured from the start of its own sample
101 hit_time = (i_tot - i_soi) * clock_cycle_ +
102 tot_sample.toa() * (clock_cycle_ / 1024);
103
104 ldmx_log(trace) << "Recon { TOA: " << hit_time << " ns } ";
105 ldmx_log(trace) << "TOT Mode (sample " << i_tot << ") -> "
106 << tot_sample.tot() << "TDC -> " << charge << " fC";
107 } else {
108 hit_time = digi.soi().toa() * (clock_cycle_ / 1024); // ns
109 ldmx_log(trace) << "Recon { TOA: " << hit_time << " ns } ";
110
111 // ADC mode of readout
112 // ADC - voltage measurement at a specific time of the pulse
113 // Pulse Shape:
114 // p[0]/(1.0+exp(p[1](t-p[2]+p[3]-p[4])))/(1.0+exp(p[5]*(t-p[6]+p[3]-p[4])))
115 // p[0] = amplitude to be fit (TBD)
116 // p[1] = -0.345 shape parameter - rate of up slope
117 // p[2] = 70.6547 shape parameter - time of up slope relative to shape
118 // fit p[3] = 77.732 shape parameter - time of peak relative to shape fit
119 // p[4] = peak time to be fit (TBD)
120 // p[5] = 0.140068 shape parameter - rate of down slope
121 // p[6] = 87.7649 shape paramter - time of down slope relative to shape
122 // fit
123 // These measurements can be used to fit the pulse shape if TOT is not
124 // available. For now, we simply take the measurement of the SOI as the
125 // peak amplitude.
126
127 charge = (digi.soi().adcT() - the_conditions.adcPedestal(id)) *
128 the_conditions.adcGain(id);
129
130 ldmx_log(trace) << "ADC Mode -> " << charge << " fC";
131 }
132
145 if (charge <= 0) continue;
146
147 double num_mips_equivalent = charge / charge_per_mip_;
148 double energy_deposited_in_si = num_mips_equivalent * mip_si_energy_;
149
150 ldmx_log(trace) << " -> " << num_mips_equivalent << " equiv MIPs -> "
151 << energy_deposited_in_si << " MeV";
152
153 // incorporate layer_ weights
154 double reconstructed_energy =
155 (num_mips_equivalent *
157 id.layer()) // energy lost in non-sensitive layers
158 + energy_deposited_in_si // energy deposited in Si itself
159 ) *
161
162 // copy over information to rec hit structure in new collection
163 ldmx::EcalHit rec_hit;
164 rec_hit.setID(id.raw());
165 rec_hit.setXPos(x_);
166 rec_hit.setYPos(y_);
167 rec_hit.setZPos(z_);
168 rec_hit.setAmplitude(energy_deposited_in_si);
169 rec_hit.setEnergy(reconstructed_energy);
170 rec_hit.setTime(hit_time);
171
172 ecal_rec_hits.push_back(rec_hit);
173 }
174
176 // ecal sim hits_ exist ==> label which hits_ are real and which are pure
177 // noise
178 auto ecal_sim_hits{event.getCollection<ldmx::SimCalorimeterHit>(
180 std::set<int> real_hits;
181 for (auto const& sim_hit : ecal_sim_hits) real_hits.insert(sim_hit.getID());
182 for (auto& hit : ecal_rec_hits)
183 hit.setNoise(real_hits.find(hit.getID()) == real_hits.end());
184 }
185
186 // add collection to event bus
187 event.add(rec_hit_coll_name_, ecal_rec_hits);
188}
189
190} // namespace ecal
191
Class that translates raw positions of ECal module hits into cells in a hexagonal readout.
Class that defines an ECal detector ID with a cell number.
Class that performs basic ECal digitization.
#define DECLARE_PRODUCER(CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that represents a digitized hit in a calorimeter cell readout by an HGCROC.
Class which stores simulated calorimeter hit information.
Performs basic ECal reconstruction.
double mip_si_energy_
Energy [MeV] deposited by a MIP in Si 0.5mm thick.
std::string sim_hit_coll_name_
simhit collection name
std::vector< double > layer_weights_
Layer Weights to use for this reconstruction.
double second_order_energy_correction_
Second Order Energy Correction to use for this reconstruction.
double charge_per_mip_
Number of electrons generated by average MIP in Si 0.5mm thick.
std::string rec_hit_coll_name_
output hit collection name
EcalRecProducer(const std::string &name, framework::Process &process)
Constructor.
virtual void produce(framework::Event &event)
Produce EcalHits and put them into the event bus using the EcalDigis as input.
std::string digi_pass_name_
Digi Pass Name to use as input.
virtual void configure(framework::config::Parameters &)
Grabs configure parameters from the python config file.
double clock_cycle_
Length of clock cycle [ns].
std::string sim_hit_pass_name_
simhit pass name
std::string digi_coll_name_
Digi Collection Name to use as input.
Class to wrap around an double table of conditions.
double adcPedestal(const ldmx::EcalID &id) const
get the ADC pedestal
double adcGain(const ldmx::EcalID &id) const
get the ADC gain
double totPedestal(const ldmx::EcalID &id) const
get the TOT pedestal
static const std::string CONDITIONS_NAME
the name of the EcalReconConditions table (must match python registration name)
double totGain(const ldmx::EcalID &id) const
get the TOT gain
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
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].
Stores reconstructed hit information from the ECAL.
Definition EcalHit.h:19
Extension of DetectorID providing access to ECal layers and cell numbers in a hex grid.
Definition EcalID.h:20
Represents a collection of the digi hits readout by an HGCROC.
unsigned int getSampleOfInterestIndex() const
Get index of sample of interest.
Stores simulated calorimeter hit information.