diff --git a/include/Hcal/Event/HcalCluster.h b/include/Hcal/Event/HcalCluster.h index 98e229b..b2cb1ae 100644 --- a/include/Hcal/Event/HcalCluster.h +++ b/include/Hcal/Event/HcalCluster.h @@ -30,7 +30,7 @@ class HcalCluster { /** * Class constructor. */ - HcalCluster(); + HcalCluster() = default; /** * Class destructor. diff --git a/include/Hcal/Event/HcalHit.h b/include/Hcal/Event/HcalHit.h index 66f5eb5..9faafae 100644 --- a/include/Hcal/Event/HcalHit.h +++ b/include/Hcal/Event/HcalHit.h @@ -25,12 +25,12 @@ class HcalHit : public ldmx::CalorimeterHit { /** * Class constructor. */ - HcalHit() {} + HcalHit() = default; /** * Class destructor. */ - virtual ~HcalHit() {} + virtual ~HcalHit() = default; /** * Clear the data in the object. @@ -188,11 +188,14 @@ class HcalHit : public ldmx::CalorimeterHit { /** * Set original position */ - void setPositionUnchanged(double position, int isX) { position_ = position; isX_ = isX; } + void setPositionUnchanged(double position, int isX) { + position_ = position; + isX_ = isX; + } - double getPosition() const { return position_;} - int getIsX() const { return isX_;} - double getTimeDiff() const { return timeDiff_;} + double getPosition() const { return position_; } + int getIsX() const { return isX_; } + double getTimeDiff() const { return timeDiff_; } private: /** The number of PE estimated for this hit. */ diff --git a/include/Hcal/Event/HcalVetoResult.h b/include/Hcal/Event/HcalVetoResult.h index 922887d..d9f31fc 100644 --- a/include/Hcal/Event/HcalVetoResult.h +++ b/include/Hcal/Event/HcalVetoResult.h @@ -23,10 +23,10 @@ namespace ldmx { class HcalVetoResult { public: /** Constructor */ - HcalVetoResult(); + HcalVetoResult() = default; /** Destructor */ - ~HcalVetoResult(); + virtual ~HcalVetoResult() = default; /** Reset the object. */ void Clear(); @@ -38,14 +38,14 @@ class HcalVetoResult { bool passesVeto() const { return passesVeto_; }; /** @return The maximum PE HcalHit. */ - inline ldmx::HcalHit getMaxPEHit() const { return maxPEHit_; } + ldmx::HcalHit getMaxPEHit() const { return maxPEHit_; } /** * Sets whether the Hcal veto was passed or not. * * @param passesVeto Veto result. */ - inline void setVetoResult(const bool& passesVeto = true) { + void setVetoResult(const bool& passesVeto = true) { passesVeto_ = passesVeto; } @@ -54,9 +54,7 @@ class HcalVetoResult { * * @param maxPEHit The maximum PE HcalHit */ - inline void setMaxPEHit(const ldmx::HcalHit maxPEHit) { - maxPEHit_ = maxPEHit; - } + void setMaxPEHit(const ldmx::HcalHit maxPEHit) { maxPEHit_ = maxPEHit; } private: /** Reference to max PE hit. */ diff --git a/include/Hcal/HcalAlignPolarfires.h b/include/Hcal/HcalAlignPolarfires.h new file mode 100644 index 0000000..00f17f5 --- /dev/null +++ b/include/Hcal/HcalAlignPolarfires.h @@ -0,0 +1,59 @@ +#ifndef HCALALIGNPOLARFIRES_H +#define HCALALIGNPOLARFIRES_H +#include + +#include "Framework/EventProcessor.h" +#include "Recon/Event/HgcrocDigiCollection.h" + +namespace hcal { +/** + * Align the two polarfires with drop/keep hints signalling successful merge + * + * - Only checking for /dropped/ events + * - assuming that ticks and spills are already in correct ORDER + * - assuming spill numbering is NOT the same between the two DPMs + */ +class HcalAlignPolarfires : public framework::Producer { + /// input decoded objects (vector index == polarfire index) + std::vector input_names_; + /// pass name for decoded objects + std::string input_pass_; + /// output object name + std::string output_name_; + /// number of 5MHz ticks difference to consider polarfires aligned + static int max_tick_diff_; + + public: + struct PolarfireQueueEntry { + /// the i'th spill + int spill; + /// ticks since spill + int ticks; + ldmx::HgcrocDigiCollection digis; + PolarfireQueueEntry(const framework::Event& event, + const std::string& input_name, + const std::string& input_pass, + std::pair& spill_counter); + bool same_event(const PolarfireQueueEntry& rhs) { + return (spill == rhs.spill and abs(ticks - rhs.ticks) < max_tick_diff_); + } + bool earlier_event(const PolarfireQueueEntry& rhs) { + if (spill == rhs.spill) return ticks < rhs.ticks; + return spill < rhs.spill; + } + }; + /// queue of unmatched digis + std::queue pf0_queue, pf1_queue; + /// spill counter + std::pair pf0_spill_counter{0, -1}, pf1_spill_counter{0, -1}; + + public: + HcalAlignPolarfires(const std::string& n, framework::Process& p) + : framework::Producer(n, p) {} + virtual ~HcalAlignPolarfires() = default; + void configure(framework::config::Parameters& ps) override; + void produce(framework::Event& event) override; +}; + +} // namespace hcal +#endif /* HCALALIGNPOLARFIRES_H */ diff --git a/include/Hcal/HcalClusterProducer.h b/include/Hcal/HcalClusterProducer.h index 9fd8240..07488b7 100644 --- a/include/Hcal/HcalClusterProducer.h +++ b/include/Hcal/HcalClusterProducer.h @@ -42,9 +42,9 @@ class HcalClusterProducer : public framework::Producer { * * @param parameters Set of parameters used to configure this processor. */ - void configure(framework::config::Parameters& parameters) final override; + void configure(framework::config::Parameters& parameters) override; - virtual void produce(framework::Event& event); + void produce(framework::Event& event) override; private: bool verbose_{false}; diff --git a/include/Hcal/HcalDigiProducer.h b/include/Hcal/HcalDigiProducer.h index 79579a0..21a9169 100644 --- a/include/Hcal/HcalDigiProducer.h +++ b/include/Hcal/HcalDigiProducer.h @@ -35,19 +35,19 @@ class HcalDigiProducer : public framework::Producer { HcalDigiProducer(const std::string& name, framework::Process& process); /// Default destructor - ~HcalDigiProducer() = default; + virtual ~HcalDigiProducer() = default; /** * Configure this producer from the python configuration. * Sets event constants and configures the noise generator, noise injector, * and pulse function. Creates digi collection */ - void configure(framework::config::Parameters&) final override; + void configure(framework::config::Parameters&) override; /** * Simulates measurement of pulse and creates digi collection for input event. */ - void produce(framework::Event& event) final override; + void produce(framework::Event& event) override; private: /////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/Hcal/HcalDoubleEndRecProducer.h b/include/Hcal/HcalDoubleEndRecProducer.h new file mode 100644 index 0000000..64cfc9d --- /dev/null +++ b/include/Hcal/HcalDoubleEndRecProducer.h @@ -0,0 +1,44 @@ +#ifndef HCALDOUBLEENDRECPRODUCER_H +#define HCALDOUBLEENDRECPRODUCER_H + +#include "Conditions/SimpleTableCondition.h" +#include "DetDescr/DetectorID.h" +#include "DetDescr/HcalDigiID.h" +#include "DetDescr/HcalGeometry.h" +#include "DetDescr/HcalID.h" +#include "Framework/EventDef.h" +#include "Framework/EventProcessor.h" +#include "Hcal/HcalReconConditions.h" +#include "Recon/Event/HgcrocDigiCollection.h" +namespace hcal { + +class HcalDoubleEndRecProducer : public framework::Producer { + private: + /// name of pass of rechits to use + std::string pass_name_{""}; + /// name of rechits to use as input + std::string coll_name_{"HcalRecHits"}; + /// name of pass of rechits to reconstruct + std::string rec_pass_name_{""}; + /// name of rechits to reconstruct + std::string rec_coll_name_{"HcalRecHitsDoubleEnd"}; + + /// number of PEs per MIP + double pe_per_mip_; + /// energy per MIP [MeV] + double mip_energy_; + /// length of clock cycle [ns] + double clock_cycle_; + + public: + HcalDoubleEndRecProducer(const std::string& n, framework::Process& p) + : Producer(n, p) {} + + virtual ~HcalDoubleEndRecProducer() = default; + void configure(framework::config::Parameters& p) override; + void produce(framework::Event& event) override; + +}; // HcalDoubleEndRecProducer +} // namespace hcal + +#endif /* HCALDOUBLEENDRECPRODUCER_H */ diff --git a/include/Hcal/HcalPedestalAnalyzer.h b/include/Hcal/HcalPedestalAnalyzer.h new file mode 100644 index 0000000..4fc4941 --- /dev/null +++ b/include/Hcal/HcalPedestalAnalyzer.h @@ -0,0 +1,63 @@ +#ifndef HCALPEDESTALANALYZER_H +#define HCALPEDESTALANALYZER_H + +#include "DetDescr/HcalDigiID.h" +#include "Framework/EventProcessor.h" +#include "Recon/Event/HgcrocDigiCollection.h" +namespace hcal { + +class HcalPedestalAnalyzer : public framework::Analyzer { + std::string input_name_, input_pass_; + std::string output_file_, comments_; + bool make_histos_; + bool filter_noTOT; + bool filter_noTOA; + int low_cutoff_, high_cutoff_; + + struct Channel { + Channel() : hist{0}, sum{0}, sum_sq{0}, entries{0}, rejects{4, 0} {} + /// collection of hits accumulated to produce appropriately-binned + /// histograms + std::vector adcs; + /// Histogram, if used + TH1* hist; + /// Sum of values + uint64_t sum; + /// Sum of values squared + double sum_sq; + /// Number of entries + int entries; + /// counts of various rejections + std::vector rejects; + }; + + std::map pedestal_data_; + + void create_and_fill(Channel& chan, ldmx::HcalDigiID detid); + + public: + HcalPedestalAnalyzer(const std::string& n, framework::Process& p) + : framework::Analyzer(n, p) {} + virtual ~HcalPedestalAnalyzer() = default; + + void configure(framework::config::Parameters& ps) override { + input_name_ = ps.getParameter("input_name"); + input_pass_ = ps.getParameter("input_pass"); + output_file_ = ps.getParameter("output_file"); + comments_ = ps.getParameter("comments"); + + make_histos_ = ps.getParameter("make_histos", false); + + filter_noTOT = ps.getParameter("filter_noTOT", true); + filter_noTOA = ps.getParameter("filter_noTOA", true); + low_cutoff_ = ps.getParameter("low_cutoff", 10); + high_cutoff_ = ps.getParameter("high_cutoff", 512); + } + + void analyze(const framework::Event& event) override; + void onProcessEnd() override; +}; + +} // namespace hcal + +#endif /* HCALPEDESTALANALYZER_H */ diff --git a/include/Hcal/HcalRawDecoder.h b/include/Hcal/HcalRawDecoder.h new file mode 100644 index 0000000..83106e9 --- /dev/null +++ b/include/Hcal/HcalRawDecoder.h @@ -0,0 +1,420 @@ +#ifndef HCALRAWDECODER_H +#define HCALRAWDECODER_H + +#include +#include +#include + +#include "DetDescr/HcalElectronicsID.h" +#include "DetDescr/HcalID.h" +#include "Framework/EventProcessor.h" +#include "Hcal/HcalDetectorMap.h" +#include "Packing/Utility/CRC.h" +#include "Packing/Utility/Mask.h" +#include "Packing/Utility/Reader.h" +#include "Recon/Event/HgcrocDigiCollection.h" +namespace hcal { + +struct PolarfireEventHeader { + /// version of daq format + int version; + /// id for polarfire + int fpga; + /// number of samples + int nsamples; + /// spill number + int spill; + /// number of 5MHz ticks since spill + int ticks; + /// bunch number according to this polarfire + int bunch; + /// event number according to this polarfire + int number; + /// run number according to this polarfire + int run; + /// day of month run started + int DD; + /// month run started + int MM; + /// hour run started + int hh; + /// minute run started + int mm; + /// quality of link headers + std::vector good_bxheader; + /// quality of link trailers + std::vector good_trailer; + + /** + * board us onto the bus with the input prefix + */ + void board(framework::Event& event, const std::string& prefix); +}; + +/** + * @class HcalRawDecoder + */ +class HcalRawDecoder : public framework::Producer { + public: + HcalRawDecoder(const std::string& name, framework::Process& process) + : framework::Producer(name, process) {} + virtual ~HcalRawDecoder() = default; + void configure(framework::config::Parameters&) override; + /// add detector name if we are reading from file + void beforeNewRun(ldmx::RunHeader& rh) override; + /// use read function to decode data, then translate EIDs into DetIDs + void produce(framework::Event& event) override; + + private: + /** + * Assume input reader behaves like a binary data input stream + * where we can "pop" individual 32-bit words with operator>> + * and we can check if the reader is done using operator bool() + * + * Note: Contains quite a lot of debug details and variables that aren't + * actually used but should be kept around. Could possibly be cleaned up at + * some point but for now, best to leave alone + */ + template + std::map> + read(ReaderType& reader, PolarfireEventHeader& eh) { + /** + * Static parameters depending on ROC version + */ + static const unsigned int common_mode_channel = roc_version_ == 2 ? 19 : 1; + static const unsigned int calib_channel = 20; + /// words for reading and decoding + static uint32_t head1, head2, w; + + // special header words not counted in event length + do { + reader >> head1; + } while (head1 != 0xbeef2021 and head1 != 0xbeef2022); + + /** + * Decode event header + */ + long int eventlen; + long int i_event{0}; + /* whole event header word looks like + * + * VERSION (4) | FPGA ID (8) | NSAMPLES (4) | LEN (16) + */ + reader >> head1; + i_event++; + + eh.version = (head1 >> 28) & packing::utility::mask<4>; + eh.fpga = (head1 >> 20) & packing::utility::mask<8>; + eh.nsamples = (head1 >> 16) & packing::utility::mask<4>; + eventlen = head1 & packing::utility::mask<16>; + if (eh.version == 1u) { + // eventlen is 32-bit words in event + // do nothing here + } else if (eh.version == 2u) { + // eventlen is 64-bit words in event, + // need to multiply by 2 to get actual 32-bit event length + eventlen *= 2; + // and subtract off the special header word above + eventlen -= 1; + } else { + EXCEPTION_RAISE( + "VersMis", + "HcalRawDecoder only knows version 1 and 2 of DAQ format."); + } + // sample counters + int n_words{0}; + std::vector length_per_sample(eh.nsamples, 0); + for (uint32_t i_sample{0}; i_sample < eh.nsamples; i_sample++) { + if (i_sample % 2 == 0) { + n_words++; + reader >> w; + i_event++; + } + uint32_t shift_in_word = 16 * (i_sample % 2); + length_per_sample[i_sample] = + (w >> shift_in_word) & packing::utility::mask<12>; + } + + if (eh.version == 2) { + /** + * For the time being, the number of sample lengths is fixed to make the + * firmware for DMA readout simpler. This means we readout the leftover + * dummy words to move the pointer on the reader. + */ + for (int i_word{n_words}; i_word < 8; i_word++) { + reader >> head1; + i_event++; + } + + /** + * extended event header in version 2 + */ + reader >> head1; + i_event++; + eh.spill = ((head1 >> 12) & 0xfff); + eh.bunch = (head1 & 0xfff); + reader >> head1; + i_event++; + eh.ticks = head1; + reader >> head1; + i_event++; + eh.number = head1; + reader >> head1; + i_event++; + eh.run = (head1 & 0xFFF); + eh.DD = (head1 >> 23) & 0x1F; + eh.MM = (head1 >> 28) & 0xF; + eh.hh = (head1 >> 18) & 0x1F; + eh.mm = (head1 >> 12) & 0x3F; + } + + /** + * Re-sort the data from grouped by bunch to by channel + * + * The readout chip streams the data off of it, so it doesn't + * have time to re-group the signals across multiple bunches (samples) + * by their channel ID. We need to do that here. + */ + // fill map of **electronic** IDs to the digis that were read out + std::map> + eid_to_samples; + std::size_t i_sample{0}; + while (i_event < eventlen) { + reader >> head1 >> head2; + i_event += 2; + /** Decode Bunch Header + * We have a few words of header material before the actual data. + * This header material is assumed to be encoded as in Table 3 + * of the DAQ specs. + * + * (bits) + * + * VERSION (4) | FPGA_ID (8) | NLINKS (6) | 00 | LEN (12) + * BX ID (12) | RREQ (10) | OR (10) + * RID ok (1) | CRC ok (1) | LEN3 (6) | + * RID ok (1) | CRC ok (1) | LEN2 (6) | + * RID ok (1) | CRC ok (1) | LEN1 (6) | + * RID ok (1) | CRC ok (1) | LEN0 (6) + * ... other listing of links ... + */ + packing::utility::CRC fpga_crc; + fpga_crc << head1; + + [[maybe_unused]] uint32_t hgcroc_version = + (head1 >> 28) & packing::utility::mask<4>; + uint32_t fpga = (head1 >> 20) & packing::utility::mask<8>; + uint32_t nlinks = (head1 >> 14) & packing::utility::mask<6>; + [[maybe_unused]] uint32_t len = head1 & packing::utility::mask<12>; + + fpga_crc << head2; + + [[maybe_unused]] uint32_t bx_id = + (head2 >> 20) & packing::utility::mask<12>; + [[maybe_unused]] uint32_t rreq = + (head2 >> 10) & packing::utility::mask<10>; + [[maybe_unused]] uint32_t orbit = head2 & packing::utility::mask<10>; + + std::vector length_per_link(nlinks, 0); + for (uint32_t i_link{0}; i_link < nlinks; i_link++) { + if (i_link % 4 == 0) { + i_event++; + reader >> w; + fpga_crc << w; + } + uint32_t shift_in_word = 8 * (i_link % 4); + [[maybe_unused]] bool rid_ok = + ((w >> (shift_in_word + 7)) & packing::utility::mask<1>) == 1; + [[maybe_unused]] bool cdc_ok = + ((w >> (shift_in_word + 6)) & packing::utility::mask<1>) == 1; + length_per_link[i_link] = + (w >> shift_in_word) & packing::utility::mask<6>; + } + + /** Decode Each Link in Sequence + * Now we should be decoding each link serially + * where each link was encoded as in Table 4 of + * the DAQ specs + * + * ROC_ID (16) | CRC ok (1) | 0 (7) | RO Map (8) + * RO Map (32) + */ + eh.good_bxheader.resize(nlinks); + eh.good_trailer.resize(nlinks); + for (uint32_t i_link{0}; i_link < nlinks; i_link++) { + /** + * If minimum length of 2 is not written for this link, + * assume it went down and skip + */ + if (length_per_link.at(i_link) < 2) { + continue; + } + // move on from last word counting links or previous link + packing::utility::CRC link_crc; + i_event++; + reader >> w; + fpga_crc << w; + link_crc << w; + [[maybe_unused]] uint32_t roc_id = + (w >> 16) & packing::utility::mask<16>; + [[maybe_unused]] bool crc_ok = + ((w >> 15) & packing::utility::mask<1>) == 1; + + // get readout map from the last 8 bits of this word + // and the entire next word + std::bitset<40> ro_map = w & packing::utility::mask<8>; + ro_map <<= 32; + i_event++; + reader >> w; + fpga_crc << w; + link_crc << w; + ro_map |= w; + // loop through channels on this link, + // since some channels may have been suppressed because of low + // amplitude the channel ID is not the same as the index it + // is listed in. + int j{-1}; + for (uint32_t i_word{2}; i_word < length_per_link.at(i_link); + i_word++) { + // skip zero-suppressed channel IDs + do { + j++; + } while (j < 40 and not ro_map.test(j)); + + // next word is this channel + i_event++; + reader >> w; + fpga_crc << w; + + if (j == 0) { + /** Special "Header" Word from ROC + * + * version 3: + * 0101 | BXID (12) | RREQ (6) | OR (3) | HE (3) | 0101 + * + * version 2: + * 10101010 | BXID (12) | WADD (9) | 1010 + */ + link_crc << w; + // v2 + eh.good_bxheader[i_link] = ((w & 0xff000000) == 0xaa000000); + // v3 + // + // Shadows a previous declaration but is never used so renamed here + [[maybe_unused]] uint32_t v3_bx_id = + (w >> 16) & packing::utility::mask<12>; + [[maybe_unused]] uint32_t short_event = + (w >> 10) & packing::utility::mask<6>; + [[maybe_unused]] uint32_t short_orbit = + (w >> 7) & packing::utility::mask<3>; + [[maybe_unused]] uint32_t hamming_errs = + (w >> 4) & packing::utility::mask<3>; + } else if (j == common_mode_channel) { + /** Common Mode Channels + * 10 | 0000000000 | Common Mode ADC 0 (10) | Common Mode ADC 1 (10) + */ + link_crc << w; + } else if (j == calib_channel) { + // calib channel + link_crc << w; + } else if (j == 39) { + // trailer on each link added by ROC + // ROC v2 - IDLE word + // ROC v3 - CRC checksum + if (roc_version_ == 2) { + bool good_idle = (w == 0xaccccccc); + eh.good_trailer[i_link] = good_idle; + } else { + bool good_crc = (link_crc.get() == w); + eh.good_trailer[i_link] = good_crc; + } + /* + if (roc_version_ > 2 and link_crc.get() != w) { + EXCEPTION_RAISE("BadCRC", + "Our calculated link checksum doesn't match the " + "one from raw data."); + } + */ + } else { + /// DAQ Channels + + link_crc << w; + /** + * The HGC ROC has some odd behavior in terms of reading out the + * different channels. + * - extra header word in row j = 0 + * - common mode channel in row (j) number 19 or 1 (depending on the + * version) + * - calib channel in row j = 20 + * This introduces a special shift for the channel number to "align" + * with the range 0-35 per link. + * + * polarfire fpga = fpga readout + * roc = i_link / 2 // integer division + * channel = j - 1 - (j > common_mode_channel)*1 - (j > + * calib_channel)*1 + */ + ldmx::HcalElectronicsID eid(fpga, i_link, + j - 1 - 1 * (j > common_mode_channel) - + 1 * (j > calib_channel)); + // copy data into EID->sample map + eid_to_samples[eid].emplace_back(w); + } // type of channel + } // loop over channels (j in Table 4) + } // loop over links + + // another CRC checksum from FPGA + i_event++; + reader >> w; + [[maybe_unused]] uint32_t crc = w; + /* TODO + * fix calculation of FPGA checksum + * I can't figure out why it isn't matching, but there + * is definitely a word here where the FPGA checksum would be. + if (fpga_crc.get() != crc) { + EXCEPTION_RAISE( + "BadCRC", + "Our calculated FPGA checksum doesn't match the one read in."); + } + */ + // padding to reach 64-bit boundary in version 2 + if (eh.version == 2u and length_per_sample.at(i_sample) % 2 == 1) { + i_event++; + reader >> head1; + } + i_sample++; + } + + if (eh.version == 1u) { + // special footer words + reader >> head1 >> head2; + } + + return eid_to_samples; + } + + private: + /// input file of encoded data + std::string input_file_; + /// input object of encoded data + std::vector input_names_; + /// input pass of creating encoded data + std::string input_pass_; + /// output object to put onto event bus + std::string output_name_; + /// the detector name if we are reading from a file + std::string detector_name_; + /// version of HGC ROC we are decoding + int roc_version_; + /// are get translating electronic IDs? + bool translate_eid_; + /// is the input_name a file or an event object + bool read_from_file_; + + private: + /// the file reader (if we are doing that) + packing::utility::Reader file_reader_; +}; +} // namespace hcal +#endif /* HCALRAWDECODER_H */ diff --git a/include/Hcal/HcalRecProducer.h b/include/Hcal/HcalRecProducer.h index f3d335b..5110fd8 100644 --- a/include/Hcal/HcalRecProducer.h +++ b/include/Hcal/HcalRecProducer.h @@ -50,18 +50,18 @@ class HcalRecProducer : public framework::Producer { /** * Destructor */ - ~HcalRecProducer() = default; + virtual ~HcalRecProducer() = default; /** * Grabs configure parameters from the python config file. */ - void configure(framework::config::Parameters&) final override; + void configure(framework::config::Parameters&) override; /** * Gets Time of Arrival with respect to the SOI. */ double getTOA(const ldmx::HgcrocDigiCollection::HgcrocDigi digi, - double pedestal, unsigned int iSOI); + double pedestal, unsigned int iSOI) const; /** * Produce HcalHits and put them into the event bus using the @@ -71,7 +71,7 @@ class HcalRecProducer : public framework::Producer { * and reconstructs their energy using knowledge of how * the chip operates and the position using HcalGeometry. */ - void produce(framework::Event& event) final override; + void produce(framework::Event& event) override; private: /// Digi Collection Name to use as input diff --git a/include/Hcal/HcalSingleEndRecProducer.h b/include/Hcal/HcalSingleEndRecProducer.h new file mode 100644 index 0000000..75e036e --- /dev/null +++ b/include/Hcal/HcalSingleEndRecProducer.h @@ -0,0 +1,74 @@ +#ifndef HCALSINGLEENDRECPRODUCER_H +#define HCALSINGLEENDRECPRODUCER_H + +#include "Conditions/SimpleTableCondition.h" +#include "DetDescr/DetectorID.h" +#include "DetDescr/HcalDigiID.h" +#include "DetDescr/HcalGeometry.h" +#include "DetDescr/HcalID.h" +#include "Framework/EventDef.h" +#include "Framework/EventProcessor.h" +#include "Hcal/HcalReconConditions.h" +#include "Recon/Event/HgcrocDigiCollection.h" +namespace hcal { +class HcalSingleEndRecProducer : public framework::Producer { + /// name of pass of digis to use + std::string pass_name_{""}; + /// name of digis to reconstruct + std::string coll_name_{"HcalDigis"}; + /// name of pass of rechits to use + std::string rec_pass_name_{""}; + /// name of rechits to reconstruct + std::string rec_coll_name_{"HcalRecHits"}; + + /// number of PEs per MIP + double pe_per_mip_; + /// energy per MIP [MeV] + double mip_energy_; + /// length of clock cycle [ns] + double clock_cycle_; + /// sample of interest index + unsigned int isoi_; + + private: + /** + * extract toa, sum adc, and sum tot from the input raw digi + * + * in the far future, we can make these member functions ofthe HgcrocDigi + * class; however, right now as we develop our reconstruction method it is + * helpful to have more flexible control on how we extract these measurements + * + * with C++17 structured bindings, this tuple return can be bound to separate + * variables: + * ```cpp + * auto [ toa, sum_adc, sum_tot ] = + * extract_measurements(digi,pedestal,bx_shift); + * ``` + * giving us the dual benefit of separate variable names while only having to + * loop over the samples within a single digi once + * + * Uses isoi_ and clock_cycle_ member variables to convert TOA into ns since + * beginning of Sample Of Interest (SOI) + * + * @param[in] digi handle to HgcrocDigi to extract from + * @param[in] pedestal pedestal for this channel + * @param[in] shift in BX associated to TOA for this channel + * @return tuple of (toa [ns since SOI], sum_adc, sum_tot) + */ + std::tuple extract_measurements( + const ldmx::HgcrocDigiCollection::HgcrocDigi& digi, double pedestal, + double bx_shift); + + public: + HcalSingleEndRecProducer(const std::string& n, framework::Process& p) + : Producer(n, p) {} + virtual ~HcalSingleEndRecProducer() = default; + + void configure(framework::config::Parameters& p) override; + void produce(framework::Event& event) override; + +}; // HcalSingleEndRecProducer + +} // namespace hcal + +#endif /* HCALSINGLEENDRECPRODUCER_H */ diff --git a/include/Hcal/HcalTrigPrimDigiProducer.h b/include/Hcal/HcalTrigPrimDigiProducer.h index 4c7b229..b3efffa 100644 --- a/include/Hcal/HcalTrigPrimDigiProducer.h +++ b/include/Hcal/HcalTrigPrimDigiProducer.h @@ -20,6 +20,7 @@ class HcalTrigPrimDigiProducer : public framework::Producer { HcalTrigPrimDigiProducer(const std::string& name, framework::Process& process); + virtual ~HcalTrigPrimDigiProducer() = default; /** * Grabs configure parameters from the python config file. * @@ -28,13 +29,13 @@ class HcalTrigPrimDigiProducer : public framework::Producer { * digiPassName "" <-- blank means take any pass if only one * collection exists */ - virtual void configure(framework::config::Parameters&); + void configure(framework::config::Parameters&) override; /** * Produce HcalTrigPrimDigis and put them into the event bus using the * HcalDigis as input. */ - virtual void produce(framework::Event& event); + void produce(framework::Event& event) override; private: /** Digi Collection Name to use as input */ diff --git a/include/Hcal/HcalVetoProcessor.h b/include/Hcal/HcalVetoProcessor.h index 7762a43..275a2ba 100644 --- a/include/Hcal/HcalVetoProcessor.h +++ b/include/Hcal/HcalVetoProcessor.h @@ -28,14 +28,14 @@ class HcalVetoProcessor : public framework::Producer { HcalVetoProcessor(const std::string &name, framework::Process &process); /** Destructor */ - ~HcalVetoProcessor(); + virtual ~HcalVetoProcessor() = default; /** * Configure the processor using the given user specified parameters. * * @param parameters Set of parameters used to configure this processor. */ - void configure(framework::config::Parameters ¶meters) final override; + void configure(framework::config::Parameters ¶meters) override; /** * Run the processor and create a collection of results which @@ -43,21 +43,39 @@ class HcalVetoProcessor : public framework::Producer { * * @param event The event to process. */ - void produce(framework::Event &event); + void produce(framework::Event &event) override; private: /** Total PE threshold. */ - double totalPEThreshold_{8}; + double totalPEThreshold_{5}; /** Maximum hit time that should be considered by the veto. */ float maxTime_{50}; // ns - /** Maximum z depth that a hit can have. */ - float maxDepth_{4000}; // mm + /** The minimum number of PE in both bars needed for a hit to be considered in + * double ended readout mode. */ + float backMinPE_{1}; - /** The minimum number of PE needed for a hit. */ - float minPE_{1}; + /* + * A hit representing the case where we never reach the maxPE condition. This + * is rare but can happen which previously would record uninitialized memory. + * Stored inside of the producer so that it will have a valid lifetime when we + * persist it. + * + * See https://github.com/LDMX-Software/Hcal/issues/58 for details + * + * Ideally, this would just be stored as a part of the HcalVetoResult, but + * changing that would be breaking change so for now we work around it like + * this. + * + * It contains nonsense values but since they are predictable, they are harder + * to mistake for real hits. See constructor for the actual values. + */ + ldmx::HcalHit defaultMaxHit_; + std::string outputCollName_; + std::string inputHitCollName_; + std::string inputHitPassName_; }; // HcalVetoProcessor } // namespace hcal diff --git a/include/Hcal/HcalWABVetoProcessor.h b/include/Hcal/HcalWABVetoProcessor.h index 76794f7..11b32b3 100644 --- a/include/Hcal/HcalWABVetoProcessor.h +++ b/include/Hcal/HcalWABVetoProcessor.h @@ -30,14 +30,14 @@ class HcalWABVetoProcessor : public framework::Producer { HcalWABVetoProcessor(const std::string &name, framework::Process &process); /** Destructor */ - ~HcalWABVetoProcessor(); + virtual ~HcalWABVetoProcessor(); /** * Configure the processor using the given user specified parameters. * * @param parameters Set of parameters used to configure this processor. */ - void configure(framework::config::Parameters ¶meters) final override; + void configure(framework::config::Parameters ¶meters) override; /** * Run the processor and create a collection of results which @@ -45,7 +45,7 @@ class HcalWABVetoProcessor : public framework::Producer { * * @param event The event to process. */ - void produce(framework::Event &event); + void produce(framework::Event &event) override; private: // Maximum sum of total ECAL and HCAL energy diff --git a/python/hcal.py b/python/hcal.py index 7229726..80a3156 100644 --- a/python/hcal.py +++ b/python/hcal.py @@ -18,8 +18,10 @@ def __init__(self,name = 'hcalVeto') : self.pe_threshold = 5.0 self.max_time = 50.0 - self.max_depth = 4000.0 self.back_min_pe = 1. + self.input_hit_coll_name= "HcalRecHits"; + self.input_hit_pass_name = '' + self.output_coll_name= "HcalVeto"; class HcalWABVetoProcessor(ldmxcfg.Producer) : """Configuration for WAB veto in HCal diff --git a/python/hcal_hardcoded_conditions.py b/python/hcal_hardcoded_conditions.py index 41e6c06..8a1a3cf 100644 --- a/python/hcal_hardcoded_conditions.py +++ b/python/hcal_hardcoded_conditions.py @@ -12,10 +12,12 @@ HcalTrigPrimConditionsHardcode=SimpleCSVIntegerTableProvider("HcalTrigPrimDigiConditions",["ADC_PEDESTAL","ADC_THRESHOLD","TOT_PEDESTAL","TOT_THRESHOLD","TOT_GAIN"]) HcalTrigPrimConditionsHardcode.validForAllRows([ 1 , # ADC_PEDESTAL -- should match value from HgcrocEmulator - 5 , # ADC_THRESHOLD -- current noise is + 5 , # ADC_THRESHOLD -- current noise is 1, # TOT_PEDESTAL -- currently set to match ADC pedestal 10000, # TOT_THRESHOLD -- rather large value... - 2.5 ] # TOT_GAIN, ratio of recon TOT gain over recon ADC gain + # Rounding because trigger primitives shouldn't represent floating point operations. + # See https://github.com/LDMX-Software/Hcal/issues/66#issuecomment-1719663799 + round(2.5) ] # TOT_GAIN, ratio of recon TOT gain over recon ADC gain ) adc_pedestal = SimpleCSVDoubleTableProvider("hcal_adc_pedestal",["pedestal"]) diff --git a/src/Hcal/Event/HcalCluster.cxx b/src/Hcal/Event/HcalCluster.cxx index 24966d2..ada2077 100644 --- a/src/Hcal/Event/HcalCluster.cxx +++ b/src/Hcal/Event/HcalCluster.cxx @@ -1,32 +1,30 @@ #include "Hcal/Event/HcalCluster.h" -ClassImp(ldmx::HcalCluster) +ClassImp(ldmx::HcalCluster); - namespace ldmx { - HcalCluster::HcalCluster() {} +namespace ldmx { +HcalCluster::~HcalCluster() { Clear(); } - HcalCluster::~HcalCluster() { Clear(); } +void HcalCluster::Print() const { + std::cout << "HcalCluster { " + << "Energy: " << energy_ << ", " + << "Number of hits: " << nHits_ << " }" << std::endl; +} - void HcalCluster::Print() const { - std::cout << "HcalCluster { " - << "Energy: " << energy_ << ", " - << "Number of hits: " << nHits_ << " }" << std::endl; - } - - void HcalCluster::Clear() { - hitIDs_.clear(); - energy_ = 0; - nHits_ = 0; - centroidX_ = 0; - centroidY_ = 0; - centroidZ_ = 0; - } +void HcalCluster::Clear() { + hitIDs_.clear(); + energy_ = 0; + nHits_ = 0; + centroidX_ = 0; + centroidY_ = 0; + centroidZ_ = 0; +} - void HcalCluster::addHits(const std::vector hitsVec) { - std::vector vecIDs; - for (unsigned int iHit = 0; iHit < hitsVec.size(); iHit++) { - vecIDs.push_back(hitsVec[iHit]->getID()); - } - setIDs(vecIDs); +void HcalCluster::addHits(const std::vector hitsVec) { + std::vector vecIDs; + for (unsigned int iHit = 0; iHit < hitsVec.size(); iHit++) { + vecIDs.push_back(hitsVec[iHit]->getID()); } + setIDs(vecIDs); +} } // namespace ldmx diff --git a/src/Hcal/Event/HcalHit.cxx b/src/Hcal/Event/HcalHit.cxx index 6411d76..6bb60ee 100644 --- a/src/Hcal/Event/HcalHit.cxx +++ b/src/Hcal/Event/HcalHit.cxx @@ -3,20 +3,20 @@ // STL #include -ClassImp(ldmx::HcalHit) +ClassImp(ldmx::HcalHit); - namespace ldmx { - void HcalHit::Clear() { - ldmx::CalorimeterHit::Clear(); - pe_ = 0; - minpe_ = -99; - } +namespace ldmx { +void HcalHit::Clear() { + ldmx::CalorimeterHit::Clear(); + pe_ = 0; + minpe_ = -99; +} - void HcalHit::Print() const { - std::cout << "HcalHit { " - << "id: " << std::hex << getID() << std::dec - << ", energy: " << getEnergy() << "MeV, time: " << getTime() - << "ns, amplitude: " << getAmplitude() << ", pe: " << getPE() - << "}" << std::endl; - } +void HcalHit::Print() const { + std::cout << "HcalHit { " + << "id: " << std::hex << getID() << std::dec + << ", energy: " << getEnergy() << "MeV, time: " << getTime() + << "ns, amplitude: " << getAmplitude() << ", pe: " << getPE() << "}" + << std::endl; +} } // namespace ldmx diff --git a/src/Hcal/Event/HcalVetoResult.cxx b/src/Hcal/Event/HcalVetoResult.cxx index 32a5652..6acb4cb 100644 --- a/src/Hcal/Event/HcalVetoResult.cxx +++ b/src/Hcal/Event/HcalVetoResult.cxx @@ -10,18 +10,15 @@ //-------------// #include "Hcal/Event/HcalHit.h" -ClassImp(ldmx::HcalVetoResult) +ClassImp(ldmx::HcalVetoResult); - namespace ldmx { - HcalVetoResult::HcalVetoResult() {} +namespace ldmx { - HcalVetoResult::~HcalVetoResult() {} +void HcalVetoResult::Clear() { passesVeto_ = false; } - void HcalVetoResult::Clear() { passesVeto_ = false; } - - void HcalVetoResult::Print() const { - std::cout << "[ HcalVetoResult ]: Passes veto : " - << " Passes veto: " << passesVeto_ << std::endl; - maxPEHit_.Print(); - } +void HcalVetoResult::Print() const { + std::cout << "[ HcalVetoResult ]: Passes veto : " + << " Passes veto: " << passesVeto_ << std::endl; + maxPEHit_.Print(); +} } // namespace ldmx diff --git a/src/Hcal/HcalAlignPolarfires.cxx b/src/Hcal/HcalAlignPolarfires.cxx index 055032e..e17381d 100644 --- a/src/Hcal/HcalAlignPolarfires.cxx +++ b/src/Hcal/HcalAlignPolarfires.cxx @@ -1,71 +1,19 @@ -#include - -#include "Framework/EventProcessor.h" -#include "Recon/Event/HgcrocDigiCollection.h" - +#include "Hcal/HcalAlignPolarfires.h" namespace hcal { - -/** - * Align the two polarfires with drop/keep hints signalling successful merge - * - * - Only checking for /dropped/ events - * - assuming that ticks and spills are already in correct ORDER - * - assuming spill numbering is NOT the same between the two DPMs - */ -class HcalAlignPolarfires : public framework::Producer { - /// input decoded objects (vector index == polarfire index) - std::vector input_names_; - /// pass name for decoded objects - std::string input_pass_; - /// output object name - std::string output_name_; - /// number of 5MHz ticks difference to consider polarfires aligned - static int max_tick_diff_; - - public: - struct PolarfireQueueEntry { - /// the i'th spill - int spill; - /// ticks since spill - int ticks; - ldmx::HgcrocDigiCollection digis; - PolarfireQueueEntry(const framework::Event& event, - const std::string& input_name, - const std::string& input_pass, - std::pair& spill_counter) { - int spilln = event.getObject(input_name + "Spill", input_pass); - if (spilln != spill_counter.second) { - spill_counter.first++; - spill_counter.second = spilln; - } - spill = spill_counter.first; - ticks = event.getObject(input_name + "Ticks", input_pass); - digis = - event.getObject(input_name, input_pass); - } - bool same_event(const PolarfireQueueEntry& rhs) { - return (spill == rhs.spill and abs(ticks - rhs.ticks) < max_tick_diff_); - } - bool earlier_event(const PolarfireQueueEntry& rhs) { - if (spill == rhs.spill) return ticks < rhs.ticks; - return spill < rhs.spill; - } - }; - /// queue of unmatched digis - std::queue pf0_queue, pf1_queue; - /// spill counter - std::pair pf0_spill_counter{0, -1}, pf1_spill_counter{0, -1}; - - public: - HcalAlignPolarfires(const std::string& n, framework::Process& p) - : framework::Producer(n, p) {} - virtual ~HcalAlignPolarfires() = default; - virtual void configure(framework::config::Parameters& ps) final override; - virtual void produce(framework::Event& event) final override; -}; - int HcalAlignPolarfires::max_tick_diff_ = 10; +HcalAlignPolarfires::PolarfireQueueEntry::PolarfireQueueEntry( + const framework::Event& event, const std::string& input_name, + const std::string& input_pass, std::pair& spill_counter) { + int spilln = event.getObject(input_name + "Spill", input_pass); + if (spilln != spill_counter.second) { + spill_counter.first++; + spill_counter.second = spilln; + } + spill = spill_counter.first; + ticks = event.getObject(input_name + "Ticks", input_pass); + digis = event.getObject(input_name, input_pass); +} void HcalAlignPolarfires::configure(framework::config::Parameters& ps) { input_names_ = ps.getParameter>("input_names"); @@ -137,7 +85,6 @@ void HcalAlignPolarfires::produce(framework::Event& event) { event.add(output_name_, merged); event.add(output_name_ + "Aligned", aligned); } // produce - } // namespace hcal DECLARE_PRODUCER_NS(hcal, HcalAlignPolarfires); diff --git a/src/Hcal/HcalDigiProducer.cxx b/src/Hcal/HcalDigiProducer.cxx index 0ae4338..98e4724 100644 --- a/src/Hcal/HcalDigiProducer.cxx +++ b/src/Hcal/HcalDigiProducer.cxx @@ -54,7 +54,7 @@ void HcalDigiProducer::configure(framework::config::Parameters& ps) { double gain = ps.getParameter("avgGain"); double pedestal = ps.getParameter("avgPedestal"); // rms noise in mV - noiseGenerator_->setNoise(gain*ps.getParameter("avgNoiseRMS")); + noiseGenerator_->setNoise(gain * ps.getParameter("avgNoiseRMS")); // mean noise amplitude (if using Gaussian Model for the noise) in mV noiseGenerator_->setPedestal(gain * pedestal); // threshold for readout in mV diff --git a/src/Hcal/HcalDoubleEndRecProducer.cxx b/src/Hcal/HcalDoubleEndRecProducer.cxx index c36bb37..1448b24 100644 --- a/src/Hcal/HcalDoubleEndRecProducer.cxx +++ b/src/Hcal/HcalDoubleEndRecProducer.cxx @@ -1,43 +1,7 @@ -#include "Conditions/SimpleTableCondition.h" -#include "DetDescr/DetectorID.h" -#include "DetDescr/HcalDigiID.h" -#include "DetDescr/HcalGeometry.h" -#include "DetDescr/HcalID.h" -#include "Framework/EventDef.h" -#include "Framework/EventProcessor.h" -#include "Hcal/HcalReconConditions.h" -#include "Recon/Event/HgcrocDigiCollection.h" +#include "Hcal/HcalDoubleEndRecProducer.h" namespace hcal { -class HcalDoubleEndRecProducer : public framework::Producer { - /// name of pass of rechits to use - std::string pass_name_{""}; - /// name of rechits to use as input - std::string coll_name_{"HcalRecHits"}; - /// name of pass of rechits to reconstruct - std::string rec_pass_name_{""}; - /// name of rechits to reconstruct - std::string rec_coll_name_{"HcalRecHitsDoubleEnd"}; - - /// number of PEs per MIP - double pe_per_mip_; - /// energy per MIP [MeV] - double mip_energy_; - /// length of clock cycle [ns] - double clock_cycle_; - - private: - - public: - HcalDoubleEndRecProducer(const std::string& n, framework::Process& p) - : Producer(n, p) {} - - virtual void configure(framework::config::Parameters& p) final override; - virtual void produce(framework::Event& event) final override; - -}; // HcalDoubleEndRecProducer - void HcalDoubleEndRecProducer::configure(framework::config::Parameters& p) { pass_name_ = p.getParameter("pass_name", pass_name_); coll_name_ = p.getParameter("coll_name", coll_name_); @@ -57,8 +21,7 @@ void HcalDoubleEndRecProducer::produce(framework::Event& event) { const auto& conditions{ getCondition(HcalReconConditions::CONDITIONS_NAME)}; - auto hcalRecHits = - event.getCollection(coll_name_, pass_name_); + auto hcalRecHits = event.getCollection(coll_name_, pass_name_); std::vector doubleHcalRecHits; @@ -78,21 +41,22 @@ void HcalDoubleEndRecProducer::produce(framework::Event& event) { // make pairs of hcal rechits indices that belong to the same pulse // @TODO: for now we just take the first two indices that have opposite-ends // we do not cover the case where two hits come separated in time - std::map> indicesByID; + std::map> indicesByID; for (auto const& hcalBar : hitsByID) { auto id = hcalBar.first; - std::pair indices(-1,-1); + std::pair indices(-1, -1); int iHit = 0; - while ( iHit < hcalBar.second.size() ) { + while (iHit < hcalBar.second.size()) { auto hit = hcalBar.second.at(iHit); - ldmx::HcalDigiID digi_id(hit.getSection(), hit.getLayer(), hit.getStrip(), hit.getEnd()); - if(digi_id.isNegativeEnd() && indices.second==-1) { - indices.second = iHit; + ldmx::HcalDigiID digi_id(hit.getSection(), hit.getLayer(), hit.getStrip(), + hit.getEnd()); + if (digi_id.isNegativeEnd() && indices.second == -1) { + indices.second = iHit; } - if(!digi_id.isNegativeEnd() && indices.first==-1) { - indices.first = iHit; + if (!digi_id.isNegativeEnd() && indices.first == -1) { + indices.first = iHit; } iHit++; } @@ -108,58 +72,65 @@ void HcalDoubleEndRecProducer::produce(framework::Event& event) { const auto orientation{hcalGeometry.getScintillatorOrientation(id)}; // skip non-double-ended layers - if (id.section() != ldmx::HcalID::HcalSection::BACK) - continue; + if (id.section() != ldmx::HcalID::HcalSection::BACK) continue; // get two hits to reconstruct auto hitPosEnd = hcalBar.second.at(indicesByID[id].first); auto hitNegEnd = hcalBar.second.at(indicesByID[id].second); // update TOA hit with negative end with mean shift - ldmx::HcalDigiID digi_id_pos(hitPosEnd.getSection(), hitPosEnd.getLayer(), hitPosEnd.getStrip(), hitPosEnd.getEnd()); - ldmx::HcalDigiID digi_id_neg(hitNegEnd.getSection(), hitNegEnd.getLayer(), hitNegEnd.getStrip(), hitNegEnd.getEnd()); + ldmx::HcalDigiID digi_id_pos(hitPosEnd.getSection(), hitPosEnd.getLayer(), + hitPosEnd.getStrip(), hitPosEnd.getEnd()); + ldmx::HcalDigiID digi_id_neg(hitNegEnd.getSection(), hitNegEnd.getLayer(), + hitNegEnd.getStrip(), hitNegEnd.getEnd()); double mean_shift = conditions.toaCalib(digi_id_neg.raw(), 1); double pos_time = hitPosEnd.getTime(); double neg_time = hitNegEnd.getTime(); - if( pos_time != 0 || neg_time !=0 ) { + if (pos_time != 0 || neg_time != 0) { neg_time = neg_time - mean_shift; } // update position in strip according to time measurement double v = - 299.792 / 1.6; // velocity of light in polystyrene, n = 1.6 = c/v + 299.792 / 1.6; // velocity of light in polystyrene, n = 1.6 = c/v double hitTimeDiff = pos_time - neg_time; - //std::cout << "\n new hit " << std::endl; - //std::cout << "strip " << id.strip() << " layer " << id.layer() << " center position " << position.X() << " " << position.Y() << " " << position.Z() << std::endl; - //std::cout << "hittime pos " << pos_time << " neg " << neg_time << " bar sign " << " diff " << hitTimeDiff << std::endl; + // std::cout << "\n new hit " << std::endl; + // std::cout << "strip " << id.strip() << " layer " << id.layer() << " + // center position " << position.X() << " " << position.Y() << " " << + // position.Z() << std::endl; std::cout << "hittime pos " << pos_time << " + // neg " << neg_time << " bar sign " << " diff " << hitTimeDiff << + // std::endl; int position_bar_sign = hitTimeDiff > 0 ? 1 : -1; - double position_unchanged = 0; int isX=0; - double position_bar = - position_bar_sign * fabs(hitTimeDiff) * v / 2; - if (orientation == - ldmx::HcalGeometry::ScintillatorOrientation::horizontal) { + double position_unchanged = 0; + int isX = 0; + double position_bar = position_bar_sign * fabs(hitTimeDiff) * v / 2; + if (orientation == + ldmx::HcalGeometry::ScintillatorOrientation::horizontal) { position_unchanged = position.X(); - isX=1; + isX = 1; position.SetX(position_bar); } else { position_unchanged = position.Y(); - isX=0; + isX = 0; position.SetY(position_bar); } - //std::cout << "position unchanged " << position_unchanged << " isx " << isX << std::endl; - //std::cout << "newposition " << position.X() << " " << position.Y() << " " << position.Z() << std::endl; + // std::cout << "position unchanged " << position_unchanged << " isx " << + // isX << std::endl; std::cout << "newposition " << position.X() << " " << + // position.Y() << " " << position.Z() << std::endl; // TODO: switch unique hit time for this pulse - double hitTime = (hitPosEnd.getTime() + hitNegEnd.getTime()); + [[maybe_unused]] double hitTime = + (hitPosEnd.getTime() + hitNegEnd.getTime()); // amplitude and PEs - double num_mips_equivalent = (hitPosEnd.getAmplitude() + hitNegEnd.getAmplitude()); + double num_mips_equivalent = + (hitPosEnd.getAmplitude() + hitNegEnd.getAmplitude()); double PEs = (hitPosEnd.getPE() + hitNegEnd.getPE()); double reconstructed_energy = - num_mips_equivalent * pe_per_mip_ * mip_energy_; + num_mips_equivalent * pe_per_mip_ * mip_energy_; // reconstructed Hit ldmx::HcalHit recHit; @@ -171,7 +142,7 @@ void HcalDoubleEndRecProducer::produce(framework::Event& event) { recHit.setStrip(id.strip()); recHit.setLayer(id.layer()); recHit.setPE(PEs); - recHit.setMinPE(std::min(hitPosEnd.getPE(),hitNegEnd.getPE())); + recHit.setMinPE(std::min(hitPosEnd.getPE(), hitNegEnd.getPE())); recHit.setAmplitude(num_mips_equivalent); recHit.setAmplitudePos(hitPosEnd.getAmplitude()); recHit.setAmplitudeNeg(hitNegEnd.getAmplitude()); diff --git a/src/Hcal/HcalPedestalAnalyzer.cxx b/src/Hcal/HcalPedestalAnalyzer.cxx index a641e4e..0424ab9 100644 --- a/src/Hcal/HcalPedestalAnalyzer.cxx +++ b/src/Hcal/HcalPedestalAnalyzer.cxx @@ -1,64 +1,10 @@ /** @class Service application which creates standard CSV-format pedestal files */ -#include "DetDescr/HcalDigiID.h" -#include "Framework/EventProcessor.h" -#include "Recon/Event/HgcrocDigiCollection.h" +#include "Hcal/HcalPedestalAnalyzer.h" namespace hcal { -class HcalPedestalAnalyzer : public framework::Analyzer { - std::string input_name_, input_pass_; - std::string output_file_, comments_; - bool make_histos_; - bool filter_noTOT; - bool filter_noTOA; - int low_cutoff_, high_cutoff_; - - struct Channel { - Channel() : hist{0}, sum{0}, sum_sq{0}, entries{0}, rejects{4, 0} {} - /// collection of hits accumulated to produce appropriately-binned - /// histograms - std::vector adcs; - /// Histogram, if used - TH1* hist; - /// Sum of values - uint64_t sum; - /// Sum of values squared - double sum_sq; - /// Number of entries - int entries; - /// counts of various rejections - std::vector rejects; - }; - - std::map pedestal_data_; - - void create_and_fill(Channel& chan, ldmx::HcalDigiID detid); - - public: - HcalPedestalAnalyzer(const std::string& n, framework::Process& p) - : framework::Analyzer(n, p) {} - ~HcalPedestalAnalyzer() {} - - void configure(framework::config::Parameters& ps) final override { - input_name_ = ps.getParameter("input_name"); - input_pass_ = ps.getParameter("input_pass"); - output_file_ = ps.getParameter("output_file"); - comments_ = ps.getParameter("comments"); - - make_histos_ = ps.getParameter("make_histos", false); - - filter_noTOT = ps.getParameter("filter_noTOT", true); - filter_noTOA = ps.getParameter("filter_noTOA", true); - low_cutoff_ = ps.getParameter("low_cutoff", 10); - high_cutoff_ = ps.getParameter("high_cutoff", 512); - } - - virtual void analyze(const framework::Event& event); - virtual void onProcessEnd(); -}; - void HcalPedestalAnalyzer::analyze(const framework::Event& event) { auto const& digis{ event.getObject(input_name_, input_pass_)}; diff --git a/src/Hcal/HcalRawDecoder.cxx b/src/Hcal/HcalRawDecoder.cxx index d7fa1b6..88ed07a 100644 --- a/src/Hcal/HcalRawDecoder.cxx +++ b/src/Hcal/HcalRawDecoder.cxx @@ -1,17 +1,6 @@ -#include -#include -#include - -#include "DetDescr/HcalElectronicsID.h" -#include "DetDescr/HcalID.h" -#include "Framework/EventProcessor.h" -#include "Hcal/HcalDetectorMap.h" -#include "Packing/Utility/CRC.h" -#include "Packing/Utility/Mask.h" -#include "Packing/Utility/Reader.h" -#include "Recon/Event/HgcrocDigiCollection.h" +#include "Hcal/HcalRawDecoder.h" // un comment for HcalRawDecoder-specific debug printouts to std::cout //#define DEBUG @@ -63,40 +52,9 @@ inline std::ostream& operator<<(std::ostream& os, const debug::hex& h) { * Struct to help interface between raw decoder read function * and putting stuff onto event bus */ -struct PolarfireEventHeader { - /// version of daq format - int version; - /// id for polarfire - int fpga; - /// number of samples - int nsamples; - /// spill number - int spill; - /// number of 5MHz ticks since spill - int ticks; - /// bunch number according to this polarfire - int bunch; - /// event number according to this polarfire - int number; - /// run number according to this polarfire - int run; - /// day of month run started - int DD; - /// month run started - int MM; - /// hour run started - int hh; - /// minute run started - int mm; - /// quality of link headers - std::vector good_bxheader; - /// quality of link trailers - std::vector good_trailer; - - /** - * board us onto the bus with the input prefix - */ - void board(framework::Event& event, const std::string& prefix) { +void PolarfireEventHeader::board(framework::Event& event, + const std::string& prefix) { + { event.add(prefix + "Version", version); event.add(prefix + "FPGA", fpga); event.add(prefix + "NSamples", nsamples); @@ -109,496 +67,10 @@ struct PolarfireEventHeader { event.add(prefix + "MM", MM); event.add(prefix + "hh", hh); event.add(prefix + "mm", mm); - event.add(prefix+"GoodLinkHeader", good_bxheader); - event.add(prefix+"GoodLinkTrailer", good_trailer); - } -}; - -/** - * @class HcalRawDecoder - */ -class HcalRawDecoder : public framework::Producer { - public: - HcalRawDecoder(const std::string& name, framework::Process& process) - : framework::Producer(name, process) {} - virtual ~HcalRawDecoder() = default; - virtual void configure(framework::config::Parameters&) final override; - /// add detector name if we are reading from file - virtual void beforeNewRun(ldmx::RunHeader& rh) final override; - /// use read function to decode data, then translate EIDs into DetIDs - virtual void produce(framework::Event& event) final override; - - private: - /** - * Assume input reader behaves like a binary data input stream - * where we can "pop" individual 32-bit words with operator>> - * and we can check if the reader is done using operator bool() - */ - template - std::map> - read(ReaderType& reader, PolarfireEventHeader& eh) { - /** - * Static parameters depending on ROC version - */ - static const unsigned int common_mode_channel = roc_version_ == 2 ? 19 : 1; - static const unsigned int calib_channel = 20; - /// words for reading and decoding - static uint32_t head1, head2, w; - - // special header words not counted in event length - do { - reader >> head1; -#ifdef DEBUG - if (head1 == 0xbeef2021) { - std::cout << "Signal words imply version 1" << std::endl; - } else if (head1 == 0xbeef2022) { - std::cout << "Signal words imply version 2" << std::endl; - } else { - std::cout << "Extra header (inserted by rogue): " << debug::hex(head1) - << std::endl; - } -#endif - } while (head1 != 0xbeef2021 and head1 != 0xbeef2022); - - /** - * Decode event header - */ - long int eventlen; - long int i_event{0}; - /* whole event header word looks like - * - * VERSION (4) | FPGA ID (8) | NSAMPLES (4) | LEN (16) - */ - reader >> head1; - i_event++; - - eh.version = (head1 >> 28) & packing::utility::mask<4>; - eh.fpga = (head1 >> 20) & packing::utility::mask<8>; - eh.nsamples = (head1 >> 16) & packing::utility::mask<4>; - eventlen = head1 & packing::utility::mask<16>; - if (eh.version == 1u) { - // eventlen is 32-bit words in event - // do nothing here - } else if (eh.version == 2u) { - // eventlen is 64-bit words in event, - // need to multiply by 2 to get actual 32-bit event length - eventlen *= 2; - // and subtract off the special header word above - eventlen -= 1; - } else { - EXCEPTION_RAISE( - "VersMis", - "HcalRawDecoder only knows version 1 and 2 of DAQ format."); - } -#ifdef DEBUG - std::cout << debug::hex(head1) << " EventHeader(version = " << eh.version - << ", fpga = " << eh.fpga << ", nsamples = " << eh.nsamples - << ", eventlen = " << eventlen << ")" << std::endl; - std::cout << "Sample Lenghts: "; -#endif - // sample counters - int n_words{0}; - std::vector length_per_sample(eh.nsamples, 0); - for (uint32_t i_sample{0}; i_sample < eh.nsamples; i_sample++) { - if (i_sample % 2 == 0) { - n_words++; - reader >> w; - i_event++; - } - uint32_t shift_in_word = 16 * (i_sample % 2); - length_per_sample[i_sample] = - (w >> shift_in_word) & packing::utility::mask<12>; -#ifdef DEBUG - std::cout << "len(" << i_sample << ") = " << length_per_sample[i_sample] - << " "; -#endif - } -#ifdef DEBUG - std::cout << std::endl; -#endif - - if (eh.version == 2) { - /** - * For the time being, the number of sample lengths is fixed to make the - * firmware for DMA readout simpler. This means we readout the leftover - * dummy words to move the pointer on the reader. - */ -#ifdef DEBUG - std::cout << "Padding words to reach 8 total sample length words." - << std::endl; -#endif - for (int i_word{n_words}; i_word < 8; i_word++) { - reader >> head1; - i_event++; -#ifdef DEBUG - std::cout << " " << debug::hex(head1); -#endif - } -#ifdef DEBUG - std::cout << std::endl; -#endif - - /** - * extended event header in version 2 - */ - reader >> head1; - i_event++; - eh.spill = ((head1 >> 12) & 0xfff); - eh.bunch = (head1 & 0xfff); -#ifdef DEBUG - std::cout << " " << debug::hex(head1) << " Spill: " << eh.spill - << " Bunch: " << eh.bunch << std::endl; -#endif - reader >> head1; - i_event++; - eh.ticks = head1; -#ifdef DEBUG - std::cout << " " << debug::hex(head1) - << " 5 MHz Ticks since Spill: " << head1 - << " Time: " << head1 / 5e6 << "s" << std::endl; -#endif - reader >> head1; - i_event++; - eh.number = head1; -#ifdef DEBUG - std::cout << " " << debug::hex(head1) << " Event Number: " << head1 - << std::endl; -#endif - reader >> head1; - i_event++; - eh.run = (head1 & 0xFFF); - eh.DD = (head1 >> 23) & 0x1F; - eh.MM = (head1 >> 28) & 0xF; - eh.hh = (head1 >> 18) & 0x1F; - eh.mm = (head1 >> 12) & 0x3F; -#ifdef DEBUG - std::cout << " " << debug::hex(head1) << " Run: " << eh.run - << " DD-MM hh:mm " << eh.DD << "-" << eh.MM << " " << eh.hh - << ":" << eh.mm << std::endl; -#endif - } - - /** - * Re-sort the data from grouped by bunch to by channel - * - * The readout chip streams the data off of it, so it doesn't - * have time to re-group the signals across multiple bunches (samples) - * by their channel ID. We need to do that here. - */ - // fill map of **electronic** IDs to the digis that were read out - std::map> - eid_to_samples; - std::size_t i_sample{0}; - while (i_event < eventlen) { -#ifdef DEBUG - std::cout << "Decoding sample " << i_sample << " on word " << i_event - << std::endl; -#endif - reader >> head1 >> head2; - i_event += 2; - /** Decode Bunch Header - * We have a few words of header material before the actual data. - * This header material is assumed to be encoded as in Table 3 - * of the DAQ specs. - * - * (bits) - * - * VERSION (4) | FPGA_ID (8) | NLINKS (6) | 00 | LEN (12) - * BX ID (12) | RREQ (10) | OR (10) - * RID ok (1) | CRC ok (1) | LEN3 (6) | - * RID ok (1) | CRC ok (1) | LEN2 (6) | - * RID ok (1) | CRC ok (1) | LEN1 (6) | - * RID ok (1) | CRC ok (1) | LEN0 (6) - * ... other listing of links ... - */ - packing::utility::CRC fpga_crc; - fpga_crc << head1; -#ifdef DEBUG - std::cout << debug::hex(head1) << " : "; -#endif - uint32_t hgcroc_version = (head1 >> 28) & packing::utility::mask<4>; -#ifdef DEBUG - std::cout << "hgcroc_version " << hgcroc_version << std::flush; -#endif - uint32_t fpga = (head1 >> 20) & packing::utility::mask<8>; - uint32_t nlinks = (head1 >> 14) & packing::utility::mask<6>; - uint32_t len = head1 & packing::utility::mask<12>; - -#ifdef DEBUG - std::cout << ", fpga: " << fpga << ", nlinks: " << nlinks - << ", len: " << len << std::endl; -#endif - fpga_crc << head2; -#ifdef DEBUG - std::cout << debug::hex(head2) << " : "; -#endif - - uint32_t bx_id = (head2 >> 20) & packing::utility::mask<12>; - uint32_t rreq = (head2 >> 10) & packing::utility::mask<10>; - uint32_t orbit = head2 & packing::utility::mask<10>; - -#ifdef DEBUG - std::cout << "bx_id: " << bx_id << ", rreq: " << rreq - << ", orbit: " << orbit << std::endl; -#endif - - std::vector length_per_link(nlinks, 0); - for (uint32_t i_link{0}; i_link < nlinks; i_link++) { - if (i_link % 4 == 0) { - i_event++; - reader >> w; - fpga_crc << w; -#ifdef DEBUG - std::cout << debug::hex(w) << " : Four Link Pack " << std::endl; -#endif - } - uint32_t shift_in_word = 8 * (i_link % 4); - bool rid_ok = - (w >> (shift_in_word + 7)) & packing::utility::mask<1> == 1; - bool cdc_ok = - (w >> (shift_in_word + 6)) & packing::utility::mask<1> == 1; - length_per_link[i_link] = - (w >> shift_in_word) & packing::utility::mask<6>; -#ifdef DEBUG - std::cout << " Link " << i_link << " readout " - << length_per_link.at(i_link) << " channels" << std::endl; -#endif - } - - /** Decode Each Link in Sequence - * Now we should be decoding each link serially - * where each link was encoded as in Table 4 of - * the DAQ specs - * - * ROC_ID (16) | CRC ok (1) | 0 (7) | RO Map (8) - * RO Map (32) - */ - eh.good_bxheader.resize(nlinks); - eh.good_trailer.resize(nlinks); - for (uint32_t i_link{0}; i_link < nlinks; i_link++) { -#ifdef DEBUG - std::cout << "RO Link " << i_link << std::endl; -#endif - /** - * If minimum length of 2 is not written for this link, - * assume it went down and skip - */ - if (length_per_link.at(i_link) < 2) { -#ifdef DEBUG - std::cout << "DOWN" << std::endl; -#endif - continue; - } - // move on from last word counting links or previous link - packing::utility::CRC link_crc; - i_event++; - reader >> w; - fpga_crc << w; - link_crc << w; - uint32_t roc_id = (w >> 16) & packing::utility::mask<16>; - bool crc_ok = (w >> 15) & packing::utility::mask<1> == 1; -#ifdef DEBUG - std::cout << debug::hex(w) << " : roc_id " << roc_id - << ", crc_ok (v2 always false) " << std::boolalpha << crc_ok - << std::endl; -#endif - - // get readout map from the last 8 bits of this word - // and the entire next word - std::bitset<40> ro_map = w & packing::utility::mask<8>; - ro_map <<= 32; - i_event++; - reader >> w; - fpga_crc << w; - link_crc << w; - ro_map |= w; -#ifdef DEBUG - std::cout << debug::hex(w) << " : lower 32 bits of RO map" << std::endl; - std::cout << "Start looping through " << length_per_link.at(i_link) - << " words for this link" << std::endl; -#endif - // loop through channels on this link, - // since some channels may have been suppressed because of low - // amplitude the channel ID is not the same as the index it - // is listed in. - int j{-1}; - for (uint32_t i_word{2}; i_word < length_per_link.at(i_link); - i_word++) { - // skip zero-suppressed channel IDs - do { - j++; - } while (j < 40 and not ro_map.test(j)); - - // next word is this channel - i_event++; - reader >> w; - fpga_crc << w; -#ifdef DEBUG - std::cout << debug::hex(w) << " " << j; -#endif - - if (j == 0) { - /** Special "Header" Word from ROC - * - * version 3: - * 0101 | BXID (12) | RREQ (6) | OR (3) | HE (3) | 0101 - * - * version 2: - * 10101010 | BXID (12) | WADD (9) | 1010 - */ -#ifdef DEBUG - std::cout << " : ROC Header"; -#endif - link_crc << w; - // v2 - eh.good_bxheader[i_link] = ((w & 0xff000000) == 0xaa000000); - // v3 - uint32_t bx_id = (w >> 16) & packing::utility::mask<12>; - uint32_t short_event = (w >> 10) & packing::utility::mask<6>; - uint32_t short_orbit = (w >> 7) & packing::utility::mask<3>; - uint32_t hamming_errs = (w >> 4) & packing::utility::mask<3>; - } else if (j == common_mode_channel) { - /** Common Mode Channels - * 10 | 0000000000 | Common Mode ADC 0 (10) | Common Mode ADC 1 (10) - */ - link_crc << w; -#ifdef DEBUG - std::cout << " : Common Mode"; -#endif - } else if (j == calib_channel) { - // calib channel - link_crc << w; -#ifdef DEBUG - std::cout << " : Calib"; -#endif - } else if (j == 39) { - // trailer on each link added by ROC - // ROC v2 - IDLE word - // ROC v3 - CRC checksum - if (roc_version_ == 2) { - bool good_idle = (w == 0xaccccccc); - eh.good_trailer[i_link] = good_idle; -#ifdef DEBUG - std::cout << " : " << (good_idle?"Good":"Bad") << " Idle"; -#endif - } else { - bool good_crc = (link_crc.get() == w); - eh.good_trailer[i_link] = good_crc; -#ifdef DEBUG - std::cout << " : CRC checksum : " << debug::hex(link_crc.get()) << " =? " << debug::hex(w); -#endif - } - /* - if (roc_version_ > 2 and link_crc.get() != w) { - EXCEPTION_RAISE("BadCRC", - "Our calculated link checksum doesn't match the " - "one from raw data."); - } - */ - } else { - /// DAQ Channels - - link_crc << w; - /** - * The HGC ROC has some odd behavior in terms of reading out the - * different channels. - * - extra header word in row j = 0 - * - common mode channel in row (j) number 19 or 1 (depending on the - * version) - * - calib channel in row j = 20 - * This introduces a special shift for the channel number to "align" - * with the range 0-35 per link. - * - * polarfire fpga = fpga readout - * roc = i_link / 2 // integer division - * channel = j - 1 - (j > common_mode_channel)*1 - (j > - * calib_channel)*1 - */ - ldmx::HcalElectronicsID eid(fpga, i_link, - j - 1 - 1 * (j > common_mode_channel) - - 1 * (j > calib_channel)); -#ifdef DEBUG - std::cout << " : DAQ Channel "; - std::cout << "EID(" << eid.fiber() << "," << eid.elink() << "," - << eid.channel() << ") "; -#endif - // copy data into EID->sample map - eid_to_samples[eid].emplace_back(w); - } // type of channel -#ifdef DEBUG - std::cout << std::endl; -#endif - } // loop over channels (j in Table 4) -#ifdef DEBUG - std::cout << "done looping through channels" << std::endl; -#endif - } // loop over links - - // another CRC checksum from FPGA - i_event++; - reader >> w; - uint32_t crc = w; -#ifdef DEBUG - std::cout << "Done with sample " << i_sample << std::endl; - std::cout << "FPGA Checksum : " << debug::hex(fpga_crc.get()) << " =? " - << debug::hex(crc) << std::endl; - std::cout << " N Sample Words : " << length_per_sample.at(i_sample) - << std::endl; -#endif - /* TODO - * fix calculation of FPGA checksum - * I can't figure out why it isn't matching, but there - * is definitely a word here where the FPGA checksum would be. - if (fpga_crc.get() != crc) { - EXCEPTION_RAISE( - "BadCRC", - "Our calculated FPGA checksum doesn't match the one read in."); - } - */ - // padding to reach 64-bit boundary in version 2 - if (eh.version == 2u and length_per_sample.at(i_sample) % 2 == 1) { - i_event++; - reader >> head1; -#ifdef DEBUG - std::cout << "Padding to reach 64-bit boundary: " << debug::hex(head1) - << std::endl; -#endif - } - i_sample++; - } - - if (eh.version == 1u) { - // special footer words - reader >> head1 >> head2; - } - - return eid_to_samples; + event.add(prefix + "GoodLinkHeader", good_bxheader); + event.add(prefix + "GoodLinkTrailer", good_trailer); } - - private: - /// input file of encoded data - std::string input_file_; - /// input object of encoded data - std::vector input_names_; - /// input pass of creating encoded data - std::string input_pass_; - /// output object to put onto event bus - std::string output_name_; - /// the detector name if we are reading from a file - std::string detector_name_; - /// version of HGC ROC we are decoding - int roc_version_; - /// are get translating electronic IDs? - bool translate_eid_; - /// is the input_name a file or an event object - bool read_from_file_; - - private: - /// the file reader (if we are doing that) - packing::utility::Reader file_reader_; -}; +} void HcalRawDecoder::configure(framework::config::Parameters& ps) { input_file_ = ps.getParameter("input_file"); @@ -707,5 +179,4 @@ void HcalRawDecoder::produce(framework::Event& event) { } // produce } // namespace hcal - DECLARE_PRODUCER_NS(hcal, HcalRawDecoder); diff --git a/src/Hcal/HcalRecProducer.cxx b/src/Hcal/HcalRecProducer.cxx index c6f5551..ecf00f1 100644 --- a/src/Hcal/HcalRecProducer.cxx +++ b/src/Hcal/HcalRecProducer.cxx @@ -77,11 +77,12 @@ void HcalRecProducer::configure(framework::config::Parameters& ps) { if (n == 0) minAmpl_ = ampl_t; n++; } + correctionTOA_.SetBit(TGraph::kIsSortedX); } double HcalRecProducer::getTOA( const ldmx::HgcrocDigiCollection::HgcrocDigi digi, double pedestal, - unsigned int iSOI) { + unsigned int iSOI) const { // get toa relative to the startBX double toaRelStartBX(0.), maxMeas{0.}; int toaSample(0), maxSample(0), iADC(0); @@ -174,7 +175,6 @@ void HcalRecProducer::produce(framework::Event& event) { ldmx::HcalDigiID id_negend(digi_negend.id()); double voltage_posend, voltage_negend; - double amplT_posend_copy, amplT_negend_copy; if (digi_posend.isTOT()) { voltage_posend = (digi_posend.tot() - the_conditions.totCalib(id_posend, 0)) * @@ -192,9 +192,6 @@ void HcalRecProducer::produce(framework::Event& event) { amplTm1_negend = digi_negend.soi().adc_tm1() - the_conditions.adcPedestal(id_negend); - amplT_posend_copy = amplT_posend; - amplT_negend_copy = amplT_negend; - // correct amplitude (amplitude fractions from both ends need to be // above the boundary of the correction) if (amplTm1_posend / amplT_posend > minAmplFraction_ && @@ -204,7 +201,7 @@ void HcalRecProducer::produce(framework::Event& event) { } // set voltage - voltage_posend = amplT_posend * the_conditions.adcGain(id_posend, 0); + voltage_posend = amplT_posend * the_conditions.adcGain(id_posend, 0); voltage_negend = amplT_negend * the_conditions.adcGain(id_negend, 0); } @@ -217,9 +214,6 @@ void HcalRecProducer::produce(framework::Event& event) { // get sign of position along the bar int position_bar_sign = (TOA_posend - TOA_negend) > 0 ? 1 : -1; - double TOA_posend_copy = TOA_posend; - double TOA_negend_copy = TOA_negend; - // correct TOA // amplitudes from both ends need to be above the boundary of the // correction otherwise, one TOA gets corrected and the other does not, diff --git a/src/Hcal/HcalReconConditions.cxx b/src/Hcal/HcalReconConditions.cxx index 39b8a84..fb14e22 100644 --- a/src/Hcal/HcalReconConditions.cxx +++ b/src/Hcal/HcalReconConditions.cxx @@ -11,8 +11,7 @@ HcalReconConditions::HcalReconConditions( const conditions::DoubleTableCondition& adc_ped, const conditions::DoubleTableCondition& adc_gain, const conditions::DoubleTableCondition& tot_calib, - const conditions::DoubleTableCondition& toa_calib - ) + const conditions::DoubleTableCondition& toa_calib) : framework::ConditionsObject(HcalReconConditions::CONDITIONS_NAME), adc_pedestals_{adc_ped}, adc_gains_{adc_gain}, @@ -71,7 +70,7 @@ class HcalReconConditionsProvider : public framework::ConditionsObjectProvider { std::string tot_calib_; /// name of condition object for hcal toa calibrations std::string toa_calib_; - + public: /** * Retrieve the name of the parent conditions from the configuration @@ -123,11 +122,12 @@ class HcalReconConditionsProvider : public framework::ConditionsObjectProvider { auto [tot_calib_co, tot_calib_iov] = requestParentCondition(tot_calib_, context); auto [toa_calib_co, toa_calib_iov] = - requestParentCondition(toa_calib_, context); + requestParentCondition(toa_calib_, context); // deduce "minimum" IOV - // Framework #56 : https://github.com/LDMX-Software/Framework/issues/56 - auto min_iov = adc_ped_iov; + // TODO: Framework #56 : + // https://github.com/LDMX-Software/Framework/issues/56 + [[maybe_unused]] auto min_iov = adc_ped_iov; // use std::move(min_iov) in return statement below // wrap @@ -135,8 +135,7 @@ class HcalReconConditionsProvider : public framework::ConditionsObjectProvider { dynamic_cast(*adc_ped_co), dynamic_cast(*adc_gain_co), dynamic_cast(*tot_calib_co), - dynamic_cast(*toa_calib_co) - ); + dynamic_cast(*toa_calib_co)); return {co, framework::ConditionsIOV(context.getRun(), context.getRun())}; } diff --git a/src/Hcal/HcalSingleEndRecProducer.cxx b/src/Hcal/HcalSingleEndRecProducer.cxx index 4321d84..ae58280 100644 --- a/src/Hcal/HcalSingleEndRecProducer.cxx +++ b/src/Hcal/HcalSingleEndRecProducer.cxx @@ -1,72 +1,9 @@ -#include "Conditions/SimpleTableCondition.h" -#include "DetDescr/DetectorID.h" -#include "DetDescr/HcalDigiID.h" -#include "DetDescr/HcalGeometry.h" -#include "DetDescr/HcalID.h" -#include "Framework/EventDef.h" -#include "Framework/EventProcessor.h" -#include "Hcal/HcalReconConditions.h" -#include "Recon/Event/HgcrocDigiCollection.h" - +#include "Hcal/HcalSingleEndRecProducer.h" namespace hcal { -class HcalSingleEndRecProducer : public framework::Producer { - /// name of pass of digis to use - std::string pass_name_{""}; - /// name of digis to reconstruct - std::string coll_name_{"HcalDigis"}; - /// name of pass of rechits to use - std::string rec_pass_name_{""}; - /// name of rechits to reconstruct - std::string rec_coll_name_{"HcalRecHits"}; - - /// number of PEs per MIP - double pe_per_mip_; - /// energy per MIP [MeV] - double mip_energy_; - /// length of clock cycle [ns] - double clock_cycle_; - /// sample of interest index - unsigned int isoi_; - - private: - /** - * extract toa, sum adc, and sum tot from the input raw digi - * - * in the far future, we can make these member functions ofthe HgcrocDigi - * class; however, right now as we develop our reconstruction method it is - * helpful to have more flexible control on how we extract these measurements - * - * with C++17 structured bindings, this tuple return can be bound to separate - * variables: - * ```cpp - * auto [ toa, sum_adc, sum_tot ] = extract_measurements(digi,pedestal,bx_shift); - * ``` - * giving us the dual benefit of separate variable names while only having to - * loop over the samples within a single digi once - * - * Uses isoi_ and clock_cycle_ member variables to convert TOA into ns since - * beginning of Sample Of Interest (SOI) - * - * @param[in] digi handle to HgcrocDigi to extract from - * @param[in] pedestal pedestal for this channel - * @param[in] shift in BX associated to TOA for this channel - * @return tuple of (toa [ns since SOI], sum_adc, sum_tot) - */ - std::tuple extract_measurements( - const ldmx::HgcrocDigiCollection::HgcrocDigi& digi, double pedestal, double bx_shift); - - public: - HcalSingleEndRecProducer(const std::string& n, framework::Process& p) - : Producer(n, p) {} - - virtual void configure(framework::config::Parameters& p) final override; - virtual void produce(framework::Event& event) final override; - -}; // HcalSingleEndRecProducer - std::tuple HcalSingleEndRecProducer::extract_measurements( - const ldmx::HgcrocDigiCollection::HgcrocDigi& digi, double pedestal, double bx_shift) { + const ldmx::HgcrocDigiCollection::HgcrocDigi& digi, double pedestal, + double bx_shift) { // sum_adc = total of all but first in-time adc measurements double sum_adc{0}; // sum_tot = total of all tot measurements @@ -74,9 +11,11 @@ std::tuple HcalSingleEndRecProducer::extract_measurements( // first, get time of arrival w.r.t to start BX int toa_sample{0}, toa_startbx{0}; // get the correction for the wrong BX assignment - int bx_to_time{0}; + // TODO: Currently not used anywhere + [[maybe_unused]] int bx_to_time{0}; // and figure out sample of maximum amplitude - int max_sample{0}; + // TODO: Currently not used anywhere + [[maybe_unused]] int max_sample{0}; double max_meas{0}; for (std::size_t i_sample{0}; i_sample < digi.size(); i_sample++) { // adc logic @@ -88,16 +27,17 @@ std::tuple HcalSingleEndRecProducer::extract_measurements( // toa logic if (digi.at(i_sample).toa() > 0) { if (digi.at(i_sample).toa() >= bx_shift) { - toa_sample = i_sample; - } - else { - toa_sample = i_sample + 1; + toa_sample = i_sample; + } else { + toa_sample = i_sample + 1; } - // sum toa in given bx - given that multiple bx may be associated with the TOA measurement - toa_startbx += digi.at(i_sample).toa() * (clock_cycle_ / 1024) + (clock_cycle_ * toa_sample); + // sum toa in given bx - given that multiple bx may be associated with the + // TOA measurement + toa_startbx += digi.at(i_sample).toa() * (clock_cycle_ / 1024) + + (clock_cycle_ * toa_sample); } - + if (digi.at(i_sample).adc_t() - pedestal > max_meas) { max_meas = digi.at(i_sample).adc_t() - pedestal; max_sample = i_sample; @@ -105,11 +45,11 @@ std::tuple HcalSingleEndRecProducer::extract_measurements( } // get toa double toa = toa_startbx; - + // get toa w.r.t the peak - //double toa = (max_sample - toa_sample) * clock_cycle_ - toa_startbx; + // double toa = (max_sample - toa_sample) * clock_cycle_ - toa_startbx; // get toa w.r.t the SOI - //toa += ((int)isoi_ - max_sample) * clock_cycle_; + // toa += ((int)isoi_ - max_sample) * clock_cycle_; return std::make_tuple(toa, sum_adc, sum_tot); } @@ -120,7 +60,7 @@ void HcalSingleEndRecProducer::configure(framework::config::Parameters& p) { rec_pass_name_ = p.getParameter("rec_pass_name", rec_pass_name_); rec_coll_name_ = p.getParameter("rec_coll_name", rec_coll_name_); - + pe_per_mip_ = p.getParameter("pe_per_mip"); mip_energy_ = p.getParameter("mip_energy"); clock_cycle_ = p.getParameter("clock_cycle"); @@ -147,7 +87,8 @@ void HcalSingleEndRecProducer::produce(framework::Event& event) { // amplitude/TOT reconstruction double num_mips_equivalent{0}; auto [toa, sum_adc, sum_tot] = - extract_measurements(digi, conditions.adcPedestal(digi.id()), conditions.toaCalib(digi.id(), 0)); + extract_measurements(digi, conditions.adcPedestal(digi.id()), + conditions.toaCalib(digi.id(), 0)); auto is_adc = conditions.is_adc(digi.id(), sum_tot); if (is_adc) { double adc_calib = sum_adc / conditions.adcGain(digi.id(), 0); diff --git a/src/Hcal/HcalVetoProcessor.cxx b/src/Hcal/HcalVetoProcessor.cxx index b6c5db0..eee4703 100644 --- a/src/Hcal/HcalVetoProcessor.cxx +++ b/src/Hcal/HcalVetoProcessor.cxx @@ -17,36 +17,66 @@ HcalVetoProcessor::HcalVetoProcessor(const std::string &name, framework::Process &process) : Producer(name, process) {} -HcalVetoProcessor::~HcalVetoProcessor() {} - void HcalVetoProcessor::configure(framework::config::Parameters ¶meters) { totalPEThreshold_ = parameters.getParameter("pe_threshold"); maxTime_ = parameters.getParameter("max_time"); - maxDepth_ = parameters.getParameter("max_depth"); - minPE_ = parameters.getParameter("back_min_pe"); + outputCollName_ = parameters.getParameter("output_coll_name"); + inputHitCollName_ = + parameters.getParameter("input_hit_coll_name"); + inputHitPassName_ = + parameters.getParameter("input_hit_pass_name"); + // A fake-hit that gets added for the rare case where no hit actually reaches + // the maxPE < pe check to avoid producing uninitialized memory + // + // Default constructed hits have nonsense-but predictable values and are + // harder to mistake for real hits + defaultMaxHit_.Clear(); + defaultMaxHit_.setPE(-9999); + defaultMaxHit_.setMinPE(-9999); + defaultMaxHit_.setSection(-9999); + defaultMaxHit_.setLayer(-9999); + defaultMaxHit_.setStrip(-9999); + defaultMaxHit_.setEnd(-999); + defaultMaxHit_.setTimeDiff(-9999); + defaultMaxHit_.setToaPos(-9999); + defaultMaxHit_.setToaNeg(-9999); + defaultMaxHit_.setAmplitudePos(-9999); + defaultMaxHit_.setAmplitudeNeg(-9999); + + double maxDepth_ = parameters.getParameter("max_depth", 0.); + if (maxDepth_ != 0.) { + EXCEPTION_RAISE( + "InvalidParam", + "Earlier versions of the Hcal veto defined a max depth for " + "positions which is no longer implemented. Remove the " + "parameter (max_depth) from your configuration. See " + "https://github.com/LDMX-Software/Hcal/issues/61 for details"); + } + backMinPE_ = parameters.getParameter("back_min_pe"); } void HcalVetoProcessor::produce(framework::Event &event) { // Get the collection of sim particles from the event const std::vector hcalRecHits = - event.getCollection("HcalRecHits"); + event.getCollection(inputHitCollName_, inputHitPassName_); // Loop over all of the Hcal hits and calculate to total photoelectrons // in the event. - float totalPe{0}; + [[maybe_unused]] float totalPe{0}; float maxPE{-1000}; - const ldmx::HcalHit *maxPEHit; + + const ldmx::HcalHit *maxPEHit{&defaultMaxHit_}; for (const ldmx::HcalHit &hcalHit : hcalRecHits) { // If the hit time is outside the readout window, don't consider it. - if (hcalHit.getTime() >= maxTime_) continue; - - // If the hit z position is beyond the maximum HCal depth, skip it. - if (hcalHit.getZPos() > maxDepth_) continue; + if (hcalHit.getTime() >= maxTime_) { + continue; + } // Get the total PE in the bar float pe = hcalHit.getPE(); // Keep track of the total PE + // TODO: This is currently not used anywhere totalPe += pe; // Check that both sides of the bar have a PE value above threshold. @@ -54,7 +84,8 @@ void HcalVetoProcessor::produce(framework::Event &event) { // being used for the back HCal bars. For the side HCal, just // use the maximum PE as before. ldmx::HcalID id(hcalHit.getID()); - if ((id.section() == ldmx::HcalID::BACK) && (hcalHit.getMinPE() < minPE_)) + if ((id.section() == ldmx::HcalID::BACK) && + (hcalHit.getMinPE() < backMinPE_)) continue; // Find the maximum PE in the list @@ -77,7 +108,7 @@ void HcalVetoProcessor::produce(framework::Event &event) { setStorageHint(framework::hint_shouldDrop); } - event.add("HcalVeto", result); + event.add(outputCollName_, result); } } // namespace hcal