LDMX Software
EcalDigiProducer.cxx
Go to the documentation of this file.
1
10
11#include <set> //for tracking used detector IDs
12
14#include "DetDescr/EcalID.h"
18
19namespace ecal {
20
22 // settings of readout chip
23 // used in actual digitization
24 auto hgcroc_params = ps.get<framework::config::Parameters>("hgcroc");
25 hgcroc_ = std::make_unique<ldmx::HgcrocEmulator>(hgcroc_params);
26 clock_cycle_ = hgcroc_params.get<double>("clock_cycle");
27 n_adcs_ = hgcroc_params.get<int>("n_adcs");
28 i_soi_ = hgcroc_params.get<int>("i_soi");
29 noise_ = hgcroc_params.get<bool>("noise");
30
31 // collection names
32 input_coll_name_ = ps.get<std::string>("input_coll_name");
33 input_pass_name_ = ps.get<std::string>("input_pass_name");
34 digi_coll_name_ = ps.get<std::string>("digi_coll_name");
35
36 zero_suppression_ = ps.get<bool>("zero_suppression");
37
38 // physical constants
39 // used to calculate unit conversions
40 mev_ = ps.get<double>("mev");
41
42 // Time -> clock counts conversion
43 // time [ns] * ( 2^10 / max time in ns ) = clock counts
44 ns_ = 1024. / clock_cycle_;
45
46 readout_threshold_ = ps.get<double>("avg_readout_threshold");
47 pedestal_ = ps.get<double>("avg_pedestal");
48 noise_rms_ = ps.get<double>("avg_noise_rms");
49 time_phase_width_ = ps.get<double>("time_phase_width", 0.);
50}
51
53 // noise generator by default uses a Gausian model for noise
54 // i.e. It assumes the noise is distributed around a mean (setPedestal)
55 // with a certain RMS (setNoise) and then calculates
56 // how many hits_ should be generated for a given number of empty
57 // channels and a minimum readout value (setNoiseThreshold)
58 noise_generator_ = std::make_unique<ldmx::NoiseGenerator>();
59 // Configure generator that will produce noise hits_ in empty channels
60 // rms noise in mV
62 // mean noise amplitude (if using Gaussian Model for the noise) in mV
63 noise_generator_->setPedestal(pedestal_);
64 // threshold for readout in mV
65 noise_generator_->setNoiseThreshold(readout_threshold_);
66 // Set up seeds
69 noise_generator_->seedGenerator(
70 rseed.getSeed("EcalDigiProducer::NoiseGenerator"));
71 // Random number generator for layer_ / module_ / cell
72 rng_.seed(rseed.getSeed("EcalDigiProducer"));
73 // Setting up the read-out chip
74 hgcroc_->seedGenerator(rseed.getSeed("EcalDigiProducer::HgcrocEmulator"));
75 hgcroc_->condition(
77}
78
80 // Empty collection to be filled
82 ecal_digis.setNumSamplesPerDigi(n_adcs_);
84
85 // detector IDs that already have a hit in them
86 std::set<unsigned int> filled_det_i_ds;
87
88 /******************************************************************************************
89 * HGCROC Emulation on Simulated Hits
90 *****************************************************************************************/
91 // std::cout << "Sim Hits" << std::endl;
92 // get simulated ecal hits_ from Geant4
93 // the class EcalHitIO in the SimApplication module_ handles the translation
94 // from G4CalorimeterHits to SimCalorimeterHits this class ensures that only
95 // one SimCalorimeterHit is generated per cell, but multiple "contributions"
96 // are still handled within SimCalorimeterHit
97 auto ecal_sim_hits{event.getCollection<ldmx::SimCalorimeterHit>(
99
100 /* debug printout
101 std::cout << "Energy to Voltage Conversion: " << mev_ << " mV/MeV" <<
102 std::endl;
103 */
104
105 // event time relative to the clock, random for asynchronous triggers
106 double phase{0.};
107 if (time_phase_width_ > 0.) {
108 phase = std::uniform_real_distribution<double>(0., time_phase_width_)(rng_);
109 event.getEventHeader().setFloatParameter("ecal_digi_phase", phase);
110 }
111
112 for (auto const& sim_hit : ecal_sim_hits) {
113 std::vector<std::pair<double, double>> pulses_at_chip;
114 for (int i_contrib = 0; i_contrib < sim_hit.getNumberOfContribs();
115 i_contrib++) {
116 /* debug printout
117 std::cout << simHit.getContrib(iContrib).edep << " MeV" << std::endl;
118 */
125 pulses_at_chip.emplace_back(
126 sim_hit.getContrib(i_contrib).edep_ * mev_,
127 sim_hit.getContrib(i_contrib).time_ // global time (t=0ns at target)
128 - sim_hit.getPosition().at(2) /
129 299.702547 // shift light-speed particle traveling along z_
130 + phase);
131 }
132
133 unsigned int hit_id = sim_hit.getID();
134 filled_det_i_ds.insert(hit_id);
135
136 ldmx_log(debug) << " Emulation of hitID = " << hit_id
137 << " with energy = " << sim_hit.getEdep()
138 << " MeV at time = "
139 << sim_hit.getTime() -
140 sim_hit.getPosition().at(2) / 299.702547;
141
142 // container emulator uses to write out samples and
143 // transfer samples into the digi collection
144 std::vector<ldmx::HgcrocDigiCollection::Sample> digi_to_add;
145 if (hgcroc_->digitize(hit_id, pulses_at_chip, digi_to_add)) {
146 ldmx_log(debug) << " --> The HGCROC will read-out this hit!";
147 ecal_digis.addDigi(hit_id, digi_to_add);
148 }
149 }
150
151 /******************************************************************************************
152 * Noise Simulation on Empty Channels
153 *****************************************************************************************/
154 if (noise_) {
155 // put noise into some empty channels
156
157 // geometry constants
158 // These are used in the noise generation so that we can randomly
159 // distribute the noise uniformly throughout the ECal channels.
160 const auto& geom = getCondition<ldmx::EcalGeometry>(
161 ldmx::EcalGeometry::CONDITIONS_OBJECT_NAME);
162 int n_ecal_layers = geom.getNumLayers();
163 int n_modules_per_layer = geom.getNumModulesPerLayer();
164 int n_cells_per_module = geom.getNumCellsPerModule();
165 int num_empty_channels =
166 n_ecal_layers * n_modules_per_layer * n_cells_per_module -
167 ecal_digis.getNumDigis();
168
169 // Uniform distributions for integer generation
170 std::uniform_int_distribution<int> layer_dist(0, n_ecal_layers - 1);
171 std::uniform_int_distribution<int> module_dist(0, n_modules_per_layer - 1);
172 std::uniform_int_distribution<int> cell_dist(0, n_cells_per_module - 1);
173
174 if (zero_suppression_) {
175 // noise generator gives us a list of noise amplitudes [mV] that randomly
176 // populate the empty channels and are above the readout threshold
177 auto noise_hit_amplitudes{
178 noise_generator_->generateNoiseHits(num_empty_channels)};
179 std::vector<std::pair<double, double>> fake_pulse(1, {0., 0.});
180 for (double noise_hit : noise_hit_amplitudes) {
181 // generate detector ID for noise hit
182 // making sure that it is in an empty channel
183 unsigned int noise_id;
184 do {
185 int layer_id = layer_dist(rng_);
186 int module_id = module_dist(rng_);
187 int cell_id = cell_dist(rng_);
188 auto det_id = ldmx::EcalID(layer_id, module_id, cell_id);
189 noise_id = det_id.raw();
190 } while (filled_det_i_ds.find(noise_id) != filled_det_i_ds.end());
191 filled_det_i_ds.insert(noise_id);
192
193 // noise generator gives the amplitude above the readout threshold
194 // we need to convert it to the amplitude above the pedestal
195 noise_hit +=
196 hgcroc_->gain(noise_id) *
197 (hgcroc_->readoutThreshold(noise_id) - hgcroc_->pedestal(noise_id));
198
199 // create a digi as put it into the collection
200 ecal_digis.addDigi(noise_id, hgcroc_->noiseDigi(noise_id, noise_hit));
201 } // loop over noise amplitudes
202 } else {
203 // no zero suppression, put some noise emulation in **all** empty channels
204 // loop through all channels
205 for (int layer{0}; layer < n_ecal_layers; layer++) {
206 for (int module{0}; module < n_modules_per_layer; module++) {
207 for (int cell{0}; cell < n_cells_per_module; cell++) {
208 unsigned int channel{ldmx::EcalID(layer, module, cell).raw()};
209 // check if channel already has a (real) hit in it
210 if (filled_det_i_ds.find(channel) != filled_det_i_ds.end())
211 continue;
212 // create a digi as put it into the collection
213 ecal_digis.addDigi(channel, hgcroc_->noiseDigi(channel));
214 } // cells in each module_
215 } // modules in each layer_
216 } // layers in ECal
217 } // yes or no zero suppression
218 } // if we should do the noise
219
220 event.add(digi_coll_name_, ecal_digis);
221
222 return;
223} // produce
224
225} // namespace ecal
226
Class that performs basic ECal digitization.
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.
#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.
Conditions object for random number seeds.
Class which stores simulated calorimeter hit information.
Performs basic ECal digitization.
std::unique_ptr< ldmx::NoiseGenerator > noise_generator_
Generates noise hits based off of number of cells that are not hit.
std::string input_pass_name_
input pass name
double noise_rms_
Noise RMS.
bool noise_
Put noise into empty channels, not configurable, only helpful in development.
std::mt19937 rng_
Generates random numbers for which channels to fill up with noise.
double readout_threshold_
Read out threshold.
double time_phase_width_
width [ns] of the uniform random event phase, 0 for none
virtual void produce(framework::Event &event) override
Simulates measurement of pulse and creates digi collection for input event.
double pedestal_
Read out pedestal.
double mev_
Conversion from energy in MeV to voltage in mV.
virtual void configure(framework::config::Parameters &) override
Configure this producer from the python configuration.
int i_soi_
Index for the Sample Of Interest in the list of digi samples.
double clock_cycle_
Time interval for chip clock in ns.
double ns_
Conversion from time in ns to ticks of the internal clock.
std::string digi_coll_name_
output hit collection name
virtual void onNewRun(const ldmx::RunHeader &runHeader) override
Set up random number / noise generation.
int n_adcs_
Depth of ADC buffer.
std::string input_coll_name_
input hit collection name
std::unique_ptr< ldmx::HgcrocEmulator > hgcroc_
Hgcroc Emulator to digitize analog voltage signals.
bool zero_suppression_
When emulating noise in empty channels, do we zero suppress?
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
static const std::string CONDITIONS_OBJECT_NAME
Conditions object name.
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
RawValue raw() const
Definition DetectorID.h:69
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.
void setNumSamplesPerDigi(unsigned int n)
Set number of samples for each digi.
void setSampleOfInterestIndex(unsigned int n)
Set index of sample of interest.
void addDigi(unsigned int id, const std::vector< Sample > &digi)
Add samples to collection.
unsigned int getNumDigis() const
Get total number of digis.
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:68
Stores simulated calorimeter hit information.