LDMX Software
HcalDigiPipelineTest.cxx
1#include <catch2/catch_approx.hpp>
2#include <catch2/catch_test_macros.hpp>
3#include <catch2/matchers/catch_matchers.hpp>
4
5using Catch::Approx;
6
7#include "DetDescr/HcalID.h" //creating unique hcal IDs
8#include "Framework/ConfigurePython.h"
10#include "Framework/Process.h"
11#include "Hcal/Event/HcalHit.h"
14
15namespace hcal {
16namespace test {
17
24static const double PE_ENERGY = 4.66 / 68; // 0.069 MeV
25
43// static const double MAX_ENERGY_ERROR_DAQ = 4 * PE_ENERGY;
44// static const double MAX_ENERGY_PERCENT_ERROR_DAQ = 0.2;
45static const double MAX_PE_ERROR_DAQ = 40;
46// large percentage error for now
47static const double MAX_PE_PERCENT_ERROR_DAQ = 0.4;
48
59static const int NUM_TEST_SIM_HITS = 1000;
60
66class isCloseEnough : public Catch::Matchers::MatcherBase<double> {
67 private:
69 double truth_;
70
72 const double max_absolute_diff_;
73
75 const double max_relative_diff_;
76
77 public:
83 isCloseEnough(double const &truth, double const &abs_diff,
84 double const &rel_diff)
85 : truth_{truth},
86 max_absolute_diff_{abs_diff},
87 max_relative_diff_{rel_diff} {}
88
96 bool match(const double &daq) const override {
97 return (daq == Approx(truth_).epsilon(max_relative_diff_) or
98 daq == Approx(truth_).margin(max_absolute_diff_));
99 }
100
104 virtual std::string describe() const override {
105 std::ostringstream ss;
106 ss << "is within an absolute difference of " << max_absolute_diff_
107 << " OR a relative difference of " << max_relative_diff_ << " with "
108 << truth_;
109 return ss.str();
110 }
111};
112
125 // Based on the current gain settings for the ADC readout mode
126 // we will reach saturation ~ 20 MeV ~ 290 PEs
127 const double maxEnergy_ = 200 * PE_ENERGY; // ~ 13 MeV
128
133 const double minEnergy_ = 4 * PE_ENERGY;
139 const double energyStep_ = (maxEnergy_ - minEnergy_) / NUM_TEST_SIM_HITS;
140
143
144 public:
145 HcalFakeSimHits(const std::string &name, framework::Process &p)
146 : framework::Producer(name, p) {}
148
149 void beforeNewRun(ldmx::RunHeader &header) final override {
150 header.setDetectorName("ldmx-det-v12");
151 }
152
153 void produce(framework::Event &event) final override {
154 // put in a single sim hit
155 std::vector<ldmx::SimCalorimeterHit> pretendSimHits(1);
156
157 // We hard-code the position of one hit: back hcal, layer 1, strip 31
158 // This real simHit position is obtained by looking at calorimeter
159 // SimHits of a 4 GeV muon shoot through the beamline
160 ldmx::HcalID id(0, 1, 31);
161 pretendSimHits[0].setPosition(-6.70265, 3.70265, 879); // mm
162 pretendSimHits[0].setID(id.raw());
163 pretendSimHits[0].addContrib(
164 -1, // incidentID
165 -1, // trackID
166 0, // pdg ID
167 currEnergy_, // edep
168 2.96628 // time - 299mm is about 1ns from target and in middle of HCal
169 );
170
171 // needs to be correct collection name
172 // REQUIRE_NOTHROW(event.add("HcalSimHits", pretendSimHits));
173 REQUIRE_NOTHROW(event.add("HcalFakeSimHits", pretendSimHits));
175
176 return;
177 }
178}; // HcalFakeSimHits
179
193 // save ntuple? False by default because if ntuplizer is on, the HcalGeometry
194 // test cannot be run
195 const bool save_ = false;
196
197 public:
198 HcalCheckReconstruction(const std::string &name, framework::Process &p)
199 : framework::Analyzer(name, p) {}
201
202 void onProcessStart() final override {
203 if (save_) {
205 ntuple_.create("HcalDigiTest");
206 ntuple_.addVar<float>("HcalDigiTest", "SimEnergy");
207 ntuple_.addVar<float>("HcalDigiTest", "RecEnergy");
208 ntuple_.addVar<float>("HcalDigiTest", "SimX");
209 ntuple_.addVar<float>("HcalDigiTest", "SimY");
210 ntuple_.addVar<float>("HcalDigiTest", "SimZ");
211 ntuple_.addVar<float>("HcalDigiTest", "SimTime");
212 ntuple_.addVar<float>("HcalDigiTest", "RecX");
213 ntuple_.addVar<float>("HcalDigiTest", "RecY");
214 ntuple_.addVar<float>("HcalDigiTest", "RecZ");
215 ntuple_.addVar<float>("HcalDigiTest", "RecTime");
216 ntuple_.addVar<int>("HcalDigiTest", "DaqDigi");
217 ntuple_.addVar<int>("HcalDigiTest", "DaqDigiIsADC");
218 ntuple_.addVar<int>("HcalDigiTest", "DaqDigiADC");
219 ntuple_.addVar<int>("HcalDigiTest", "DaqDigiTOT");
220 }
221 }
222
223 void analyze(const framework::Event &event) final override {
224 const auto simHits =
225 event.getCollection<ldmx::SimCalorimeterHit>("HcalFakeSimHits");
226
227 REQUIRE(simHits.size() == 1);
228
229 float truth_energy = simHits.at(0).getEdep();
230
231 if (save_) {
232 ntuple_.setVar<float>("SimEnergy", truth_energy);
233 ntuple_.setVar<float>("SimX", simHits.at(0).getPosition()[0]);
234 ntuple_.setVar<float>("SimY", simHits.at(0).getPosition()[1]);
235 ntuple_.setVar<float>("SimZ", simHits.at(0).getPosition()[2]);
236 ntuple_.setVar<float>("SimTime", simHits.at(0).getContrib(0).time);
237 }
238
239 const auto daqDigis{
240 event.getObject<ldmx::HgcrocDigiCollection>("HcalDigis")};
241 auto daqDigi = daqDigis.getDigi(0);
242 bool is_in_adc_mode = daqDigi.isADC();
243
244 if (save_) {
245 ntuple_.setVar<int>("DaqDigi", daqDigi.soi().raw());
246 ntuple_.setVar<int>("DaqDigiIsADC", is_in_adc_mode);
247 ntuple_.setVar<int>("DaqDigiADC", daqDigi.soi().adc_t());
248 ntuple_.setVar<int>("DaqDigiTOT", daqDigi.tot());
249 }
250
251 const auto recHits = event.getCollection<ldmx::HcalHit>("HcalRecHits");
252 CHECK(recHits.size() == 1);
253
254 auto hit = recHits.at(0);
255 ldmx::HcalID id(hit.getID());
256 CHECK_FALSE(hit.isNoise());
257 CHECK(id.raw() == simHits.at(0).getID());
258
259 if (save_) {
260 ntuple_.setVar<float>("RecX", hit.getXPos());
261 ntuple_.setVar<float>("RecY", hit.getYPos());
262 ntuple_.setVar<float>("RecZ", hit.getZPos());
263 ntuple_.setVar<float>("RecTime", hit.getTime());
264 ntuple_.setVar<float>("RecPE", hit.getPE());
265 ntuple_.setVar<float>("RecEnergy", hit.getEnergy());
266 }
267
268 // define target pe by using the settings at the top
269 double daq_pe{hit.getPE()};
270 CHECK_THAT(daq_pe, isCloseEnough(truth_energy / PE_ENERGY, MAX_PE_ERROR_DAQ,
271 MAX_PE_PERCENT_ERROR_DAQ));
272
273 // std::cout << "rec energy " << hit.getEnergy() << " * approx sampl
274 // fraction " << hit.getEnergy()*sampling_fraction << " truth " <<
275 // truth_energy
276 // << std::endl;
277 // std::cout << "npes " << hit.getPE() << " approx PE " << int(truth_energy
278 // / PE_ENERGY) << std::endl;
279 /*
280 if (id.section() == 0) {
281 double truth_pos, rec_pos;
282 if ((id.layer() % 2) == 1) {
283 truth_pos = simHits.at(0).getPosition()[0];
284 rec_pos = hit.getXPos();
285 } else {
286 truth_pos = simHits.at(0).getPosition()[1];
287 rec_pos = hit.getYPos();
288 }
289 // std::cout << "rec pos " << rec_pos << " truth " << truth_pos <<
290 // std::endl;
291 // comment position check for now
292 // CHECK_THAT(rec_pos, isCloseEnough(truth_pos,
293 MAX_POSITION_ERROR_DAQ,
294 // MAX_POSITION_PERCENT_ERROR_DAQ));
295 }
296 */
297 return;
298 }
299}; // HcalCheckReconstruction
300
301} // namespace test
302} // namespace hcal
303
304DECLARE_PRODUCER_NS(hcal::test, HcalFakeSimHits)
305DECLARE_ANALYZER_NS(hcal::test, HcalCheckReconstruction)
306
307
321TEST_CASE("Hcal Digi Pipeline test", "[Hcal][functionality]") {
322 const std::string config_file{"hcal_digi_pipeline_test_config.py"};
323
324 char **args{nullptr};
326
327 framework::ConfigurePython cfg(config_file, args, 0);
328 REQUIRE_NOTHROW(p = cfg.makeProcess());
329 p->run();
330}
Base classes for all user event processing components to extend.
#define DECLARE_ANALYZER_NS(NS, CLASS)
Macro which allows the framework to construct an analyzer given its name during configuration.
#define DECLARE_PRODUCER_NS(NS, CLASS)
Macro which allows the framework to construct a producer given its name during configuration.
Class that stores Stores reconstructed hit information from the HCAL.
Class that defines an HCal sensitive detector.
Class that represents a digitized hit in a calorimeter cell readout by an HGCROC.
Class which represents the process under execution.
Class which stores simulated calorimeter hit information.
Base class for a module which does not produce a data product.
Utility class which reads/executes a python script and creates a Process object based on the input.
NtupleManager & ntuple_
Manager for any ntuples.
TDirectory * getHistoDirectory()
Access/create a directory in the histogram file for this event processor to create histograms and ana...
Implements an event buffer system for storing event data.
Definition Event.h:41
void addVar(const std::string &tname, const std::string &vname)
Add a variable of type VarType to the ROOT tree with name 'tname'.
void create(const std::string &tname)
Create a ROOT tree to hold the ntuple variables (ROOT leaves).
void setVar(const std::string &vname, const T &value)
Set the value of the variable named 'vname'.
Class which represents the process under execution.
Definition Process.h:36
Base class for a module which produces a data product.
Producer(const std::string &name, Process &process)
Class constructor.
void analyze(const framework::Event &event) final override
Process the event and make histograms or summaries.
void onProcessStart() final override
Callback for the EventProcessor to take any necessary action when the processing of events starts,...
const double maxEnergy_
Maximum energy to make a simulated hit for [MeV].
void beforeNewRun(ldmx::RunHeader &header) final override
Handle allowing producers to modify run headers before the run begins.
void produce(framework::Event &event) final override
Process the event and put new data products into it.
double currEnergy_
current energy of the sim hit we are on
const double minEnergy_
Minimum energy to make a sim hit for [MeV] Needs to be above readout threshold (after internal HcalDi...
const double energyStep_
The step between energies is calculated depending on the min, max energy and the total number of sim ...
Our custom checker which makes sure that the input energy/position is "close enough" to the truth ene...
const double max_absolute_diff_
maximum absolute difference
bool match(const double &daq) const override
Performs the test for this matcher.
isCloseEnough(double const &truth, double const &abs_diff, double const &rel_diff)
Constructor.
double truth_
correct (sim-level)
virtual std::string describe() const override
Describes matcher for printing to terminal.
const double max_relative_diff_
maximum relative difference
Stores reconstructed hit information from the HCAL.
Definition HcalHit.h:23
Implements detector ids for HCal subdetector.
Definition HcalID.h:19
bool isADC() const
Check if this DIGI is an ADC measurement.
Represents a collection of the digi hits readout by an HGCROC.
const HgcrocDigi getDigi(unsigned int digiIndex) const
Get samples for the input digi index.
Run-specific configuration and data stored in its own output TTree alongside the event TTree in the o...
Definition RunHeader.h:54
Stores simulated calorimeter hit information.
All classes in the ldmx-sw project use this namespace.
Definition PerfDict.cxx:45
std::unique_ptr< Process > ProcessHandle
A handle to the current process Used to pass a process from ConfigurePython to fire....
Definition Process.h:248