diff --git a/generators/Herwig/HepMCTrigger/HepMCJetTrigger.cc b/generators/Herwig/HepMCTrigger/HepMCJetTrigger.cc index 02ca208f25..a5f45f2080 100644 --- a/generators/Herwig/HepMCTrigger/HepMCJetTrigger.cc +++ b/generators/Herwig/HepMCTrigger/HepMCJetTrigger.cc @@ -85,6 +85,16 @@ bool HepMCJetTrigger::isGoodEvent(HepMC::GenEvent* e1) return false; } +/** + * @brief Cluster final-state particles into anti-kt R=0.4 jets and return the resulting PseudoJets. + * + * Clusters particles from the provided HepMC::GenEvent into jets using the anti-kt algorithm with radius 0.4. + * Final-state particles with PDG IDs in the range 12–18 (neutrinos / similar, by absolute value) are excluded + * from clustering. + * + * @param e1 Pointer to the HepMC::GenEvent whose particles will be clustered. + * @return std::vector Vector of clustered jets (inclusive jets) as FastJet PseudoJet objects. + */ std::vector HepMCJetTrigger::findAllJets(HepMC::GenEvent* e1) { // do the fast jet clustering, antikt r=-0.4 @@ -96,6 +106,11 @@ std::vector HepMCJetTrigger::findAllJets(HepMC::GenEvent* e1 if (!(*iter)->end_vertex() && (*iter)->status() == 1) { auto p = (*iter)->momentum(); + auto pd = std::abs((*iter)->pdg_id()); + if (pd >= 12 && pd <= 18) + { + continue; // keep jet in the expected behavioro + } fastjet::PseudoJet pj(p.px(), p.py(), p.pz(), p.e()); pj.set_user_index((*iter)->barcode()); input.push_back(pj); @@ -115,6 +130,15 @@ std::vector HepMCJetTrigger::findAllJets(HepMC::GenEvent* e1 return output; } +/** + * @brief Counts jets that exceed the configured transverse momentum threshold within the central acceptance. + * + * Counts how many jets in `jets` have transverse momentum greater than the module's `threshold` + * and pseudorapidity magnitude less than or equal to 1.1. + * + * @param jets Vector of clustered jets to evaluate. + * @return int Number of jets with pt greater than the threshold and |eta| <= 1.1. + */ int HepMCJetTrigger::jetsAboveThreshold(const std::vector& jets) const { // search through for the number of identified jets above the threshold @@ -122,10 +146,14 @@ int HepMCJetTrigger::jetsAboveThreshold(const std::vector& j for (const auto& j : jets) { float const pt = j.pt(); + if (std::abs(j.eta()) > 1.1) + { + continue; + } if (pt > this->threshold) { n_good_jets++; } } return n_good_jets; -} +} \ No newline at end of file diff --git a/generators/Herwig/HepMCTrigger/HepMCParticleTrigger.cc b/generators/Herwig/HepMCTrigger/HepMCParticleTrigger.cc new file mode 100644 index 0000000000..5a2e77b2a8 --- /dev/null +++ b/generators/Herwig/HepMCTrigger/HepMCParticleTrigger.cc @@ -0,0 +1,548 @@ +#include "HepMCParticleTrigger.h" + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +//____________________________________________________________________________.. +/** + * @brief Construct a HepMCParticleTrigger with configured thresholds and limits. + * + * Initializes trigger parameters, default kinematic cut values, and control flags; + * optionally enables a Pt lower bound when a nonzero threshold is provided. + * + * @param trigger_thresh If nonzero, sets the Pt lower cut to this value and enables the Pt low cut. + * @param n_incom Target number of accepted events used when enforcing an event limit. + * @param up_lim If true, processing will stop once the target number of accepted events is reached. + * @param name Module name passed to the SubsysReco base class. + */ +HepMCParticleTrigger::HepMCParticleTrigger(float trigger_thresh, int n_incom, bool up_lim, const std::string& name) + : SubsysReco(name) + , threshold(trigger_thresh) + , goal_event_number(n_incom) + , set_event_limit(up_lim) + , _theEtaHigh(1.1) + , _theEtaLow(-1.1) + , _thePtHigh(999.9) + , _thePtLow(0) + , _thePHigh(999.9) + , _thePLow(-999.9) + , _thePzHigh(999.9) + , _thePzLow(-999.9) + , + + _doEtaHighCut(true) + , _doEtaLowCut(true) + , _doBothEtaCut(true) + , + + _doAbsEtaHighCut(false) + , _doAbsEtaLowCut(false) + , _doBothAbsEtaCut(false) + , + + _doPtHighCut(false) + , _doPtLowCut(false) + , _doBothPtCut(false) + , + _doPHighCut(false) + , _doPLowCut(false) + , _doBothPCut(false) + , + + _doPzHighCut(false) + , _doPzLowCut(false) + , _doBothPzCut(false) +{ + if (threshold != 0) + { + _doPtLowCut = true; + _thePtLow = threshold; + } +} + +/** + * @brief Process a single PHCompositeNode event and decide whether it passes the configured HepMC particle trigger. + * + * Increments the processed-event counter and, if a configured event limit is reached, aborts further processing. + * Retrieves the PHHepMCGenEventMap from the node tree, validates each contained HepMC::GenEvent, and accepts the event only if every GenEvent satisfies isGoodEvent. + * When accepted, increments the accepted-event counter. + * + * @param topNode Root node of the Fun4All node tree containing the "PHHepMCGenEventMap". + * @return int Fun4AllReturnCodes::EVENT_OK if the event is accepted; Fun4AllReturnCodes::ABORTEVENT if the node map or any GenEvent is missing, if the event fails the trigger, or if the configured accepted-event limit has been reached. + */ +int HepMCParticleTrigger::process_event(PHCompositeNode* topNode) +{ + // std::cout << "HepMCParticleTrigger::process_event(PHCompositeNode *topNode) Processing Event" << std::endl; + n_evts++; + if (this->set_event_limit == true) + { // needed to keep all HepMC output at the same number of events + if (n_good >= this->goal_event_number) + { + return Fun4AllReturnCodes::ABORTEVENT; + } + } + bool good_event{false}; + PHHepMCGenEventMap* phg = findNode::getClass(topNode, "PHHepMCGenEventMap"); + if (!phg) + { + return Fun4AllReturnCodes::ABORTEVENT; + } + for (PHHepMCGenEventMap::ConstIter eventIter = phg->begin(); eventIter != phg->end(); ++eventIter) + { + PHHepMCGenEvent* hepev = eventIter->second; + if (!hepev) + { + return Fun4AllReturnCodes::ABORTEVENT; + } + HepMC::GenEvent* ev = hepev->getEvent(); + if (!ev) + { + return Fun4AllReturnCodes::ABORTEVENT; + } + good_event = isGoodEvent(ev); + if (!good_event) + { + return Fun4AllReturnCodes::ABORTEVENT; + } + } + if (good_event) + { + n_good++; + } + return Fun4AllReturnCodes::EVENT_OK; +} +/** + * @brief Adds a particle PDG ID to the list of trigger particles. + * + * @param particlePid PDG identifier to require for the trigger (use negative values for antiparticles). + */ +void HepMCParticleTrigger::AddParticle(int particlePid) +{ + _theParticles.push_back(particlePid); + return; +} +/** + * @brief Appends a collection of PDG particle IDs to the internal trigger list. + * + * @param particles Vector of PDG IDs to add; each ID is appended in order and duplicates are preserved. + */ +void HepMCParticleTrigger::AddParticles(const std::vector& particles) +{ + for (auto p : particles) + { + _theParticles.push_back(p); + } + return; +} + +/** + * @brief Sets the upper transverse momentum (pT) threshold and enables the high-pT cut. + * + * If a low-pT cut is already enabled, also enables the combined pT-range check. + * + * @param pt Upper pT threshold to apply. + */ +void HepMCParticleTrigger::SetPtHigh(double pt) +{ + _thePtHigh = pt; + _doPtHighCut = true; + if (_doPtLowCut) + { + _doBothPtCut = true; + } + return; +} +/** + * @brief Set the lower bound for the transverse momentum (pt) cut and enable the pt-low selection. + * + * Enables the low-pt cut using the provided threshold and, if the high-pt cut is already enabled, + * also enables the combined (both low and high) pt-range cut. + * + * @param pt Lower bound for transverse momentum (pt) in the same units used by event particles. + */ +void HepMCParticleTrigger::SetPtLow(double pt) +{ + _thePtLow = pt; + _doPtLowCut = true; + if (_doPtHighCut) + { + _doBothPtCut = true; + } + return; +} +/** + * @brief Enable a combined transverse momentum (pT) range cut and set its bounds. + * + * Sets the upper and lower pT thresholds and enables the high, low, and combined pT cuts. + * + * @param ptHigh Upper bound on transverse momentum (pT). + * @param ptLow Lower bound on transverse momentum (pT). + */ +void HepMCParticleTrigger::SetPtHighLow(double ptHigh, double ptLow) +{ + _thePtHigh = ptHigh; + _doPtHighCut = true; + _thePtLow = ptLow; + _doPtLowCut = true; + _doBothPtCut = true; + return; +} +/** + * @brief Set the upper bound for the total momentum cut and enable it. + * + * Enables the momentum high cut at the supplied value. If a low-momentum cut is already enabled, + * also enables the combined (high+low) momentum-range cut. + * + * @param pt Upper momentum threshold (same units as input momenta, e.g., GeV/c). + */ +void HepMCParticleTrigger::SetPHigh(double pt) +{ + _thePHigh = pt; + _doPHighCut = true; + if (_doPLowCut) + { + _doBothPCut = true; + } + return; +} +/** + * @brief Set the lower bound for the total momentum (P) cut and enable the low-P cut. + * + * Enables the P low cut using the provided value; if a P high cut is already enabled, + * also enables the combined P high-low range check. + * + * @param pt Lower bound for the particle momentum P. + */ +void HepMCParticleTrigger::SetPLow(double pt) +{ + _thePLow = pt; + _doPLowCut = true; + if (_doPHighCut) + { + _doBothPCut = true; + } + return; +} +/** + * @brief Set both minimum and maximum total-momentum (P) cuts and enable them. + * + * Enables the low and high total-momentum cuts and marks that both-P range should be applied. + * + * @param ptHigh Upper bound for total momentum (P). + * @param ptLow Lower bound for total momentum (P). + */ +void HepMCParticleTrigger::SetPHighLow(double ptHigh, double ptLow) +{ + _thePHigh = ptHigh; + _doPHighCut = true; + _thePLow = ptLow; + _doPLowCut = true; + _doBothPCut = true; + return; +} +/** + * @brief Set the upper bound for the Pz cut and enable the high-Pz cut. + * + * Configures the trigger to reject particles with Pz greater than the given value by + * setting the internal high-Pz threshold and enabling the high-Pz cut. If a low-Pz + * cut is already enabled, also enable the combined (high-and-low) Pz range check. + * + * @param pt Upper Pz threshold (same units as particle Pz). + */ +void HepMCParticleTrigger::SetPzHigh(double pt) +{ + _thePzHigh = pt; + _doPzHighCut = true; + if (_doPzLowCut) + { + _doBothPzCut = true; + } + return; +} +/** + * @brief Set the lower bound for the longitudinal momentum (Pz) cut. + * + * Sets the minimum Pz value (_thePzLow) used to filter particles and enables the low-Pz cut flag. + * If a high-Pz cut is already enabled, also enables the combined Pz-range flag. + * + * @param pt Lower bound for Pz. + */ +void HepMCParticleTrigger::SetPzLow(double pt) +{ + _thePzLow = pt; + _doPzLowCut = true; + if (_doPzHighCut) + { + _doBothPzCut = true; + } + return; +} +/** + * @brief Configure both upper and lower limits for the longitudinal momentum (Pz) cut. + * + * Enables the Pz high and low cuts and sets their threshold values so that subsequent + * particle selection uses the configured Pz range. + * + * @param ptHigh Upper bound for Pz. + * @param ptLow Lower bound for Pz. + */ +void HepMCParticleTrigger::SetPzHighLow(double ptHigh, double ptLow) +{ + _thePzHigh = ptHigh; + _doPzHighCut = true; + _thePzLow = ptLow; + _doPzLowCut = true; + _doBothPzCut = true; + return; +} +/** + * @brief Set the upper eta threshold and enable the eta high cut. + * + * Sets the maximum allowed pseudorapidity (eta) for the trigger and enables the + * high-eta cut. If a low-eta cut is already enabled, also enables the combined + * eta-range (both) cut. + * + * @param pt Upper eta limit to apply for the high-eta cut. + */ +void HepMCParticleTrigger::SetEtaHigh(double pt) +{ + _theEtaHigh = pt; + _doEtaHighCut = true; + if (_doEtaLowCut) + { + _doBothEtaCut = true; + } + return; +} +/** + * @brief Set the lower bound for the pseudorapidity (eta) selection and enable the corresponding cut. + * + * Enables the eta-low cut using the provided value. If an eta-high cut is already enabled, also enables the combined + * eta-range cut. + * + * @param pt Lower bound for eta selection (inclusive). + */ +void HepMCParticleTrigger::SetEtaLow(double pt) +{ + _theEtaLow = pt; + _doEtaLowCut = true; + if (_doEtaHighCut) + { + _doBothEtaCut = true; + } + return; +} +/** + * @brief Set both upper and lower pseudorapidity (eta) thresholds and enable their cuts. + * + * Sets the high and low eta limits used by the trigger and enables the individual + * high/low eta cut flags as well as the combined eta-range flag. + * + * @param ptHigh Upper (maximum) eta threshold. + * @param ptLow Lower (minimum) eta threshold. + */ +void HepMCParticleTrigger::SetEtaHighLow(double ptHigh, double ptLow) +{ + _theEtaHigh = ptHigh; + _doEtaHighCut = true; + _theEtaLow = ptLow; + _doEtaLowCut = true; + _doBothEtaCut = true; + return; +} +/** + * @brief Set the upper bound for the absolute pseudorapidity (|eta|) cut. + * + * Enables the absolute-eta high cut and, if an absolute-eta low cut is already enabled, + * enables the combined absolute-eta range check. + * + * @param pt Upper threshold for absolute pseudorapidity (|eta|). + */ +void HepMCParticleTrigger::SetAbsEtaHigh(double pt) +{ + _theEtaHigh = pt; + _doAbsEtaHighCut = true; + if (_doAbsEtaLowCut) + { + _doBothAbsEtaCut = true; + } + return; +} +/** + * @brief Configure the lower bound for the absolute pseudorapidity (|eta|) cut. + * + * Sets the lower |eta| threshold and enables the absolute-eta low cut. If an absolute-eta + * high cut is already enabled, also enables the combined absolute-eta range check. + * + * @param pt Lower threshold for |eta|. + */ +void HepMCParticleTrigger::SetAbsEtaLow(double pt) +{ + _theEtaLow = pt; + _doAbsEtaLowCut = true; + if (_doAbsEtaHighCut) + { + _doBothAbsEtaCut = true; + } + return; +} +/** + * @brief Configure absolute pseudorapidity (|eta|) high and low cut thresholds. + * + * Enables and sets the absolute-eta upper and lower bounds used when filtering particles. + * + * @param ptHigh Upper bound for absolute pseudorapidity (|eta|). + * @param ptLow Lower bound for absolute pseudorapidity (|eta|). + */ +void HepMCParticleTrigger::SetAbsEtaHighLow(double ptHigh, double ptLow) +{ + _theEtaHigh = ptHigh; + _doAbsEtaHighCut = true; + _theEtaLow = ptLow; + _doAbsEtaLowCut = true; + _doBothAbsEtaCut = true; + return; +} +/** + * @brief Determines whether an event satisfies the configured particle-trigger requirements. + * + * Evaluates the event's particle content against the configured trigger particle list and kinematic cuts and requires at least one matching particle for each configured trigger entry. + * + * @param e1 Pointer to the HepMC::GenEvent to evaluate. + * @return true if every configured trigger particle type is present at least once in the event, `false` otherwise. + */ +bool HepMCParticleTrigger::isGoodEvent(HepMC::GenEvent* e1) +{ + // this is really just the call to actually evaluate and return the filter + /*if (this->threshold == 0) + { + return true; + }*/ + std::vector n_trigger_particles = getParticles(e1); + for (auto ntp : n_trigger_particles) + { + if (ntp <= 0) + { + return false; // make sure all particles have at least 1 + } + } + return true; +} + +/** + * @brief Counts particles in a GenEvent that satisfy the configured stability and kinematic cuts. + * + * Scans the provided HepMC::GenEvent, applies the instance's stability (if enabled) and all enabled + * eta, absolute-eta, pt, momentum (P), and Pz cuts, tallies surviving particles by absolute PDG ID, + * and returns counts for each particle type requested by the trigger configuration. + * + * @param e1 Input HepMC::GenEvent to be scanned. + * @return std::vector A vector of counts where each element is the number of particles found + * matching the corresponding entry in the trigger's _theParticles list (same order). If a + * requested particle type is absent, its count is 0. + */ +std::vector HepMCParticleTrigger::getParticles(HepMC::GenEvent* e1) +{ + std::vector n_trigger{}; + std::map particle_types; + for (HepMC::GenEvent::particle_const_iterator iter = e1->particles_begin(); iter != e1->particles_end(); ++iter) + { + if (m_doStableParticleOnly && ((*iter)->end_vertex() || (*iter)->status() != 1)) + { + continue; + } + auto p = (*iter)->momentum(); + float px = p.px(); + float py = p.py(); + float pz = p.pz(); + float p_M = std::sqrt(std::pow(px, 2) + std::pow(py, 2) + std::pow(pz, 2)); + float pt = std::sqrt(std::pow(px, 2) + std::pow(py, 2)); + int pid = std::abs((*iter)->pdg_id()); + double eta = p.eta(); + if ((_doEtaHighCut || _doBothEtaCut) && eta > _theEtaHigh) + { + continue; + } + if ((_doEtaLowCut || _doBothEtaCut) && eta < _theEtaLow) + { + continue; + } + if ((_doAbsEtaHighCut || _doBothAbsEtaCut) && std::abs(eta) > _theEtaHigh) + { + continue; + } + if ((_doAbsEtaLowCut || _doBothAbsEtaCut) && std::abs(eta) < _theEtaLow) + { + continue; + } + if ((_doPtHighCut || _doBothPtCut) && pt > _thePtHigh) + { + continue; + } + if ((_doPtLowCut || _doBothPtCut) && pt < _thePtLow) + { + continue; + } + if ((_doPHighCut || _doBothPCut) && p_M > _thePHigh) + { + continue; + } + if ((_doPLowCut || _doBothPCut) && p_M < _thePLow) + { + continue; + } + if ((_doPzHighCut || _doBothPzCut) && pz > _thePzHigh) + { + continue; + } + if ((_doPzLowCut || _doBothPzCut) && pz < _thePzLow) + { + continue; + } + if (particle_types.contains(pid)) + { + particle_types[pid]++; + } + else + { + particle_types[pid] = 1; + } + } + n_trigger.reserve(_theParticles.size()); + for (auto p : _theParticles) + { + n_trigger.push_back(particleAboveThreshold(particle_types, p)); // make sure we have at least one of each required particle + } + return n_trigger; +} +/** + * @brief Retrieve the count of particles for a given trigger PDG identifier. + * + * @param n_particles Map from absolute PDG ID to the number of particles that passed cuts. + * @param trigger_particle PDG ID to look up; its sign is ignored. + * @return int The count for the absolute value of `trigger_particle` if present in `n_particles`, `0` otherwise. + */ +int HepMCParticleTrigger::particleAboveThreshold(const std::map& n_particles, int trigger_particle) +{ + // search through for the number of identified trigger particles passing cuts + auto it = n_particles.find(std::abs(trigger_particle)); + if (it != n_particles.end()) + { + return it->second; + } + return 0; +} \ No newline at end of file diff --git a/generators/Herwig/HepMCTrigger/HepMCParticleTrigger.h b/generators/Herwig/HepMCTrigger/HepMCParticleTrigger.h new file mode 100644 index 0000000000..7c68eefb7d --- /dev/null +++ b/generators/Herwig/HepMCTrigger/HepMCParticleTrigger.h @@ -0,0 +1,130 @@ +// Tell emacs that this is a C++ source +// -*- C++ -*-. +#ifndef HEPMCPARTICLETRIGGER_H +#define HEPMCPARTICLETRIGGER_H + +#include + +#include + +#include +#include +#include +#include + +class PHCompositeNode; +/** + * Representation of a generated event in the HepMC event record. + * + * Forward declaration of `GenEvent` used to reference HepMC event objects (the container for generated particles and event-level information). + */ +namespace HepMC +{ + class GenEvent; +} + +class HepMCParticleTrigger : public SubsysReco +{ + public: + HepMCParticleTrigger(float trigger_thresh = 10., int n_incom = 1000, bool up_lim = false, const std::string& name = "HepMCParticleTrigger"); + + ~HepMCParticleTrigger() override = default; + + /** Called for each event. + This is where you do the real work. + */ + int process_event(PHCompositeNode* topNode) override; + + /// Clean up internals after each event. + + /// Called at the end of each run. + + /// Called at the end of all processing. + + /// Reset + void AddParticles(const std::vector&); //exclusively take input in the form of a pdg_ids (22 for photon, primary use case) + void AddParticle(int); + + /* void AddParents(const std::string &parents); + void AddParents(int parent); + void AddParents(std::vector parents); + void AddParentspID(std::vector parents); + */ + void SetPtHigh(double); + void SetPtLow(double); + void SetPtHighLow(double, double); + + void SetPHigh(double); + void SetPLow(double); + void SetPHighLow(double, double); + + void SetEtaHigh(double); + void SetEtaLow(double); + void SetEtaHighLow(double, double); + + void SetAbsEtaHigh(double); + void SetAbsEtaLow(double); + void SetAbsEtaHighLow(double, double); + + void SetPzHigh(double); + void SetPzLow(double); + void SetPzHighLow(double, double); + + /** + * Restrict particle selection to stable particles when enabled. + * @param b If `true`, only stable particles are considered; if `false`, both stable and unstable particles are allowed. + */ +void SetStableParticleOnly(bool b) { m_doStableParticleOnly = b; } + + private: + bool isGoodEvent(HepMC::GenEvent* e1); + std::vector getParticles(HepMC::GenEvent* e1); + int particleAboveThreshold(const std::map& n_particles, int particle); + // std::vector _theParentsi {}; + std::vector _theParticles{}; + bool m_doStableParticleOnly{true}; + float threshold{0.}; + int goal_event_number{1000}; + /** + * @brief Total number of events processed by this instance. + * + * Counts every event examined by process_event; used for bookkeeping and progress/statistics. + */ +int n_evts{0}; + /** + * Number of events that met the trigger selection criteria. + */ +int n_good{0}; + bool set_event_limit{false}; + + float _theEtaHigh{1.1}; + float _theEtaLow{-1.1}; + float _thePtHigh{999.9}; + float _thePtLow{-999.9}; + float _thePHigh{999.9}; + float _thePLow{-999.9}; + float _thePzHigh{999.9}; + float _thePzLow{-999.9}; + + bool _doEtaHighCut{true}; + bool _doEtaLowCut{true}; + bool _doBothEtaCut{true}; + + bool _doAbsEtaHighCut{false}; + bool _doAbsEtaLowCut{false}; + bool _doBothAbsEtaCut{false}; + + bool _doPtHighCut{false}; + bool _doPtLowCut{false}; + bool _doBothPtCut{false}; + + bool _doPHighCut{false}; + bool _doPLowCut{false}; + bool _doBothPCut{false}; + + bool _doPzHighCut{false}; + bool _doPzLowCut{false}; + bool _doBothPzCut{false}; +}; + +#endif // HEPMCPARTICLETRIGGER_H \ No newline at end of file diff --git a/generators/PHPythia8/PHPythia8.cc b/generators/PHPythia8/PHPythia8.cc index 9aa14d4684..c844354019 100644 --- a/generators/PHPythia8/PHPythia8.cc +++ b/generators/PHPythia8/PHPythia8.cc @@ -33,6 +33,20 @@ #include #include // for operator<<, endl +/** + * @brief Construct a PHPythia8 generator instance and configure HepMC conversion. + * + * Initializes the Pythia8 engine using the path from the environment variable + * `PYTHIA8`, configures a HepMC::Pythia8ToHepMC converter to store process, + * PDF, and cross-section information, and sets the default embedding ID to 1. + * The constructor preserves and restores std::cout formatting around Pythia8 + * construction to avoid altering global stream state. + * + * If `PYTHIA8` is not set, an error message is printed and the Pythia8 instance + * remains uninitialized. + * + * @param name Name forwarded to the SubsysReco base class (module instance name). + */ PHPythia8::PHPythia8(const std::string &name) : SubsysReco(name) { @@ -45,8 +59,12 @@ PHPythia8::PHPythia8(const std::string &name) std::string thePath(charPath); thePath += "/xmldoc/"; + // the pythia8 ctor messes with the formatting, so we save the cout state here + // and restore it later + std::ios old_state(nullptr); + old_state.copyfmt(std::cout); m_Pythia8.reset(new Pythia8::Pythia(thePath)); - + std::cout.copyfmt(old_state); m_Pythia8ToHepMC.reset(new HepMC::Pythia8ToHepMC()); m_Pythia8ToHepMC->set_store_proc(true); m_Pythia8ToHepMC->set_store_pdf(true); @@ -55,6 +73,18 @@ PHPythia8::PHPythia8(const std::string &name) PHHepMCGenHelper::set_embedding_id(1); // default embedding ID to 1 } +/** + * @brief Initialize the Pythia8 generator, configure nodes, and seed the RNG. + * + * Performs module initialization: reads an optional configuration file and any + * queued Pythia command strings, creates the required node tree under the + * provided top-level node, sets Pythia's random seed (mapped from PHRandomSeed + * into Pythia's valid range) and prints it for reproducibility, then calls + * Pythia8::init(). + * + * @param topNode Top-level PHCompositeNode under which generator nodes are created. + * @return int Fun4All return code; returns Fun4AllReturnCodes::EVENT_OK on success. + */ int PHPythia8::Init(PHCompositeNode *topNode) { if (!m_ConfigFileName.empty()) @@ -92,8 +122,15 @@ int PHPythia8::Init(PHCompositeNode *topNode) // print out seed so we can make this is reproducible std::cout << "PHPythia8 random seed: " << seed << std::endl; + +// pythia again messes with the cout formatting + std::ios old_state(nullptr); + old_state.copyfmt(std::cout); // save current state + m_Pythia8->init(); + std::cout.copyfmt(old_state); // restore state to saved state + return Fun4AllReturnCodes::EVENT_OK; } @@ -160,6 +197,18 @@ void PHPythia8::print_config() const m_Pythia8->info.list(); } +/** + * @brief Generate a Pythia8 event, apply configured triggers, convert to HepMC, and record statistics. + * + * Produces Pythia8 events until the module's trigger logic accepts one, converts the accepted event + * to a HepMC::GenEvent (optionally storing the Pythia event weight), inserts it into the framework's + * HepMC record, increments the internal event counter, and updates the optional integrated-luminosity + * statistics node. + * + * @param /*topNode*/ Unused in this implementation (node tree is accessed/created elsewhere). + * @return int `Fun4AllReturnCodes::EVENT_OK` on successful processing of an accepted event, + * `Fun4AllReturnCodes::ABORTRUN` if the HepMC event could not be inserted into the framework record. + */ int PHPythia8::process_event(PHCompositeNode * /*topNode*/) { if (Verbosity() >= VERBOSITY_MORE) @@ -170,6 +219,9 @@ int PHPythia8::process_event(PHCompositeNode * /*topNode*/) bool passedGen = false; bool passedTrigger = false; // int genCounter = 0; +// pythia again messes with the cout formatting in its event loop + std::ios old_state(nullptr); + old_state.copyfmt(std::cout); // save current state while (!passedTrigger) { @@ -245,6 +297,7 @@ int PHPythia8::process_event(PHCompositeNode * /*topNode*/) if (!success) { std::cout << "PHPythia8::process_event - Failed to add event to HepMC record!" << std::endl; + std::cout.copyfmt(old_state); // restore state to saved state return Fun4AllReturnCodes::ABORTRUN; } @@ -265,6 +318,8 @@ int PHPythia8::process_event(PHCompositeNode * /*topNode*/) ++m_EventCount; + std::cout.copyfmt(old_state); // restore state to saved state + // save statistics if (m_IntegralNode) { @@ -321,4 +376,4 @@ void PHPythia8::register_trigger(PHPy8GenTrigger *theTrigger) std::cout << "PHPythia8::registerTrigger - trigger " << theTrigger->GetName() << " registered" << std::endl; } m_RegisteredTriggers.push_back(theTrigger); -} +} \ No newline at end of file diff --git a/generators/phhepmc/PHHepMCGenEventv1.cc b/generators/phhepmc/PHHepMCGenEventv1.cc index bc8eef6f41..744e54b410 100644 --- a/generators/phhepmc/PHHepMCGenEventv1.cc +++ b/generators/phhepmc/PHHepMCGenEventv1.cc @@ -9,6 +9,7 @@ #include #include // for cout +#include #include // for map #include #include // for swap @@ -101,6 +102,14 @@ CLHEP::HepLorentzRotation PHHepMCGenEventv1::get_LorentzRotation_Lab2EvtGen() co return get_LorentzRotation_EvtGen2Lab().inverse(); } +/** + * @brief Retrieve the stored flow reaction-plane angle psi_n for the given harmonic order. + * + * @param n Harmonic order key for the requested reaction-plane angle. + * @return float The psi_n angle (radians) if present, `NaN` otherwise. + * + * If the requested entry is missing, a warning is written to stdout and `NaN` is returned. + */ float PHHepMCGenEventv1::get_flow_psi(unsigned int n) const { auto it = m_psi_n.find(n); @@ -109,6 +118,6 @@ float PHHepMCGenEventv1::get_flow_psi(unsigned int n) const return it->second; } - std::cout << "PHHepMCGenEventv1::get_flow_psi - Warning - requested reaction plane angle psi_n for n=" << n << " does not exist. Returning 0.0" << std::endl; - return 0.0F; -} + std::cout << "PHHepMCGenEventv1::get_flow_psi - Warning - requested reaction plane angle psi_n for n=" << n << " does not exist. Returning NAN" << std::endl; + return std::numeric_limits::quiet_NaN(); +} \ No newline at end of file diff --git a/offline/QA/Calorimeters/CaloValid.cc b/offline/QA/Calorimeters/CaloValid.cc index 2451672807..11befc0da6 100644 --- a/offline/QA/Calorimeters/CaloValid.cc +++ b/offline/QA/Calorimeters/CaloValid.cc @@ -142,6 +142,18 @@ int CaloValid::process_event(PHCompositeNode* topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Process calorimeter towers, trigger information, and clusters and fill QA histograms for the current event. + * + * This routine reads event header, vertex, GL1 trigger packet, calibrated and raw tower containers + * for CEMC/IHCal/OHCal, MBD PMTs, and CEMC clusters; it computes per-event totals, applies + * thresholds and QA gating, builds pi0 candidates from cluster pairs when appropriate, and + * populates the module's monitoring histograms and profiles (energy, time, eta-phi maps, + * correlations, per-channel pedestals/energies, pi0 masses, and trigger-related quantities). + * + * @param topNode Top-level node pointer providing access to event data nodes (event header, towers, clusters, geometry, triggers, MBD, etc.). + * @return Fun4AllReturnCodes::EVENT_OK on successful processing; returns 0 if required cluster or geometry nodes are missing (fatal for cluster-based steps). + */ int CaloValid::process_towers(PHCompositeNode* topNode) { //---------------------------Event header--------------------------------// @@ -335,13 +347,16 @@ int CaloValid::process_towers(PHCompositeNode* topNode) status = status >> 1U; // clang-tidy mark 1 as unsigned } - totalcemc += offlineenergy; + if (isGood) + { + totalcemc += offlineenergy; + } h_emcaltime->Fill(_timef); if (offlineenergy > emcal_hit_threshold) { h_cemc_etaphi_time->Fill(ieta, iphi, _timef); h_cemc_etaphi->Fill(ieta, iphi); - if (isGood && (scaledBits[10] || scaledBits[11])) + if (isGood && (scaledBits[10] || scaledBits[12])) { h_cemc_etaphi_wQA->Fill(ieta, iphi, offlineenergy); } @@ -407,14 +422,17 @@ int CaloValid::process_towers(PHCompositeNode* topNode) status = status >> 1U; // clang-tidy mark 1 as unsigned } - totalihcal += offlineenergy; + if (isGood) + { + totalihcal += offlineenergy; + } h_ihcaltime->Fill(_timef); if (offlineenergy > ihcal_hit_threshold) { h_ihcal_etaphi->Fill(ieta, iphi); h_ihcal_etaphi_time->Fill(ieta, iphi, _timef); - if (isGood && (scaledBits[10] || scaledBits[11])) + if (isGood && (scaledBits[10] || scaledBits[12])) { h_ihcal_etaphi_wQA->Fill(ieta, iphi, offlineenergy); } @@ -472,14 +490,17 @@ int CaloValid::process_towers(PHCompositeNode* topNode) status = status >> 1U; // clang-tidy mark 1 as unsigned } - totalohcal += offlineenergy; + if (isGood) + { + totalohcal += offlineenergy; + } h_ohcaltime->Fill(_timef); if (offlineenergy > ohcal_hit_threshold) { h_ohcal_etaphi_time->Fill(ieta, iphi, _timef); h_ohcal_etaphi->Fill(ieta, iphi); - if (isGood && (scaledBits[10] || scaledBits[11])) + if (isGood && (scaledBits[10] || scaledBits[12])) { h_ohcal_etaphi_wQA->Fill(ieta, iphi, offlineenergy); } @@ -1292,4 +1313,4 @@ void CaloValid::createHistos() } hm->registerHisto(h_triggerVec); hm->registerHisto(pr_ldClus_trig); -} +} \ No newline at end of file diff --git a/offline/QA/Tracking/MicromegasClusterQA.cc b/offline/QA/Tracking/MicromegasClusterQA.cc index 41efeffaa7..b0b714e454 100644 --- a/offline/QA/Tracking/MicromegasClusterQA.cc +++ b/offline/QA/Tracking/MicromegasClusterQA.cc @@ -61,7 +61,19 @@ MicromegasClusterQA::MicromegasClusterQA(const std::string& name) { } -//____________________________________________________________________________.. +/** + * @brief Initialize configuration, geometry mapping, and QA histograms. + * + * Loads calibration data if a calibration filename is provided, prints runtime + * configuration values, reads the Micromegas cylinder geometry to determine + * the number of layers and per-tile detector names, records the first layer, + * and creates QA histograms. + * + * Populates member state including m_calibration_data (when loaded), + * m_nlayers, m_detector_names, and m_firstlayer, then calls create_histograms(). + * + * @return int Fun4AllReturnCodes::EVENT_OK on success. + */ int MicromegasClusterQA::InitRun(PHCompositeNode* topNode) { // print configuration @@ -73,6 +85,9 @@ int MicromegasClusterQA::InitRun(PHCompositeNode* topNode) << (m_calibration_filename.empty() ? "unspecified" : m_calibration_filename) << std::endl; + std::cout << "MicromegasClusterQA::InitRun - m_sample_min: " << m_sample_min << std::endl; + std::cout << "MicromegasClusterQA::InitRun - m_sample_max: " << m_sample_max << std::endl; + // read calibrations if (!m_calibration_filename.empty()) { @@ -115,7 +130,20 @@ int MicromegasClusterQA::InitRun(PHCompositeNode* topNode) return Fun4AllReturnCodes::EVENT_OK; } -//____________________________________________________________________________.. +/** + * @brief Process Micromegas clusters for the current event and fill QA histograms. + * + * Iterates Micromegas hitsets and their clusters to compute per-detector cluster + * multiplicity, size, and charge. Clusters whose associated hits contain no + * sample within [m_sample_min, m_sample_max) are skipped. Per-detector counts + * of "good" clusters (cluster charge > 200) are accumulated and used to fill + * reference/found comparison histograms. All results are recorded in the + * module's QA histograms. + * + * @param topNode Top-level node of the Fun4All node tree (provides geometry, + * hitsets, clusters, and cluster-hit associations). + * @return Fun4AllReturnCodes::EVENT_OK on success. + */ int MicromegasClusterQA::process_event(PHCompositeNode* topNode) { // acts geometry @@ -162,6 +190,13 @@ int MicromegasClusterQA::process_event(PHCompositeNode* topNode) // find associated hits const auto hit_range = m_cluster_hit_map->getHits(ckey); + // check hit samples + // if none of the associated hits' sample is within acceptable range, skip the cluster + if( std::none_of( hit_range.first, hit_range.second, + [this]( const TrkrClusterHitAssoc::Map::value_type& pair ) + { return MicromegasDefs::getSample( pair.second ) >= m_sample_min && MicromegasDefs::getSample( pair.second ) < m_sample_max; } ) ) + { continue; } + // store cluster size and fill cluster size histogram const int cluster_size = std::distance(hit_range.first, hit_range.second); m_h_cluster_size->Fill(detid, cluster_size); @@ -269,4 +304,4 @@ void MicromegasClusterQA::create_histograms() } return; -} +} \ No newline at end of file diff --git a/offline/QA/Tracking/MicromegasClusterQA.h b/offline/QA/Tracking/MicromegasClusterQA.h index 338e5302de..7f646598c0 100644 --- a/offline/QA/Tracking/MicromegasClusterQA.h +++ b/offline/QA/Tracking/MicromegasClusterQA.h @@ -23,6 +23,26 @@ class TH2; class PHCompositeNode; +/** + * Set the pedestal value to use when calibration data is not applied. + * @param value Pedestal ADC value to apply as the default. + */ +/** + * Enable or disable using the default pedestal value instead of calibration data. + * @param value `true` to use the default pedestal, `false` to use calibration data when available. + */ +/** + * Specify a calibration file to load micromegas calibration data from. + * @param value Path to the calibration file. + */ +/** + * Set the minimum sample index to consider as part of a signal hit. + * @param value Minimum sample index (inclusive). + */ +/** + * Set the maximum sample index to consider as part of a signal hit. + * @param value Maximum sample index (inclusive). + */ class MicromegasClusterQA : public SubsysReco { public: @@ -51,6 +71,13 @@ class MicromegasClusterQA : public SubsysReco m_calibration_filename = value; } + /// set min sample for signal hits + void set_sample_min(uint16_t value) { m_sample_min = value; } + + /// set max sample for signal hits + void set_sample_max(uint16_t value) { m_sample_max = value; } + + private: void create_histograms(); @@ -98,6 +125,12 @@ class MicromegasClusterQA : public SubsysReco /// keep track of detector names std::vector m_detector_names; + /// min sample for signal + uint16_t m_sample_min = 0; + + /// max sample for signal + uint16_t m_sample_max = 1024; + ///@name calibration filename //@{ @@ -116,4 +149,4 @@ class MicromegasClusterQA : public SubsysReco //@} }; -#endif // MicromegasClusterQA_H +#endif // MicromegasClusterQA_H \ No newline at end of file diff --git a/offline/QA/Tracking/StateClusterResidualsQA.cc b/offline/QA/Tracking/StateClusterResidualsQA.cc new file mode 100644 index 0000000000..890ce893ea --- /dev/null +++ b/offline/QA/Tracking/StateClusterResidualsQA.cc @@ -0,0 +1,318 @@ +#include "StateClusterResidualsQA.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include + +namespace +{ + template + /** + * @brief Computes the square of a value. + * + * @tparam T Type of the value; must support multiplication. + * @param t Value to be squared. + * @return T The product of `t` with itself. + */ +inline T square (T const& t) { return t * t; } + + template + class range_adaptor + { + public: + /** + * @brief Construct a range adaptor from a pair of iterators. + * + * Stores the provided begin and end iterators/keys for later use by begin() and end(). + * + * @param begin Iterator or key pointing to the start of the range. + * @param end Iterator or key pointing one past the end of the range. + */ + explicit range_adaptor( + T const& begin, + T const& end) + : m_begin(begin) + , m_end(end) + { + } + /** + * @brief Accesses the stored begin iterator. + * + * @return T const& A const reference to the stored begin iterator or key. + */ +T const& begin() { return m_begin; } + /** + * @brief Access the stored end iterator or key. + * + * @return T const& Reference to the stored end iterator or key. + */ +T const& end() { return m_end; } + + private: + T m_begin; + T m_end; + }; +} /** + * @brief Construct a StateClusterResidualsQA module with the specified name. + * + * The provided name is used to identify this QA module instance within the + * Fun4All/SubsysReco framework. + * + * @param name Module name used for identification and bookkeeping. + */ + +StateClusterResidualsQA::StateClusterResidualsQA(const std::string& name) + : SubsysReco(name) +{ +} + +/** + * @brief Initialize the QA module for a run and bind required resources. + * + * Creates histograms and resolves required nodes from the provided top-level + * node tree. Populates the internal histogram vectors with handles retrieved + * from the QA histogram manager. + * + * @param top_node Top-level PHCompositeNode used to look up detector, cluster, + * and geometry nodes. + * @return int `EVENT_OK` on success; `ABORTRUN` if a required node or the QA + * histogram manager could not be retrieved. + */ +int StateClusterResidualsQA::InitRun( + PHCompositeNode* top_node) +{ + createHistos(); + + // F4A will not actually ABORTRUN unless that return code is issued here + auto* track_map = findNode::getClass(top_node, m_track_map_node_name); + if (!track_map) + { + std::cout + << PHWHERE << "\n" + << "\tCould not get track map:\n" + << "\t\"" << m_track_map_node_name << "\"\n" + << "\tAborting\n" + << std::endl; + return Fun4AllReturnCodes::ABORTRUN; + } + + auto* cluster_map = findNode::getClass(top_node, m_clusterContainerName); + if (!cluster_map) + { + std::cout + << PHWHERE << "\n" + << "\tCould not get cluster map:\n" + << "\t\"" << m_clusterContainerName << "\"\n" + << "\tAborting\n" + << std::endl; + return Fun4AllReturnCodes::ABORTRUN; + } + + auto *geometry = findNode::getClass(top_node, "ActsGeometry"); + if (!geometry) + { + std::cout + << PHWHERE << "\n" + << "\tCould not get ActsGeometry:\n" + << "\t\"" << "ActsGeometry" << "\"\n" + << "\tAborting\n" + << std::endl; + return Fun4AllReturnCodes::ABORTRUN; + } + + auto* hm = QAHistManagerDef::getHistoManager(); + if (!hm) + { + std::cout + << PHWHERE << "\n" + << "\tCould not get QAHistManager\n" + << "\tAborting\n" + << std::endl; + return Fun4AllReturnCodes::ABORTRUN; + } + + for (const auto& cfg : m_pending) + { + m_histograms_x.push_back(dynamic_cast(hm->getHisto(std::string(cfg.name + "_x")))); + m_histograms_y.push_back(dynamic_cast(hm->getHisto(std::string(cfg.name + "_y")))); + m_histograms_z.push_back(dynamic_cast(hm->getHisto(std::string(cfg.name + "_z")))); + } + + return Fun4AllReturnCodes::EVENT_OK; +} + +/** + * @brief Process one event: fill state–cluster residual histograms for tracks matching configured selections. + * + * Iterates all tracks in the SvtxTrackMap, counts per-detector clusters per track, applies per-configuration + * selection cuts (charge, per-detector cluster count ranges, phi/eta/pt ranges), and for tracks that pass, + * computes residuals between track state positions and corresponding cluster global positions (state - cluster) + * in x, y, and z and fills the configured histograms. The initial vertex state (path_length == 0) is skipped. + * + * @param top_node Top-level node of the Fun4All node tree from which required data (track map, cluster container, + * geometry) are retrieved. + * @return int Fun4All return code: `EVENT_OK` on success. + */ +int StateClusterResidualsQA::process_event(PHCompositeNode* top_node) +{ + auto* track_map = findNode::getClass(top_node, m_track_map_node_name); + auto *cluster_map = findNode::getClass(top_node, m_clusterContainerName); + auto *geometry = findNode::getClass(top_node, "ActsGeometry"); + + for (auto const& [idkey, track] : *track_map) + { + if (!track) + { + continue; + } + + // count states + std::map counters = { + {TrkrDefs::mvtxId, 0}, + {TrkrDefs::inttId, 0}, + {TrkrDefs::tpcId, 0}, + {TrkrDefs::micromegasId, 0}, + }; + + for (auto const& [path_length, state] : range_adaptor(track->begin_states(), track->end_states())) + { + // There is an additional state representing the vertex at the beginning of the map, + // but getTrkrId will return 0 for its corresponding cluster + // Identify it as having path_length identically equal to 0 + if (path_length == 0) { continue; } + + auto trkr_id = static_cast(TrkrDefs::getTrkrId(state->get_cluskey())); + auto itr = counters.find(trkr_id); + if (itr == counters.end()) { continue; } + ++itr->second; + } + + float track_eta = track->get_eta(); + float track_phi = track->get_phi(); + float track_pt = track->get_pt(); + int h = 0; + for (const auto& cfg : m_pending) + { + if (cfg.charge != 0) + { + if ((cfg.charge < 0) && track->get_positive_charge()) + { + continue; + } + if ((cfg.charge > 0) && !(track->get_positive_charge())) + { + continue; + } + } + if (cfg.min_mvtx_clusters <= counters[TrkrDefs::mvtxId] && cfg.max_mvtx_clusters >= counters[TrkrDefs::mvtxId] + && cfg.min_intt_clusters <= counters[TrkrDefs::inttId] && cfg.max_intt_clusters >= counters[TrkrDefs::inttId] + && cfg.min_tpc_clusters <= counters[TrkrDefs::tpcId] && cfg.max_tpc_clusters >= counters[TrkrDefs::tpcId] + && cfg.phi_min <= track_phi && cfg.phi_max >= track_phi + && cfg.eta_min <= track_eta && cfg.eta_max >= track_eta + && cfg.pt_min <= track_pt && cfg.pt_max >= track_pt) + { + for (auto const& [path_length, state] : range_adaptor(track->begin_states(), track->end_states())) + { + if (path_length == 0) { continue; } + + auto *cluster = cluster_map->findCluster(state->get_cluskey()); + float state_x = state->get_x(); + float state_y = state->get_y(); + float state_z = state->get_z(); + Acts::Vector3 glob = geometry->getGlobalPosition(state->get_cluskey(), cluster); + float cluster_x = glob.x(); + float cluster_y = glob.y(); + float cluster_z = glob.z(); + if (cluster) + { + m_histograms_x[h]->Fill(state_x - cluster_x); + m_histograms_y[h]->Fill(state_y - cluster_y); + m_histograms_z[h]->Fill(state_z - cluster_z); + } + } + } + ++h; + } + } + + return Fun4AllReturnCodes::EVENT_OK; +} + +/** + * @brief Create and register residual histograms for each pending configuration. + * + * For each entry in m_pending this creates three TH1F histograms named + * "_x", "_y", and "_z" for the State-Cluster + * residuals in X, Y, and Z (units: cm). Each histogram is initialized with + * m_nBins and the corresponding axis range (m_xrange, m_yrange, m_zrange) and + * styled with blue marker and line color before being registered with the QA + * histogram manager. + */ +void StateClusterResidualsQA::createHistos() +{ + auto *hm = QAHistManagerDef::getHistoManager(); + assert(hm); + + for (const auto& cfg : m_pending) + { + TH1F* h_new_x = new TH1F( + (cfg.name + "_x").c_str(), + ";State-Cluster X Residual [cm];Entries", + m_nBins, m_xrange.first, m_xrange.second); + h_new_x->SetMarkerColor(kBlue); + h_new_x->SetLineColor(kBlue); + hm->registerHisto(h_new_x); + TH1F* h_new_y = new TH1F( + (cfg.name + "_y").c_str(), + ";State-Cluster Y Residual [cm];Entries", + m_nBins, m_yrange.first, m_yrange.second); + h_new_y->SetMarkerColor(kBlue); + h_new_y->SetLineColor(kBlue); + hm->registerHisto(h_new_y); + TH1F* h_new_z = new TH1F( + (cfg.name + "_z").c_str(), + ";State-Cluster Z Residual [cm];Entries", + m_nBins, m_zrange.first, m_zrange.second); + h_new_z->SetMarkerColor(kBlue); + h_new_z->SetLineColor(kBlue); + hm->registerHisto(h_new_z); + } +} + +/** + * @brief Finalize the QA module at the end of a run. + * + * Asserts that the QA histogram manager is available. + * + * @return Fun4AllReturnCodes::EVENT_OK on success. + */ +int StateClusterResidualsQA::EndRun(const int /*unused*/) +{ + auto *hm = QAHistManagerDef::getHistoManager(); + assert(hm); + + return Fun4AllReturnCodes::EVENT_OK; +} \ No newline at end of file diff --git a/offline/QA/Tracking/StateClusterResidualsQA.h b/offline/QA/Tracking/StateClusterResidualsQA.h new file mode 100644 index 0000000000..ff02a210ac --- /dev/null +++ b/offline/QA/Tracking/StateClusterResidualsQA.h @@ -0,0 +1,131 @@ +// Tell emacs that this is a C++ source +// -*- C++ -*-. +#ifndef STATECLUSTERRESIDUALSQA_H +#define STATECLUSTERRESIDUALSQA_H + +#include + +#include +#include +#include +#include +#include + +class PHCompositeNode; +class TH1; + +struct ResidualHistConfig +{ + std::string name = "h_StateClusterResidualsQA_"; + std::string title = ";Residual [cm];Entries"; + + int min_mvtx_clusters = 0; + int max_mvtx_clusters = 3; + int min_intt_clusters = 0; + int max_intt_clusters = 4; + int min_tpc_clusters = 0; + int max_tpc_clusters = 48; + + float phi_min = -M_PI; + float phi_max = M_PI; + float eta_min = -1.1; + float eta_max = 1.1; + + float pt_min = 0.0; + float pt_max = FLT_MAX; + + int charge = 0; +}; + +class StateClusterResidualsQA : public SubsysReco +{ + public: + StateClusterResidualsQA(const std::string& name = "StateClusterResidualsQA"); + ~StateClusterResidualsQA() override = default; + + /** + * Specify the node name used to retrieve the track map. + * + * @param track_map_node_name Node name to look up the track map; defaults to "SvtxTrackMap" if not changed. + */ + void set_track_map_name(std::string const& track_map_node_name) { m_track_map_node_name = track_map_node_name; } + + StateClusterResidualsQA& addHistogram(const std::string& name) + { + ResidualHistConfig cfg; + cfg.name += name; + m_pending.push_back(cfg); + return *this; + } + StateClusterResidualsQA& setNMvtx(int min, int max) + { + m_pending.back().min_mvtx_clusters = min; + m_pending.back().max_mvtx_clusters = max; + return *this; + } + StateClusterResidualsQA& setNIntt(int min, int max) + { + m_pending.back().min_intt_clusters = min; + m_pending.back().max_intt_clusters = max; + return *this; + } + StateClusterResidualsQA& setNTpc(int min, int max) + { + m_pending.back().min_tpc_clusters = min; + m_pending.back().max_tpc_clusters = max; + return *this; + } + StateClusterResidualsQA& setPhiRange(float min, float max) + { + m_pending.back().phi_min = min; + m_pending.back().phi_max = max; + return *this; + } + StateClusterResidualsQA& setEtaRange(float min, float max) + { + m_pending.back().eta_min = min; + m_pending.back().eta_max = max; + return *this; + } + StateClusterResidualsQA& setPtRange(float min, float max) + { + m_pending.back().pt_min = min; + m_pending.back().pt_max = max; + return *this; + } + StateClusterResidualsQA& setPositiveTracks() + { + m_pending.back().charge = 1; + return *this; + } + StateClusterResidualsQA& setNegativeTracks() + { + m_pending.back().charge = -1; + return *this; + } + + void createHistos(); + + int InitRun(PHCompositeNode*) override; + + int process_event(PHCompositeNode*) override; + + int EndRun(const int runnumber) override; + + private: + std::vector m_pending; + + std::string m_track_map_node_name = "SvtxTrackMap"; + std::string m_clusterContainerName = "TRKR_CLUSTER"; + + int m_nBins = 50; + std::pair m_xrange {-0.5,0.5}; + std::pair m_yrange {-0.5,0.5}; + std::pair m_zrange {-0.5,0.5}; + + std::vector m_histograms_x{}; + std::vector m_histograms_y{}; + std::vector m_histograms_z{}; +}; + +#endif // TRACKFITTINGQA_H \ No newline at end of file diff --git a/offline/framework/ffamodules/CDBInterface.cc b/offline/framework/ffamodules/CDBInterface.cc index 2b4feef203..a7fb1ad9b2 100644 --- a/offline/framework/ffamodules/CDBInterface.cc +++ b/offline/framework/ffamodules/CDBInterface.cc @@ -120,6 +120,24 @@ void CDBInterface::Print(const std::string & /* what */) const } } +/** + * @brief Retrieve and cache a calibration file URL for the specified domain. + * + * Queries the configured CDB client for a calibration URL associated with the given + * domain at the current TIMESTAMP. On success the returned URL is recorded in the + * internal URL vector for later persistence. Behavior is affected by module flags: + * - If the internal `disable` flag is true, the function returns an empty string immediately. + * - If no URL is found and `disable_default` is false, the function will attempt the + * domain suffixed with `_default` and, if still not found, return the provided filename. + * - If required recoConsts flags ("CDB_GLOBALTAG" or "TIMESTAMP") are missing the process + * exits via gSystem->Exit(1). + * + * @param domain Calibration domain to query (e.g., subsystem/key). + * @param filename Fallback filename to return when no URL is found and defaults are allowed. + * @return std::string The resolved calibration URL if found; the provided `filename` when no URL + * exists and defaults are allowed; or an empty string if lookups are disabled or no URL + * was found and defaults are disabled. + */ std::string CDBInterface::getUrl(const std::string &domain, const std::string &filename) { if (disable) @@ -185,11 +203,14 @@ std::string CDBInterface::getUrl(const std::string &domain, const std::string &f std::cout << "... reply: " << return_url << std::endl; } } - auto pret = m_UrlVector.insert(make_tuple(domain_noconst, return_url, timestamp)); - if (!pret.second && Verbosity() > 1) + if (! return_url.empty()) { - std::cout << PHWHERE << "not adding again " << domain_noconst << ", url: " << return_url - << ", time stamp: " << timestamp << std::endl; + auto pret = m_UrlVector.insert(make_tuple(domain_noconst, return_url, timestamp)); + if (!pret.second && Verbosity() > 1) + { + std::cout << PHWHERE << "not adding again " << domain_noconst << ", url: " << return_url + << ", time stamp: " << timestamp << std::endl; + } } return return_url; -} +} \ No newline at end of file diff --git a/offline/framework/ffamodules/FlagHandler.h b/offline/framework/ffamodules/FlagHandler.h index f5cc06bcdb..74f1568b3e 100644 --- a/offline/framework/ffamodules/FlagHandler.h +++ b/offline/framework/ffamodules/FlagHandler.h @@ -7,14 +7,32 @@ #include -class PHCompositeNode; - +/** + * Ensure the Flag node exists in the node tree and synchronize any existing flags + * into the reconstruction constants. + * @param topNode Top-level node of the run's node tree. + * @returns Status code: `0` on success, non-zero on failure. + */ +/** + * Perform end-of-processing tasks for the FlagHandler. + * @param topNode Top-level node of the run's node tree. + * @returns Status code: `0` on success, non-zero on failure. + */ +/** + * Print FlagHandler information filtered by the specified scope. + * @param what Scope of information to print (default: "ALL"). + */ +/** + * Update the run node with the current flag state. + * @param topNode Top-level node of the run's node tree. + * @returns Status code: `0` on success, non-zero on failure. + */ class FlagHandler : public SubsysReco { public: FlagHandler(const std::string &name = "FlagHandler"); - ~FlagHandler() override {} + ~FlagHandler() override = default; /** Create the Flag Node if it does not exist, if it exists, read back flags and copy them into recoConsts @@ -31,4 +49,4 @@ class FlagHandler : public SubsysReco private: }; -#endif // FFAMODULES_FLAGHANDLER_H +#endif // FFAMODULES_FLAGHANDLER_H \ No newline at end of file diff --git a/offline/framework/ffamodules/HeadReco.cc b/offline/framework/ffamodules/HeadReco.cc index 75e879fdff..b1241a22fd 100644 --- a/offline/framework/ffamodules/HeadReco.cc +++ b/offline/framework/ffamodules/HeadReco.cc @@ -61,6 +61,20 @@ int HeadReco::InitRun(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Populate the EventHeader from available event metadata nodes. + * + * Reads the PHHepMCGenEventMap (if present) and, for the first foreground + * GenEvent encountered in reverse order, extracts HeavyIon information to set + * ImpactParameter, EventPlaneAngle, FlowPsiN (n=1..6 when flow data exists), + * eccentricity, Ncoll, and total Npart in the EventHeader. If the HepMC map is + * absent, reads the PRDF Event node to set EvtType. Always sets RunNumber and + * EvtSequence from the Fun4AllServer and, when verbosity is enabled, calls + * identify() on the EventHeader. + * + * @param topNode Root of the node tree used to locate EventHeader, PHHepMCGenEventMap, and PRDF Event nodes. + * @return int Fun4AllReturnCodes::EVENT_OK on success. + */ int HeadReco::process_event(PHCompositeNode *topNode) { Fun4AllServer *se = Fun4AllServer::instance(); @@ -84,10 +98,13 @@ int HeadReco::process_event(PHCompositeNode *topNode) { evtheader->set_ImpactParameter(hi->impact_parameter()); evtheader->set_EventPlaneAngle(hi->event_plane_angle()); - for (unsigned int n = 1; n <= 6; ++n) - { - evtheader->set_FlowPsiN(n, genevt->get_flow_psi(n)); - } + if (! genevt->get_flow_psi_map().empty()) + { + for (unsigned int n = 1; n <= 6; ++n) + { + evtheader->set_FlowPsiN(n, genevt->get_flow_psi(n)); + } + } evtheader->set_eccentricity(hi->eccentricity()); evtheader->set_ncoll(hi->Ncoll()); evtheader->set_npart(hi->Npart_targ() + hi->Npart_proj()); @@ -112,4 +129,4 @@ int HeadReco::process_event(PHCompositeNode *topNode) } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/framework/ffamodules/HeadReco.h b/offline/framework/ffamodules/HeadReco.h index ca2f6cf34e..462cbc795b 100644 --- a/offline/framework/ffamodules/HeadReco.h +++ b/offline/framework/ffamodules/HeadReco.h @@ -7,13 +7,51 @@ #include // for string -class PHCompositeNode; - +/** + * HeadReco subsystem for event reconstruction within the FUN4ALL framework. + * + * Performs initialization and per-run setup, and processes each event to + * execute the subsystem's reconstruction tasks. + */ + +/** + * Construct a HeadReco subsystem. + * + * @param name Optional name for the subsystem instance; defaults to "HeadReco". + */ + +/** + * Destroy the HeadReco subsystem. + */ + +/** + * Perform one-time initialization for the subsystem using the node tree. + * + * @param topNode Root node of the PHENIX/FUN4ALL node tree used to locate + * input/output nodes and services required by the subsystem. + * @returns 0 on success, non-zero error code on failure. + */ + +/** + * Perform per-run initialization using the node tree. + * + * @param topNode Root node of the PHENIX/FUN4ALL node tree used to locate + * run-dependent nodes and configuration. + * @returns 0 on success, non-zero error code on failure. + */ + +/** + * Process a single event using the node tree. + * + * @param topNode Root node of the PHENIX/FUN4ALL node tree containing event + * data and output containers manipulated by this subsystem. + * @returns 0 on success, non-zero error code on failure. + */ class HeadReco : public SubsysReco { public: HeadReco(const std::string &name = "HeadReco"); - ~HeadReco() override {} + ~HeadReco() override = default; int Init(PHCompositeNode *topNode) override; int InitRun(PHCompositeNode *topNode) override; int process_event(PHCompositeNode *topNode) override; @@ -21,4 +59,4 @@ class HeadReco : public SubsysReco protected: }; -#endif +#endif \ No newline at end of file diff --git a/offline/framework/ffamodules/SyncReco.h b/offline/framework/ffamodules/SyncReco.h index 0a490302a0..43bf9e218d 100644 --- a/offline/framework/ffamodules/SyncReco.h +++ b/offline/framework/ffamodules/SyncReco.h @@ -5,13 +5,44 @@ #include -class PHCompositeNode; - +/** + * Construct a SyncReco module with an optional instance name. + * @param name Instance name used to identify the module. + */ + +/** + * Initialize module-level resources and create required node structure if needed. + * @param topNode Pointer to the top node of the Fun4All node tree. + * @returns `0` on success, non-zero on error. + */ + +/** + * Perform run-specific initialization before processing events. + * @param topNode Pointer to the top node of the Fun4All node tree. + * @returns `0` on success, non-zero on error. + */ + +/** + * Process a single event. + * @param topNode Pointer to the top node of the Fun4All node tree. + * @returns `0` on success, non-zero on error. + */ + +/** + * Override the segment number used by the module. + * @param i Segment number to force; use `-1` to unset the override. + */ + +/** + * Create the required node tree under the provided top node. + * @param topNode Pointer to the top node of the Fun4All node tree. + * @returns `0` on success, non-zero on error. + */ class SyncReco : public SubsysReco { public: SyncReco(const std::string &name = "SYNC"); - ~SyncReco() override {} + ~SyncReco() override = default; int Init(PHCompositeNode *topNode) override; int InitRun(PHCompositeNode *topNode) override; @@ -24,7 +55,7 @@ class SyncReco : public SubsysReco // just if we need to override the segment for e.g. embedding // where we want to reuse hijing files which normally set // the segment number - int forced_segment = -1; + int forced_segment {-1}; }; -#endif /* FFAMODULES_SYNCRECO_H */ +#endif /* FFAMODULES_SYNCRECO_H */ \ No newline at end of file diff --git a/offline/framework/ffamodules/Timing.cc b/offline/framework/ffamodules/Timing.cc index f662ba1b33..3fab3db491 100644 --- a/offline/framework/ffamodules/Timing.cc +++ b/offline/framework/ffamodules/Timing.cc @@ -3,10 +3,13 @@ #include #include // for SubsysReco -#include - #include +/** + * @brief Construct a Timing subsystem with the specified name. + * + * @param name Identifier for this Timing instance used to label the subsystem. + */ Timing::Timing(const std::string &name) : SubsysReco(name) { @@ -30,4 +33,4 @@ int Timing::process_event(PHCompositeNode * /*topNode*/) call_counter = 0; } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/framework/ffamodules/Timing.h b/offline/framework/ffamodules/Timing.h index 01b95638e0..cdac1dcaa3 100644 --- a/offline/framework/ffamodules/Timing.h +++ b/offline/framework/ffamodules/Timing.h @@ -8,16 +8,18 @@ #include // for string #include -class PHCompositeNode; - class Timing : public SubsysReco { public: Timing(const std::string &name = "Timing"); - ~Timing() override {} - int InitRun(PHCompositeNode *topNode) override; - int process_event(PHCompositeNode *topNode) override; - void SetCallCounter(unsigned int i) { calls = i; } + ~Timing() override = default; + int InitRun(PHCompositeNode * /*topNode*/) override; + int process_event(PHCompositeNode * /*topNode*/) override; + /** + * Set the target number of calls used by the Timing module. + * @param i Target number of calls; replaces the internal `calls` limit. + */ +void SetCallCounter(unsigned int i) { calls = i; } private: unsigned int call_counter{0}; @@ -26,4 +28,4 @@ class Timing : public SubsysReco time_t starttime{0}; }; -#endif +#endif \ No newline at end of file diff --git a/offline/framework/fun4all/Fun4AllServer.cc b/offline/framework/fun4all/Fun4AllServer.cc index 9d8779204e..657964ec48 100644 --- a/offline/framework/fun4all/Fun4AllServer.cc +++ b/offline/framework/fun4all/Fun4AllServer.cc @@ -120,6 +120,14 @@ Fun4AllServer::~Fun4AllServer() return; } +/** + * @brief Initialize core server state, managers, and top-level node tree. + * + * Performs global startup actions required before processing runs and events: + * disables ROOT signal handlers, snapshots startup monitoring, saves cout formatting state, + * creates and registers the server histogram manager and FrameWorkVars histogram, + * constructs the default sync manager, and creates the TOP node with its subtree. + */ void Fun4AllServer::InitAll() { // first remove stupid root signal handler to get @@ -128,9 +136,9 @@ void Fun4AllServer::InitAll() { gSystem->IgnoreSignal((ESignals) i); } + m_saved_cout_state.copyfmt(std::cout); // save current state Fun4AllMonitoring::instance()->Snapshot("StartUp"); - std::string histomanagername; - histomanagername = Name() + "HISTOS"; + std::string histomanagername = Name() + "HISTOS"; ServerHistoManager = new Fun4AllHistoManager(histomanagername); registerHistoManager(ServerHistoManager); double uplim = NFRAMEWORKBINS - 0.5; @@ -176,6 +184,18 @@ int Fun4AllServer::isHistoRegistered(const std::string &name) const return iret; } +/** + * @brief Register a SubsysReco instance with the server under a named top node. + * + * Creates (if necessary) ROOT TDirectory subdirectories for the top node and the + * subsystem, calls the subsystem's Init() with the resolved top node, and on + * successful Init stores the subsystem with its target top node, creates a + * per-subsystem timer entry, and records the subsystem return code. + * + * @param subsystem Pointer to the SubsysReco to register. + * @param topnodename Name of the top-level node under which the subsystem's node subtree will be placed. + * @return int `0` on successful registration; if the subsystem's Init() returns DONOTREGISTERSUBSYSTEM the call is treated as success and returns `0`; any other nonzero value from Init() is returned to indicate failure. + */ int Fun4AllServer::registerSubsystem(SubsysReco *subsystem, const std::string &topnodename) { Fun4AllServer *se = Fun4AllServer::instance(); @@ -245,6 +265,7 @@ int Fun4AllServer::registerSubsystem(SubsysReco *subsystem, const std::string &t << subsystem->Name() << std::endl; exit(1); } + std::cout.copyfmt(m_saved_cout_state); // restore cout to default formatting gROOT->cd(currdir.c_str()); if (iret) { @@ -503,6 +524,26 @@ TNamed *Fun4AllServer::getHisto(const std::string &hname) const return (ServerHistoManager->getHisto(hname)); } +/** + * @brief Process a single event through the framework. + * + * Runs the event loop for one event: increments internal counters, invokes + * each registered subsystem's process_event, records subsystem return codes, + * writes output managers and histogram managers as required, resets per-event + * state (subsystems and sync managers), updates monitoring, and resets the + * transient node tree. + * + * The method enforces subsystem return-code semantics (e.g., discard, abort + * event/run/processing), enforces output-node consistency before writing, and + * updates run and histogram state when files are rotated or closed. + * + * @return int One of the Fun4AllReturnCodes values: + * - `Fun4AllReturnCodes::EVENT_OK` on successful processing of the event, + * - `Fun4AllReturnCodes::DISCARDEVENT` if the event was discarded, + * - `Fun4AllReturnCodes::ABORTEVENT` if a subsystem requested aborting this event, + * - `Fun4AllReturnCodes::ABORTRUN` if a subsystem requested aborting the run, + * - `Fun4AllReturnCodes::ABORTPROCESSING` if processing must stop entirely. + */ int Fun4AllServer::process_event() { eventcounter++; @@ -576,6 +617,7 @@ int Fun4AllServer::process_event() ffamemtracker->Snapshot("Fun4AllServerProcessEvent"); #endif int retcode = Subsystem.first->process_event(Subsystem.second); + std::cout.copyfmt(m_saved_cout_state); // restore cout to default formatting #ifdef FFAMEMTRACKER ffamemtracker->Snapshot("Fun4AllServerProcessEvent"); #endif @@ -854,6 +896,17 @@ int Fun4AllServer::BeginRunTimeStamp(PHTimeStamp &TimeStp) return 0; } +/** + * @brief Prepare and start a new run identified by the given run number. + * + * Resets the per-run event counter, updates or preserves the begin-of-run timestamp, + * invokes BeginRun on all currently registered subsystems (and any subsystems + * added during this phase), processes any pending subsystem unregistrations, + * and prints the node tree state. + * + * @param runno Run number to begin. + * @return int The return code from the last subsystem's BeginRun call (0 if no subsystem invoked). + */ int Fun4AllServer::BeginRun(const int runno) { eventcounter = 0; // reset event counter for every new run @@ -899,6 +952,7 @@ int Fun4AllServer::BeginRun(const int runno) for (iter = Subsystems.begin(); iter != Subsystems.end(); ++iter) { iret = BeginRunSubsystem(*iter); + std::cout.copyfmt(m_saved_cout_state); // restore cout to default formatting } for (; !NewSubsystems.empty(); NewSubsystems.pop_front()) { @@ -1048,6 +1102,16 @@ int Fun4AllServer::MakeNodesPersistent(PHCompositeNode *startNode) // NOLINT(mi return 0; } +/** + * @brief Finalizes all registered subsystems for the given run. + * + * Iterates over each registered SubsysReco, switches to the subsystem's TDirectory, + * and calls its EndRun(runno) method. Restores stdout formatting after each subsystem + * and returns to the previous ROOT directory when finished. + * + * @param runno The run number to end. + * @return int `0` on success. + */ int Fun4AllServer::EndRun(const int runno) { std::vector>::iterator iter; @@ -1092,12 +1156,23 @@ int Fun4AllServer::EndRun(const int runno) << (*iter).first->Name() << std::endl; exit(1); } + std::cout.copyfmt(m_saved_cout_state); // restore cout to default formatting } gROOT->cd(currdir.c_str()); return 0; } +/** + * @brief Finalize the current run and shut down all registered subsystems. + * + * Calls EndRun for the active run, invokes each registered subsystem's End + * method, writes the RUN node to any registered output managers, closes + * output files, dumps histograms according to each histogram manager's file + * rules, and prints any accumulated complaints collected via ScreamEveryEvent. + * + * @return int Sum of return values produced by all SubsysReco::End calls. + */ int Fun4AllServer::End() { recoConsts *rc = recoConsts::instance(); @@ -1144,6 +1219,7 @@ int Fun4AllServer::End() << (*iter).first->Name() << std::endl; exit(1); } + std::cout.copyfmt(m_saved_cout_state); // restore cout to default formatting } gROOT->cd(currdir.c_str()); PHNodeIterator nodeiter(TopNode); @@ -1820,4 +1896,4 @@ int Fun4AllServer::UpdateRunNode() iret += Subsystem.first->UpdateRunNode(Subsystem.second); } return iret; -} +} \ No newline at end of file diff --git a/offline/framework/fun4all/Fun4AllServer.h b/offline/framework/fun4all/Fun4AllServer.h index 0c7ea72c14..974c72f69a 100644 --- a/offline/framework/fun4all/Fun4AllServer.h +++ b/offline/framework/fun4all/Fun4AllServer.h @@ -140,10 +140,27 @@ class Fun4AllServer : public Fun4AllBase int ScreamEveryEvent{0}; int unregistersubsystem{0}; int runnumber{0}; - int eventnumber{0}; - int eventcounter{0}; - int keep_db_connected{0}; - + /** + * Current event number within the active run. + * + * Holds the index of the event currently being processed; updated as events are processed. + */ +int eventnumber{0}; + /** + * Tracks the number of events processed by the server. + * + * This counter is incremented as events are processed and is initialized to 0. + */ +int eventcounter{0}; + /** + * @brief Controls whether the database connection is kept open between operations. + * + * When non-zero, the server preserves the database connection across operations; when zero, + * the server may close the connection when it is no longer needed. + */ +int keep_db_connected{0}; + + std::ios m_saved_cout_state{nullptr}; std::vector ComplaintList; std::vector ResetNodeList {"DST"}; std::vector> Subsystems; @@ -160,4 +177,4 @@ class Fun4AllServer : public Fun4AllBase std::map timer_map; }; -#endif +#endif \ No newline at end of file diff --git a/offline/framework/fun4all/InputFileHandler.cc b/offline/framework/fun4all/InputFileHandler.cc index 8939b67568..42e2064008 100644 --- a/offline/framework/fun4all/InputFileHandler.cc +++ b/offline/framework/fun4all/InputFileHandler.cc @@ -3,11 +3,23 @@ #include +#include + #include #include #include #include +#include +/** + * @brief Add a filename to the handler's internal file lists. + * + * Appends the provided filename to both the active file list and the backup copy. + * If verbosity is greater than zero, a message indicating the addition may be printed. + * + * @param filename Name or path of the file to add. + * @return int `0` on success. + */ int InputFileHandler::AddFile(const std::string &filename) { if (GetVerbosity() > 0) @@ -80,6 +92,14 @@ int InputFileHandler::AddListFile(const std::string &filename) return 0; } +/** + * @brief Open the next available file from the internal file list, invoking a configured pre-opening script if present. + * + * Iterates the queued file list until a file is successfully opened or the list is exhausted. + * If an opening script is configured, calls RunBeforeOpening with the current filename and optional target name; failure of that call is logged but does not prevent attempting to open the file. If opening a file fails, the filename is removed from the list and iteration continues. + * + * @return InputFileHandlerReturnCodes::SUCCESS if a file was opened, InputFileHandlerReturnCodes::FAILURE if no files could be opened. + */ int InputFileHandler::OpenNextFile() { while (!m_FileList.empty()) @@ -89,6 +109,19 @@ int InputFileHandler::OpenNextFile() { std::cout << PHWHERE << " opening next file: " << *iter << std::endl; } + if (!GetOpeningScript().empty()) + { + std::vector stringvec; + stringvec.push_back(*iter); + if (!m_FileName.empty()) + { + stringvec.push_back(m_FileName); + } + if (RunBeforeOpening(stringvec)) + { + std::cout << PHWHERE << " RunBeforeOpening() failed" << std::endl; + } + } if (fileopen(*iter)) { std::cout << PHWHERE << " could not open file: " << *iter << std::endl; @@ -140,8 +173,63 @@ int InputFileHandler::ResetFileList() return 0; } +/** + * @brief Open the specified input file. + * + * Attempts to open the file identified by `fname` and report the action. + * + * @param fname Path to the file to open. + * @return int `0` on success, non-zero on failure. + */ int InputFileHandler::fileopen(const std::string &fname) { std::cout << "InputFileHandler::fileopen opening " << fname << std::endl; return 0; } + +/** + * @brief Run the configured pre-opening script with supplied file arguments. + * + * If a pre-opening script is configured, constructs a command consisting of the + * script path, the configured opening arguments, and the elements of + * `stringvec`, then executes that command and returns the resulting exit status. + * + * @param stringvec Additional arguments appended to the script invocation (typically filenames or targets). + * @return int `0` if no script is configured or the script exited with status 0; a positive integer exit status returned by the script otherwise; `-1` if the configured script is missing or not owner-executable. + */ +int InputFileHandler::RunBeforeOpening(const std::vector &stringvec) +{ + if (m_RunBeforeOpeningScript.empty()) + { + return 0; + } + if (!std::filesystem::exists(m_RunBeforeOpeningScript)) + { + std::cout << PHWHERE << " script " << m_RunBeforeOpeningScript << " not found" + << std::endl; + return -1; + } + if (!((std::filesystem::status(m_RunBeforeOpeningScript).permissions() & std::filesystem::perms::owner_exec) == std::filesystem::perms::owner_exec)) + { + std::cout << PHWHERE << "RunBeforeOpeningScript script " + << m_RunBeforeOpeningScript << " is not owner executable" << std::endl; + return -1; + } + std::string fullcmd = m_RunBeforeOpeningScript + " " + m_OpeningArgs; + for (const auto& iter : stringvec) + { + fullcmd += " " + iter; + } + + if (m_Verbosity > 1) + { + std::cout << PHWHERE << " running " << fullcmd << std::endl; + } + unsigned int iret = gSystem->Exec(fullcmd.c_str()); + + if (iret) + { + iret = iret >> 8U; + } + return static_cast (iret); +} \ No newline at end of file diff --git a/offline/framework/fun4all/InputFileHandler.h b/offline/framework/fun4all/InputFileHandler.h index 823ae33b38..7a2047dad0 100644 --- a/offline/framework/fun4all/InputFileHandler.h +++ b/offline/framework/fun4all/InputFileHandler.h @@ -4,13 +4,84 @@ #include #include #include +#include -class InputFileHandler +/** + * Attempt to open the specified file for processing. + * @param filename Name of the file to open. + * @returns `0` on success, a negative error code on failure. + */ + /** + * Close the currently opened file. + * @returns `-1` by default; override to return `0` on success or a negative error code on failure. + */ + /** + * Reset the internal file list state to its initial condition. + * @returns `0` on success, a negative error code on failure. + */ + /** + * Advance to and open the next file from the internal file list. + * @returns `0` on success, a negative error code if no file could be opened or on error. + */ + /** + * Add all filenames listed in the provided file to the internal file list. + * @param filename Path to a file that contains a list of filenames to add. + * @returns `0` on success, a negative error code on failure. + */ + /** + * Add a single filename to the internal file list. + * @param filename Filename to append to the file list. + * @returns `0` on success, a negative error code on failure. + */ + /** + * Record a filename as having been opened during this run. + * @param filename Filename to append to the opened-files record. + */ + /** + * Print internal state or file lists. + * @param what Determines what to print; implementation-specific values (default: "ALL"). + */ + /** + * Get whether a file is currently open. + * @returns `m_IsOpen` (nonzero if a file is open, `0` otherwise). + */ + /** + * Set the open state indicator. + * @param i New open state value (`0` for closed, nonzero for open). + */ + /** + * Set the verbosity level used for logging or printing. + * @param i Verbosity level. + */ + /** + * Get the current verbosity level. + * @returns The verbosity level. + */ + /** + * Refresh or rebuild the internal file list from configured sources. + */ + /** + * Set the current filename in focus. + * @param fn Filename to set as current. + */ + /** + * Get the current filename in focus. + * @returns The current filename. + */ + /** + * Check whether the internal file list is empty. + * @returns `true` if the file list contains no entries, `false` otherwise. + */ + /** + * Set how many times files should be repeated when iterating. + * @param i Repeat count (`-1` indicates default/unspecified behavior). + */ + class InputFileHandler { public: InputFileHandler() = default; virtual ~InputFileHandler() = default; - virtual int fileopen(const std::string & /*filename*/);// { return 0; } + virtual int fileopen(const std::string & /*filename*/); // { return 0; } virtual int fileclose() { return -1; } virtual int ResetFileList(); @@ -32,15 +103,39 @@ class InputFileHandler std::pair::const_iterator, std::list::const_iterator> FileOpenListBeginEnd() { return std::make_pair(m_FileListOpened.begin(), m_FileListOpened.end()); } const std::list &GetFileList() const { return m_FileListCopy; } const std::list &GetFileOpenedList() const { return m_FileListOpened; } + /** + * Set the script to run before opening files. + * @param script Path or command string to be executed prior to opening files. + */ +void SetOpeningScript(const std::string &script) { m_RunBeforeOpeningScript = script; } + const std::string &GetOpeningScript() const { return m_RunBeforeOpeningScript; } + /** + * Set the arguments for the opening script. + * @param args A single string containing arguments that will be supplied to the opening script; stored internally. + */ +void SetOpeningScriptArgs(const std::string &args) { m_OpeningArgs = args; } + const std::string &GetOpeningScriptArgs() const { return m_OpeningArgs; } + int RunBeforeOpening(const std::vector &stringvec); private: int m_IsOpen{0}; - int m_Repeat{0}; - uint64_t m_Verbosity{0}; + /** + * Number of times to repeat processing the file list. + */ +int m_Repeat{0}; + /** + * Verbosity level used to control amount of diagnostic or informational output. + * + * Higher values produce more detailed output; treat as an application-defined, + * unitless verbosity/verbosity-mask value. + */ +uint64_t m_Verbosity{0}; std::string m_FileName; + std::string m_RunBeforeOpeningScript; + std::string m_OpeningArgs; std::list m_FileList; std::list m_FileListCopy; std::list m_FileListOpened; // all files which were opened during running }; -#endif +#endif \ No newline at end of file diff --git a/offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc b/offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc index 78b3e3a365..08c40eb006 100644 --- a/offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc +++ b/offline/framework/fun4allraw/Fun4AllStreamingInputManager.cc @@ -642,6 +642,15 @@ int Fun4AllStreamingInputManager::FillGl1() return 0; } +/** + * @brief Populate the INTTRAWHIT node with INTT hits within the configured BCO window and update INTT QA histograms. + * + * Determines the selection window from the configured INTT BCO range and the manager's reference BCO (sets the reference BCO from the earliest available INTT entry if it is zero). + * Cleans up and skips INTT entries that are earlier than the allowed negative offset, computes per-packet and per-fee QA metrics (filling bco-difference and tagging histograms), + * and appends all INTT hits whose BCOs fall within the selection window to the InttRawHitContainer found under the top node. + * + * @return 0 on success, or a non-zero error code propagated from FillInttPool() if pool filling or readiness checks fail. + */ int Fun4AllStreamingInputManager::FillIntt() { int iret = FillInttPool(); @@ -776,9 +785,15 @@ int Fun4AllStreamingInputManager::FillIntt() { h_taggedAllFee_intt->Fill(refbcobitshift); } - while (m_InttRawHitMap.begin()->first <= select_crossings - m_intt_negative_bco) + + for (auto& [bco, hitinfo] : m_InttRawHitMap) { - for (auto *intthititer : m_InttRawHitMap.begin()->second.InttRawHitVector) + if (bco > select_crossings) + { + break; + } + + for (auto *intthititer : hitinfo.InttRawHitVector) { if (Verbosity() > 1) { @@ -788,25 +803,24 @@ int Fun4AllStreamingInputManager::FillIntt() } inttcont->AddHit(intthititer); } - for (auto *iter : m_InttInputVector) - { - iter->CleanupUsedPackets(m_InttRawHitMap.begin()->first); - if (m_intt_negative_bco < 2) // triggered mode - { - iter->clearPacketBClkStackMap(m_InttRawHitMap.begin()->first); - iter->clearFeeGTML1BCOMap(m_InttRawHitMap.begin()->first); - } - } - m_InttRawHitMap.begin()->second.InttRawHitVector.clear(); - m_InttRawHitMap.erase(m_InttRawHitMap.begin()); - if (m_InttRawHitMap.empty()) - { - break; - } - } + } return 0; } - +/** + * @brief Populate MVTX raw event header and hit container from buffered MVTX data and update MVTX QA histograms. + * + * Processes MVTX raw-hit buffers relative to the current reference BCO (m_RefBCO), selects the BCO window to include, + * emits FeeId info and L1 trigger BCOs into the MVTX raw event header, moves MVTX raw hits into the MVTX raw hit container, + * and fills related QA histograms used for FELIX/fee/LL1/GL1 tagging and BCO-difference monitoring. + * + * Side effects: + * - May update m_RefBCO. + * - Consumes/cleans buffered MVTX packets via the registered MVTX pool inputs. + * - Writes into nodes "MVTXRAWEVTHEADER" and "MVTXRAWHIT" (or the input-provided node names). + * - Exits the process if required MVTX nodes are not found. + * + * @return int 0 on success, non-zero error code propagated from FillMvtxPool() on failure. + */ int Fun4AllStreamingInputManager::FillMvtx() { int iret = FillMvtxPool(); @@ -846,7 +860,7 @@ int Fun4AllStreamingInputManager::FillMvtx() } select_crossings += m_RefBCO; - uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_bco_range ? 0 : m_RefBCO - m_mvtx_bco_range; + uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_negative_bco ? 0 : m_RefBCO - m_mvtx_negative_bco; if (Verbosity() > 2) { std::cout << "select MVTX crossings" @@ -981,90 +995,42 @@ int Fun4AllStreamingInputManager::FillMvtx() } taggedPacketsFEEs.clear(); - if (m_mvtx_is_triggered) + uint64_t lower_limit = m_mvtx_is_triggered ? select_crossings : select_crossings - m_mvtx_bco_range - m_mvtx_negative_bco; + uint64_t upper_limit = m_mvtx_is_triggered ? select_crossings + m_mvtx_bco_range : select_crossings; + + for (auto& [bco, hitinfo] : m_MvtxRawHitMap) { - while (select_crossings <= m_MvtxRawHitMap.begin()->first && m_MvtxRawHitMap.begin()->first <= select_crossings + m_mvtx_bco_range) // triggered + if (bco < lower_limit) { - if (Verbosity() > 2) - { - std::cout << "Adding 0x" << std::hex << m_MvtxRawHitMap.begin()->first - << " ref: 0x" << select_crossings << std::dec << std::endl; - } - for (auto *mvtxFeeIdInfo : m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector) - { - if (Verbosity() > 1) - { - mvtxFeeIdInfo->identify(); - } - mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo); - delete mvtxFeeIdInfo; - } - m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector.clear(); - mvtxEvtHeader->AddL1Trg(m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco); + continue; + } + if (bco > upper_limit) + { + break; + } - for (auto *mvtxhititer : m_MvtxRawHitMap.begin()->second.MvtxRawHitVector) - { - if (Verbosity() > 1) - { - mvtxhititer->identify(); - } - mvtxcont->AddHit(mvtxhititer); - } - for (auto *iter : m_MvtxInputVector) - { - iter->CleanupUsedPackets(m_MvtxRawHitMap.begin()->first); - } - m_MvtxRawHitMap.begin()->second.MvtxRawHitVector.clear(); - m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco.clear(); - m_MvtxRawHitMap.erase(m_MvtxRawHitMap.begin()); - // m_MvtxRawHitMap.empty() need to be checked here since we do not call FillPoolMvtx() - if (m_MvtxRawHitMap.empty()) - { - break; - } + if (Verbosity() > 2) + { + std::cout << "Adding 0x" << std::hex << bco + << " ref: 0x" << select_crossings << std::dec << std::endl; } - } - else - { - while (select_crossings - m_mvtx_bco_range - m_mvtx_negative_bco <= m_MvtxRawHitMap.begin()->first && m_MvtxRawHitMap.begin()->first <= select_crossings) // streamed + for (auto *mvtxFeeIdInfo : hitinfo.MvtxFeeIdInfoVector) { - if (Verbosity() > 2) - { - std::cout << "Adding 0x" << std::hex << m_MvtxRawHitMap.begin()->first - << " ref: 0x" << select_crossings << std::dec << std::endl; - } - for (auto *mvtxFeeIdInfo : m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector) + if (Verbosity() > 1) { - if (Verbosity() > 1) - { - mvtxFeeIdInfo->identify(); - } - mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo); - delete mvtxFeeIdInfo; + mvtxFeeIdInfo->identify(); } - m_MvtxRawHitMap.begin()->second.MvtxFeeIdInfoVector.clear(); - mvtxEvtHeader->AddL1Trg(m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco); + mvtxEvtHeader->AddFeeIdInfo(mvtxFeeIdInfo); + } + mvtxEvtHeader->AddL1Trg(hitinfo.MvtxL1TrgBco); - for (auto *mvtxhititer : m_MvtxRawHitMap.begin()->second.MvtxRawHitVector) - { - if (Verbosity() > 1) - { - mvtxhititer->identify(); - } - mvtxcont->AddHit(mvtxhititer); - } - for (auto *iter : m_MvtxInputVector) - { - iter->CleanupUsedPackets(m_MvtxRawHitMap.begin()->first); - } - m_MvtxRawHitMap.begin()->second.MvtxRawHitVector.clear(); - m_MvtxRawHitMap.begin()->second.MvtxL1TrgBco.clear(); - m_MvtxRawHitMap.erase(m_MvtxRawHitMap.begin()); - // m_MvtxRawHitMap.empty() need to be checked here since we do not call FillPoolMvtx() - if (m_MvtxRawHitMap.empty()) + for (auto *mvtxhititer : hitinfo.MvtxRawHitVector) + { + if (Verbosity() > 1) { - break; + mvtxhititer->identify(); } + mvtxcont->AddHit(mvtxhititer); } } @@ -1420,9 +1386,19 @@ int Fun4AllStreamingInputManager::FillMicromegasPool() return 0; } +/** + * @brief Request MVTX inputs to populate their hit pools and validate run consistency. + * + * Calls FillPool(ref_bco_minus_range) on each registered MVTX input using a reference BCO + * window computed from the manager's reference BCO and the MVTX negative BCO offset. + * Sets the manager run number from the first input and verifies all inputs share the same + * run number, exiting the process on mismatch. + * + * @return int `-1` if the MVTX raw-hit map is empty (no hits to process), `0` otherwise. + */ int Fun4AllStreamingInputManager::FillMvtxPool() { - uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_bco_range ? m_mvtx_bco_range : m_RefBCO - m_mvtx_bco_range; + uint64_t ref_bco_minus_range = m_RefBCO < m_mvtx_negative_bco ? m_mvtx_negative_bco : m_RefBCO - m_mvtx_negative_bco; for (auto *iter : m_MvtxInputVector) { if (Verbosity() > 3) @@ -1569,4 +1545,4 @@ void Fun4AllStreamingInputManager::createQAHistos() h_tagBcoFelix_mvtx[i] = dynamic_cast(hm->getHisto((boost::format("h_MvtxPoolQA_TagBCO_felix%i") % i).str())); h_tagBcoFelixAllFees_mvtx[i] = dynamic_cast(hm->getHisto((boost::format("h_MvtxPoolQA_TagBCOAllFees_Felix%i") % i).str())); } -} +} \ No newline at end of file diff --git a/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.cc b/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.cc index 7500e3badf..d43cd2c826 100644 --- a/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.cc +++ b/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.cc @@ -524,7 +524,20 @@ void SingleMicromegasPoolInput_v2::FillBcoQA(uint64_t gtm_bco) // how many waveforms found for this BCO h_waveform->Fill(n_waveforms); } -//_______________________________________________________ +/** + * @brief Create and register QA histograms and an optional evaluation TTree for BCO and waveform monitoring. + * + * Creates histograms that track per-GTM-BCO packet and waveform counts, per-packet and per-FEE waveform totals, + * and dropped-waveform statistics (due to BCO mismatch or pool mismatch). The histograms are configured with + * axis labels and registered with the global QA histogram manager. If evaluation is enabled, opens the + * evaluation ROOT file and creates a TTree with branches that record heartbeat flags, matching state, packet/fee + * identifiers, channel, GTM/fee BCOs and predicted fee BCOs for later analysis. + * + * Side effects: + * - Registers multiple TH1 histograms with QAHistManagerDef::getHistoManager(). + * - When m_do_evaluation is true, creates or overwrites the evaluation TFile and instantiates m_evaluation_tree + * with branches bound to m_waveform fields. + */ void SingleMicromegasPoolInput_v2::createQAHistos() { auto* hm = QAHistManagerDef::getHistoManager(); @@ -607,6 +620,7 @@ void SingleMicromegasPoolInput_v2::createQAHistos() m_evaluation_file.reset(new TFile(m_evaluation_filename.c_str(), "RECREATE")); m_evaluation_tree = new TTree("T", "T"); m_evaluation_tree->Branch("is_heartbeat", &m_waveform.is_heartbeat); + m_evaluation_tree->Branch("matched", &m_waveform.matched); m_evaluation_tree->Branch("packet_id", &m_waveform.packet_id); m_evaluation_tree->Branch("fee_id", &m_waveform.fee_id); m_evaluation_tree->Branch("channel", &m_waveform.channel); @@ -788,7 +802,20 @@ void SingleMicromegasPoolInput_v2::decode_gtm_data(int packet_id, const SingleMi } } -//____________________________________________________________________ +/** + * @brief Process a single FEE data stream for a given packet and FEE. + * + * Decodes and validates FEE payloads from the per-FEE data buffer, extracts waveform(s) + * and metadata, attempts to match the FEE BCO to a GTM BCO, and on successful match + * creates and stores MicromegasRawHit objects. Updates per-packet and per-FEE QA + * counters and histograms, records heartbeat statistics, and (when enabled) fills + * the evaluation TTree with matching information. Waveforms that fail BCO matching + * or are identified as heartbeats are handled according to QA and evaluation rules + * (they may be counted as dropped and not stored as hits). + * + * @param packet_id Identifier of the packet containing the FEE payloads. + * @param fee_id Index of the FEE whose buffered data will be processed. + */ void SingleMicromegasPoolInput_v2::process_fee_data(int packet_id, unsigned int fee_id) { // get bco information @@ -912,6 +939,13 @@ void SingleMicromegasPoolInput_v2::process_fee_data(int packet_id, unsigned int // try get gtm bco matching fee const auto& fee_bco = payload.bx_timestamp; + if( m_do_evaluation ) + { + m_waveform.is_heartbeat = is_heartbeat; + m_waveform.fee_id = fee_id; + m_waveform.channel = payload.channel; + m_waveform.fee_bco = fee_bco; + } // find matching gtm bco uint64_t gtm_bco = 0; @@ -920,9 +954,20 @@ void SingleMicromegasPoolInput_v2::process_fee_data(int packet_id, unsigned int { // assign gtm bco gtm_bco = result.value(); - } - else - { + if( m_do_evaluation ) + { + m_waveform.matched = true; + m_waveform.gtm_bco_matched = gtm_bco; + { + const auto predicted = bco_matching_information.get_predicted_fee_bco(gtm_bco);; + if( predicted ) + { + m_waveform.fee_bco_predicted_matched = predicted.value(); + } + } + m_evaluation_tree->Fill(); + } + } else { // increment counter and histogram ++m_waveform_counters[packet_id].dropped_bco; ++m_fee_waveform_counters[fee_id].dropped_bco; @@ -935,28 +980,16 @@ void SingleMicromegasPoolInput_v2::process_fee_data(int packet_id, unsigned int ++m_fee_heartbeat_counters[fee_id].dropped_bco; } - // skip the waverform - continue; - } - - if (m_do_evaluation) - { - m_waveform.is_heartbeat = (payload.type == HEARTBEAT_T); - m_waveform.fee_id = fee_id; - m_waveform.channel = payload.channel; - m_waveform.fee_bco = fee_bco; - - m_waveform.gtm_bco_matched = gtm_bco; + if( m_do_evaluation ) { - const auto predicted = bco_matching_information.get_predicted_fee_bco(gtm_bco); - ; - if (predicted) - { - m_waveform.fee_bco_predicted_matched = predicted.value(); - } + m_waveform.matched = false; + m_waveform.gtm_bco_matched = 0; + m_waveform.fee_bco_predicted_matched = 0; + m_evaluation_tree->Fill(); } - m_evaluation_tree->Fill(); + // skip the waverform + continue; } // ignore heartbeat waveforms @@ -1024,4 +1057,4 @@ void SingleMicromegasPoolInput_v2::process_fee_data(int packet_id, unsigned int m_MicromegasRawHitMap[gtm_bco].push_back(newhit.release()); } -} +} \ No newline at end of file diff --git a/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.h b/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.h index d8c3c1da62..3b2b567792 100644 --- a/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.h +++ b/offline/framework/fun4allraw/SingleMicromegasPoolInput_v2.h @@ -212,8 +212,25 @@ class SingleMicromegasPoolInput_v2 : public SingleStreamingInput std::unique_ptr m_evaluation_file; /** - * waveform is similar to sample except that there is only one of which per waveform, - * and that it stores the max adc and corresponding sample_id + * Holds metadata for a single waveform: identifiers, timing (BCO) values, and matching state. + * + * Contains per-waveform information required for matching and evaluation, including the + * packet/FEE/channel identifiers, heartbeat and matched flags, GTM (global) BCOs and + * the corresponding FEE (local) BCO values and predictions. + * + * Members: + * - packet_id: packet identifier. + * - fee_id: FEE identifier. + * - channel: channel identifier within the FEE. + * - is_heartbeat: true if the waveform is a heartbeat measurement. + * - matched: true if this waveform has been matched to a GTM/FEE BCO. + * - gtm_bco_first: first observed GTM BCO for this waveform. + * - gtm_bco: GTM BCO associated with the waveform. + * - gtm_bco_matched: GTM BCO value used for a successful match. + * - fee_bco_first: first observed FEE BCO for this waveform. + * - fee_bco: FEE BCO associated with the waveform. + * - fee_bco_predicted: FEE BCO predicted from GTM timing. + * - fee_bco_predicted_matched: FEE BCO value from prediction used for a successful match. */ class Waveform { @@ -230,7 +247,15 @@ class SingleMicromegasPoolInput_v2 : public SingleStreamingInput /// true if measurement is hearbeat bool is_heartbeat = false; - /// ll1 bco + /// true if matched + bool matched = false; + + /** + * GTM beam-clock counter (BCO) from the first GTM packet associated with this waveform. + * + * Represents the Global Trigger Module BCO value observed when the waveform was first seen; + * contains 0 if no GTM BCO has been recorded. + */ uint64_t gtm_bco_first {0}; /// ll1 bco @@ -260,4 +285,4 @@ class SingleMicromegasPoolInput_v2 : public SingleStreamingInput //*} }; -#endif +#endif \ No newline at end of file diff --git a/offline/framework/fun4allraw/SingleMvtxPoolInput.cc b/offline/framework/fun4allraw/SingleMvtxPoolInput.cc index de98a9936e..ac66a5a76e 100644 --- a/offline/framework/fun4allraw/SingleMvtxPoolInput.cc +++ b/offline/framework/fun4allraw/SingleMvtxPoolInput.cc @@ -1,7 +1,7 @@ #include "SingleMvtxPoolInput.h" -#include "MvtxRawDefs.h" #include "Fun4AllStreamingInputManager.h" +#include "MvtxRawDefs.h" #include "mvtx_pool.h" #include @@ -28,8 +28,18 @@ #include #include +/** + * @brief Construct a SingleMvtxPoolInput and initialize its basic state. + * + * Initializes the base SingleStreamingInput with the provided name, allocates + * an internal two-element packet pointer array, and sets the default MVTX raw + * hit container name to "MVTXRAWHIT". + * + * @param name Identifier used to initialize the underlying streaming input. + */ SingleMvtxPoolInput::SingleMvtxPoolInput(const std::string &name) - : SingleStreamingInput(name), plist(new Packet *[2]) + : SingleStreamingInput(name) + , plist(new Packet *[2]) { m_rawHitContainerName = "MVTXRAWHIT"; @@ -48,6 +58,28 @@ SingleMvtxPoolInput::~SingleMvtxPoolInput() } } +/** + * @brief Populate MVTX pools by reading events and extracting strobes, L1 triggers, and raw hits. + * + * Reads events from the current input file(s), opening subsequent files as needed, and processes + * DATAEVENTs to collect MVTX packets into per-packet pools. For each FEE in each pool the function + * extracts L1 trigger BCOs, strobe BCOs and associated raw hits, updating internal structures and + * forwarding information to the StreamingInputManager when present. + * + * Side effects: + * - May call OpenNextFile()/fileclose() and set AllDone(1) if files cannot be opened. + * - Increments m_NumSpecialEvents for non-DATAEVENTs and updates RunNumber from events. + * - Creates/extends mvtx_pool entries in poolmap and deletes packet pointers after adding them. + * - Updates m_BclkStack, m_FEEBclkMap, m_FeeStrobeMap, m_FeeGTML1BCOMap, and m_MvtxRawHitMap. + * - For each accepted strobe, forwards raw hits via StreamingInputManager()->AddMvtxRawHit and + * fee/strobe metadata via AddMvtxFeeIdInfo; associates L1 triggers to strobes via + * AddMvtxL1TrgBco. + * + * The function ignores strobes whose strobe BCO is less than minBCO and reports an error to + * stdout if an L1 BCO cannot be associated to any existing strobe BCO while the strobe stack is non-empty. + * + * @param minBCO Minimum strobe BCO threshold; strobes with BCO less than this value are skipped. + */ void SingleMvtxPoolInput::FillPool(const uint64_t minBCO) { if (AllDone()) // no more files and all events read @@ -161,7 +193,7 @@ void SingleMvtxPoolInput::FillPool(const uint64_t minBCO) m_BclkStack.insert(strb_bco); m_FEEBclkMap[feeId] = strb_bco; - if (strb_bco < minBCO - m_NegativeBco) + if (strb_bco < minBCO) { continue; } @@ -206,7 +238,7 @@ void SingleMvtxPoolInput::FillPool(const uint64_t minBCO) auto it = m_BclkStack.lower_bound(lv1Bco); // auto const strb_it = (it == m_BclkStack.begin()) ? (*it == lv1Bco ? it : m_BclkStack.cend()) : --it; // this is equivalent but human readable for the above: - auto strb_it = m_BclkStack.cend(); + auto strb_it = m_BclkStack.cend(); if (it == m_BclkStack.begin()) { @@ -435,6 +467,21 @@ void SingleMvtxPoolInput::CreateDSTNode(PHCompositeNode *topNode) } } +/** + * @brief Configure MVTX timing parameters and propagate them to the streaming manager. + * + * Determines the MVTX strobe length (optionally from the DB) and uses it to set + * internal BCO window parameters (m_BcoRange and m_NegativeBco). When operating + * in non-standalone MVTX mode the strobe length is mapped to sensible BCO ranges, + * and triggered mode is enabled when strobe width indicates triggered operation. + * + * This method forwards the resulting BCO range and negative BCO to the + * StreamingInputManager via SetMvtxBcoRange and SetMvtxNegativeBco, and calls + * runMvtxTriggered(true) if triggered mode is selected. + * + * @note If m_readStrWidthFromDB is true and the strobe length is not defined + * for the current run, the process exits with code 1. + */ void SingleMvtxPoolInput::ConfigureStreamingInputManager() { auto [runnumber, segment] = Fun4AllUtils::GetRunSegment(*(GetFileList().begin())); @@ -462,7 +509,7 @@ void SingleMvtxPoolInput::ConfigureStreamingInputManager() else if (m_strobeWidth > 9 && m_strobeWidth < 11) { m_BcoRange = 500; - m_NegativeBco = 500; + m_NegativeBco = 120; } else if (m_strobeWidth < 1) // triggered mode { @@ -492,4 +539,4 @@ void SingleMvtxPoolInput::ConfigureStreamingInputManager() StreamingInputManager()->SetMvtxNegativeBco(m_NegativeBco); } return; -} +} \ No newline at end of file diff --git a/offline/framework/fun4allraw/SingleTriggeredInput.cc b/offline/framework/fun4allraw/SingleTriggeredInput.cc index 3bb6b30a2b..f3a5a4d1f1 100644 --- a/offline/framework/fun4allraw/SingleTriggeredInput.cc +++ b/offline/framework/fun4allraw/SingleTriggeredInput.cc @@ -327,6 +327,13 @@ int SingleTriggeredInput::fileclose() return 0; } +/** + * @brief Populate per-packet event deques up to the configured pool depth. + * + * Fills m_PacketEventDeque for each tracked packet ID by reading events (advancing the active EventIterator and opening subsequent files as needed) until either the pool depth is reached or an existing deque already contains entries. This routine updates internal per-packet clock and diff arrays, may create DST nodes on the first call, handles GL1-driven skip/trace behavior when present, and maintains internal shift/backup state used for alignment. + * + * @return int `-1` if a fatal input-file/open failure occurs; otherwise the number of complete events available in the pool (the minimum size across all per-packet deques after filling). + */ int SingleTriggeredInput::FillEventVector() { while (GetEventIterator() == nullptr) // at startup this is a null pointer @@ -352,6 +359,12 @@ int SingleTriggeredInput::FillEventVector() m_bclkarray_map[pid][0] = tmp; m_bclkdiffarray_map[pid].fill(std::numeric_limits::max()); + static bool firstclockarray=true; + if(firstclockarray){ + std::cout << "first clock call pid " << pid << " m_bclkarray_map[pid][0] : " << m_bclkarray_map[pid][0] << std::endl; + firstclockarray=false; + } + if ( representative_pid == -1 ) { representative_pid = pid; @@ -368,12 +381,17 @@ int SingleTriggeredInput::FillEventVector() while (i < pooldepth) { Event* evt{nullptr}; + bool skiptrace = false; if (this != Gl1Input()) { auto* gl1 = dynamic_cast(Gl1Input()); if (gl1) { int nskip = gl1->GetGl1SkipArray()[i]; + if(nskip >0) + { + skiptrace = true; + } while (nskip > 0) { @@ -391,6 +409,7 @@ int SingleTriggeredInput::FillEventVector() if (skip_evt->getEvtType() != DATAEVENT) { + delete skip_evt; continue; } @@ -412,14 +431,91 @@ int SingleTriggeredInput::FillEventVector() { if (Verbosity() > 0) { - std::cout << Name() << ": Early stop of SEB skip after " << (gl1->GetGl1SkipArray()[i] - nskip) << " from intial " << gl1->GetGl1SkipArray()[i] << " events." << std::endl; + std::cout << Name() << ": Early stop in pool " << i << " of SEB skip after " << (gl1->GetGl1SkipArray()[i] - nskip) << " from intial " << gl1->GetGl1SkipArray()[i] << " events. gl1diff vs sebdiff : " << gl1_diff << " vs " << seb_diff << std::endl; } evt = skip_evt; + skiptrace = false; break; } delete skip_evt; nskip--; } + + if(skiptrace) + { + evt = GetEventIterator()->getNextEvent(); + while (!evt) + { + fileclose(); + if (OpenNextFile() == InputFileHandlerReturnCodes::FAILURE) + { + FilesDone(1); + return -1; + } + evt = GetEventIterator()->getNextEvent(); + } + if (evt->getEvtType() != DATAEVENT) + { + if (Verbosity() > 0) + { + std::cout << Name() << " dropping non data event: " << evt->getEvtSequence() << std::endl; + } + delete evt; + continue; + } + + Packet* pkt = evt->getPacket(representative_pid); + if (!pkt) + { + std::cout << "representative packet invalid inside skiptrace.. continuing.." << std::endl; + continue; + } + FillPacketClock(evt, pkt, i); + uint64_t seb_diff = m_bclkdiffarray_map[representative_pid][i]; + int gl1pid = Gl1Input()->m_bclkdiffarray_map.begin()->first; + uint64_t gl1_diff = gl1->m_bclkdiffarray_map[gl1pid][i]; + + bool clockconsistency=true; + if (seb_diff != gl1_diff) + { + clockconsistency=false; + int clockconstcount = 0; + while(!clockconsistency && clockconstcount<5) + { + std::cout << Name() << ": Still inconsistent clock diff after Gl1 drop. gl1diff vs sebdiff : " << gl1_diff << " vs " << seb_diff << std::endl; + delete pkt; + delete evt; + evt = GetEventIterator()->getNextEvent(); + while (!evt) + { + fileclose(); + if (OpenNextFile() == InputFileHandlerReturnCodes::FAILURE) + { + FilesDone(1); + return -1; + } + evt = GetEventIterator()->getNextEvent(); + } + pkt = evt->getPacket(representative_pid); + if (!pkt) + { + std::cout << "representative packet invalid inside skiptrace.. continuing.." << std::endl; + continue; + } + + FillPacketClock(evt, pkt, i); + uint64_t seb_diff_next = m_bclkdiffarray_map[representative_pid][i]; + uint64_t gl1_diff_next = gl1->m_bclkdiffarray_map[gl1pid][i]; + std::cout << "seb_diff_next : " << seb_diff_next << " , gl1_diff_next : " << gl1_diff_next << std::endl; + if(seb_diff_next == gl1_diff_next) + { + clockconsistency=true; + std::cout << Name() << " : recovered by additional skip in skiptrace" << std::endl; + } + clockconstcount++; + } + } + } } } @@ -485,14 +581,16 @@ int SingleTriggeredInput::FillEventVector() } FillPacketClock(thisevt, pkt, i); m_PacketEventDeque[pid].push_back(thisevt); + delete pkt; - + if (representative_pid == -1 && m_PacketShiftOffset[pid] == 0) { representative_pid = pid; } } i++; + eventcounter++; } size_t minSize = pooldepth; @@ -1079,6 +1177,24 @@ void SingleTriggeredInput::dumpdeque() return; } +/** + * @brief Assemble and output the next combined event from per-packet deques into DST structures. + * + * Populates CaloPacket nodes for the next reference event by combining one Event per configured packet ID: + * - Validates per-packet deques are non-empty and selects the front Event from the first deque as reference. + * - Updates run and event counters and clears the internal FEM event-number set. + * - For each packet that is not flagged as misaligned, validates packet identity, applies ditch/override rules, + * copies BCO/FEM metadata, samples, suppression flags, and module/channel counts into the corresponding CaloPacket + * DST node, and performs FEM event-number/clock consistency checks. + * - Deletes per-event Packet and Event objects as appropriate, advances per-packet deques by one entry, + * and decrements stored ditch indices to reflect the removed leading entry. + * + * The method modifies internal state (run number, FEM event-number set, ditch indices, per-packet deques) + * and may mark an alignment problem when inconsistencies are detected. + * + * @returns int `Fun4AllReturnCodes::EVENT_OK` on successful assembly of an output event; + * `-1` if processing cannot proceed (e.g., any packet deque is empty, or a packet ID mismatch is detected). + */ int SingleTriggeredInput::ReadEvent() { for (const auto& [pid, dq] : m_PacketEventDeque) @@ -1116,16 +1232,15 @@ int SingleTriggeredInput::ReadEvent() [](const std::pair& p) { return p.second == 0; }); std::set events_to_delete; - for (auto& [pid, dq] : m_PacketEventDeque) { if(m_PacketAlignmentProblem[pid]) { continue; } + Event* evt = dq.front(); Packet* packet = evt->getPacket(pid); - int packet_id = packet->getIdentifier(); if (packet_id != pid) { @@ -1137,7 +1252,6 @@ int SingleTriggeredInput::ReadEvent() CaloPacket *newhit = findNode::getClass(m_topNode, packet_id); newhit->Reset(); - if (m_DitchPackets.contains(packet_id) && m_DitchPackets[packet_id].contains(0)) { newhit->setStatus(OfflinePacket::PACKET_DROPPED); @@ -1240,4 +1354,4 @@ int SingleTriggeredInput::ReadEvent() } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/framework/fun4allraw/SingleTriggeredInput.h b/offline/framework/fun4allraw/SingleTriggeredInput.h index 088be73bef..188e4b5845 100644 --- a/offline/framework/fun4allraw/SingleTriggeredInput.h +++ b/offline/framework/fun4allraw/SingleTriggeredInput.h @@ -32,19 +32,51 @@ class SingleTriggeredInput : public Fun4AllBase, public InputFileHandler ~SingleTriggeredInput() override; virtual Eventiterator *GetEventIterator() { return m_EventIterator; } virtual void FillPool(); - virtual void RunNumber(const int runno) { m_RunNumber = runno; } + /** + * Set the current run number used by this input. + * @param runno Run number to assign. + */ +virtual void RunNumber(const int runno) { m_RunNumber = runno; } virtual int RunNumber() const { return m_RunNumber; } - virtual void EventNumber(const int i) { m_EventNumber = i; } - virtual int EventNumber() const { return m_EventNumber; } - virtual int EventsInThisFile() const { return m_EventsThisFile; } virtual int fileopen(const std::string &filename) override; + /** + * Close the currently open input file and release any associated resources. + * + * @returns `0` on success, non-zero error code on failure. + */ + + /** + * Return the internal completion flag indicating whether processing is finished. + * + * @returns The value of the completion flag; nonzero indicates all done, `0` indicates not done. + */ + + /** + * Set the internal completion flag indicating whether processing is finished. + * + * @param i New value for the completion flag; nonzero indicates all done, `0` indicates not done. + */ virtual int fileclose() override; virtual int AllDone() const { return m_AllDone; } virtual void AllDone(const int i) { m_AllDone = i; } virtual int FilesDone() const { return m_FilesDone; } - virtual void FilesDone(const int i) { m_FilesDone = i; } - virtual void EventAlignmentProblem(const int i) { m_EventAlignmentProblem = i; } + /** + * Set the number of input files that have been completed. + * @param i The count of files completed; assigned to the internal files-done counter. + */ +virtual void FilesDone(const int i) { m_FilesDone = i; } + /** + * Set the internal event alignment problem flag. + * @param i New value for the event alignment problem flag. + */ +virtual void EventAlignmentProblem(const int i) { m_EventAlignmentProblem = i; } virtual int EventAlignmentProblem() const { return m_EventAlignmentProblem; } + /** + * Set the current event number. + * @param i The event number to store internally. + */ +virtual void EventNumber(const int i) { m_EventNumber = i; } + virtual int EventNumber() const { return m_EventNumber; } virtual void CreateDSTNodes(Event *evt); // these ones are used directly by the derived classes, maybe later // move to cleaner accessors @@ -91,12 +123,35 @@ class SingleTriggeredInput : public Fun4AllBase, public InputFileHandler private: Eventiterator *m_EventIterator{nullptr}; SingleTriggeredInput *m_Gl1Input{nullptr}; - int m_AllDone{0}; - uint64_t m_Event{0}; - int m_EventNumber{0}; - int m_EventsThisFile{0}; - int m_EventAlignmentProblem{0}; - int m_FilesDone{0}; + /** + * Global completion flag indicating whether overall input processing has finished. + * + * 0 means processing is not finished; any non-zero value means processing is complete. + */ +int m_AllDone{0}; + /** + * Internal event counter used to assign or track unique event identifiers. + * + * Represents the running count of events processed by this input handler. + */ +uint64_t m_Event{0}; + /** + * Current event number for this input instance. + * + * Tracks the index of the currently processed event within the input stream or file; initialized to 0. + */ +int m_EventNumber{0}; + /** + * Flag indicating whether an event alignment problem has been detected. + * + * 0 means no alignment problem; a non-zero value indicates an alignment problem was detected. + */ +int m_EventAlignmentProblem{0}; + /** + * Number of input files that have been completely processed. + * + * Tracks how many input files have finished processing and been closed. */ +int m_FilesDone{0}; int m_LastEvent{std::numeric_limits::max()}; int m_ProblemEvent{-1}; int m_RepresPacket{-1}; @@ -114,6 +169,12 @@ class SingleTriggeredInput : public Fun4AllBase, public InputFileHandler std::map m_PacketAlignmentProblem; std::map m_PrevPoolLastDiffBad; std::map m_PreviousValidBCOMap; + /** + * @brief Global event counter used for bookkeeping across processed files. + * + * Tracks the total number of processed events and is incremented as events are handled. + */ +long long eventcounter{0}; }; -#endif +#endif \ No newline at end of file diff --git a/offline/packages/CaloEmbedding/CombineTowerInfo.cc b/offline/packages/CaloEmbedding/CombineTowerInfo.cc new file mode 100644 index 0000000000..2273aaf920 --- /dev/null +++ b/offline/packages/CaloEmbedding/CombineTowerInfo.cc @@ -0,0 +1,132 @@ +#include "CombineTowerInfo.h" + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include + +/** + * @brief Construct a CombineTowerInfo subsystem instance. + * + * Initializes the SubsysReco base with the provided instance name. + * + * @param name Instance name for the subsystem (used as the SubsysReco name/identifier). + */ +CombineTowerInfo::CombineTowerInfo(const std::string& name) + : SubsysReco(name) +{ +} + +/** + * @brief Validate configured node names and initialize runtime nodes. + * + * Ensures input and output node names are set, creates or locates the required nodes + * under the provided top-level node, and prepares internal containers for processing. + * + * @param topNode Top-level PHCompositeNode used to locate or attach DST and detector nodes. + * @return int Fun4AllReturnCodes::EVENT_OK on success. + * @throws std::runtime_error If any of the input or output node names are empty. + */ +int CombineTowerInfo::InitRun(PHCompositeNode* topNode) +{ + if (m_inputNodeA.empty() || m_inputNodeB.empty() || m_outputNode.empty()) + { + throw std::runtime_error("CombineTowerInfo: input/output node names not set"); + } + + CreateNodes(topNode); + return Fun4AllReturnCodes::EVENT_OK; +} + +/** + * @brief Locate input TowerInfo containers, prepare the output container, and attach it to the node tree. + * + * Retrieves the input containers named by m_inputNodeA and m_inputNodeB from topNode, verifies both exist + * and have the same size, and ensures an output TowerInfoContainer named by m_outputNode is present under + * the detector node (cloning the A container if necessary). On creation, the output container is wrapped + * in a PHIODataNode and added to the detector node. + * + * @param topNode Root node of the current PHENIX node tree used to find or create the required nodes. + * + * @throws std::runtime_error If the DST node is missing, if either input container is missing, or if the + * input containers have different sizes. + */ +void CombineTowerInfo::CreateNodes(PHCompositeNode* topNode) +{ + PHNodeIterator iter(topNode); + PHCompositeNode* dstNode = + dynamic_cast(iter.findFirst("PHCompositeNode", "DST")); + + if (!dstNode) + { + throw std::runtime_error("CombineTowerInfo: DST node not found"); + } + + PHCompositeNode *DetNode = dynamic_cast(iter.findFirst("PHCompositeNode", m_detector)); + + m_towersA = findNode::getClass(topNode, m_inputNodeA); + m_towersB = findNode::getClass(topNode, m_inputNodeB); + + if (!m_towersB) + { + std::cout << "CombineTowerInfo: " <(dstNode, m_outputNode); + if (!m_towersOut) + { + m_towersOut = + dynamic_cast(m_towersA->CloneMe()); + + auto* node = new PHIODataNode( + m_towersOut, m_outputNode, "PHObject"); + + DetNode->addNode(node); + } + + if (m_towersA->size() != m_towersB->size()) + { + throw std::runtime_error("CombineTowerInfo: input containers have different sizes"); + } +} + +/** + * @brief Merge tower data from the two input containers into the output container for the current event. + * + * For each tower channel, copies the tower contents from input A into the output and sets the output energy + * to the sum of energies from input A and input B. + * + * @returns Fun4AllReturnCodes::EVENT_OK on success. + */ +int CombineTowerInfo::process_event(PHCompositeNode* /*topNode*/) +{ + const unsigned int ntowers = m_towersA->size(); + + for (unsigned int ich = 0; ich < ntowers; ++ich) + { + TowerInfo* towerA = m_towersA->get_tower_at_channel(ich); + TowerInfo* towerB = m_towersB->get_tower_at_channel(ich); + TowerInfo* towerO = m_towersOut->get_tower_at_channel(ich); + + towerO->copy_tower(towerA); + + const float e_sum = towerA->get_energy() + towerB->get_energy(); + towerO->set_energy(e_sum); + } + + return Fun4AllReturnCodes::EVENT_OK; +} diff --git a/offline/packages/CaloEmbedding/CombineTowerInfo.h b/offline/packages/CaloEmbedding/CombineTowerInfo.h new file mode 100644 index 0000000000..aaf3f3df11 --- /dev/null +++ b/offline/packages/CaloEmbedding/CombineTowerInfo.h @@ -0,0 +1,59 @@ +#ifndef COMBINETOWERINFO_H +#define COMBINETOWERINFO_H + +#include + +#include + +class PHCompositeNode; +class TowerInfoContainer; + +class CombineTowerInfo : public SubsysReco +{ + public: + explicit CombineTowerInfo(const std::string& name = "CombineTowerInfo"); + ~CombineTowerInfo() override = default; + + int InitRun(PHCompositeNode* topNode) override; + int process_event(PHCompositeNode* topNode) override; + + /** + * Set the name of the first input node used as a source of tower information. + * @param name Node name for input A. + */ +void set_inputNodeA(const std::string& name) { m_inputNodeA = name; } + /** + * Set the name of the second input node. + * @param name Name of the input node B from which tower information will be read. + */ +void set_inputNodeB(const std::string& name) { m_inputNodeB = name; } + /** + * Set the name of the output node where the combined TowerInfoContainer will be stored. + * @param name Name of the output node for the combined TowerInfoContainer. + */ +void set_outputNode(const std::string& name) { m_outputNode = name; } + /** + * Set the detector name used to identify which detector's tower information to operate on. + * @param name Detector name to assign to this instance. + */ +void set_detector(const std::string& name) { m_detector = name; } + + private: + void CreateNodes(PHCompositeNode* topNode); + + std::string m_inputNodeA; + std::string m_inputNodeB; + std::string m_outputNode; + std::string m_detector; + + TowerInfoContainer* m_towersA{nullptr}; + TowerInfoContainer* m_towersB{nullptr}; + /** + * Container that will hold the combined tower information produced by this module. + * + * Initialized to nullptr and set to a valid TowerInfoContainer when the node tree is created. + */ +TowerInfoContainer* m_towersOut{nullptr}; +}; + +#endif diff --git a/offline/packages/CaloEmbedding/CopyIODataNodes.cc b/offline/packages/CaloEmbedding/CopyIODataNodes.cc index 0d4d4536ff..c8fbd12986 100644 --- a/offline/packages/CaloEmbedding/CopyIODataNodes.cc +++ b/offline/packages/CaloEmbedding/CopyIODataNodes.cc @@ -7,6 +7,9 @@ #include +#include +#include + #include #include @@ -25,7 +28,15 @@ CopyIODataNodes::CopyIODataNodes(const std::string &name) { } -//____________________________________________________________________________.. +/** + * @brief Initialize per-run IO data nodes in the destination topology. + * + * For each enabled copy flag, ensure the corresponding node(s) exist in the Fun4AllServer top node + * by creating or initializing destination nodes based on the input topology provided by topNode. + * + * @param topNode Source PHCompositeNode containing run-level nodes to copy or clone from. + * @return int Fun4All return code; `Fun4AllReturnCodes::EVENT_OK` on success. + */ int CopyIODataNodes::InitRun(PHCompositeNode *topNode) { Fun4AllServer *se = Fun4AllServer::instance(); @@ -57,11 +68,24 @@ int CopyIODataNodes::InitRun(PHCompositeNode *topNode) { CreateSyncObject(topNode, se->topNode()); } + if (m_CopyTowerInfoFlag) + { + CreateTowerInfo(topNode, se->topNode()); + } return Fun4AllReturnCodes::EVENT_OK; } -//____________________________________________________________________________.. +/** + * @brief Perform per-event copying of selected data nodes from the input node tree to the server's top node. + * + * For the current event, conditionally copies configured node types (event header, centrality info, + * global vertex map, minimum-bias info, MBD output, sync object, and tower info) from the provided + * input top node into the Fun4All server's output topology. + * + * @param topNode Source top-level node for this event from which data nodes will be copied. + * @return Fun4AllReturnCodes::EVENT_OK on successful processing of the event. + */ int CopyIODataNodes::process_event(PHCompositeNode *topNode) { Fun4AllServer *se = Fun4AllServer::instance(); @@ -89,6 +113,10 @@ int CopyIODataNodes::process_event(PHCompositeNode *topNode) { CopySyncObject(topNode, se->topNode()); } + if (m_CopyTowerInfoFlag) + { + CopyTowerInfo(topNode, se->topNode()); + } return Fun4AllReturnCodes::EVENT_OK; } @@ -277,6 +305,15 @@ void CopyIODataNodes::CreateMinimumBiasInfo(PHCompositeNode *from_topNode, PHCom return; } +/** + * @brief Copies MinimumBiasInfo contents from the source to the destination node tree. + * + * If both source and destination MinimumBiasInfo objects exist, the source's data is copied + * into the destination. When verbosity is greater than zero, identify() is printed for both objects. + * + * @param from_topNode Top-level node containing the source MinimumBiasInfo. + * @param to_topNode Top-level node containing the destination MinimumBiasInfo. + */ void CopyIODataNodes::CopyMinimumBiasInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode) { MinimumBiasInfo *from_minimumbiasinfo = findNode::getClass(from_topNode, "MinimumBiasInfo"); @@ -293,6 +330,50 @@ void CopyIODataNodes::CopyMinimumBiasInfo(PHCompositeNode *from_topNode, PHCompo return; } +/** + * @brief Copy per-channel tower data from the source TowerInfoContainer to the destination container. + * + * Copies each tower present in the source container named by from_towerInfo_name into the + * corresponding channel of the destination container named by to_towerInfo_name. The number + * of channels copied equals the size() of the source container. + * + * @param from_topNode Top-level node containing the source TowerInfoContainer. + * @param to_topNode Top-level node containing the destination TowerInfoContainer. + */ +void CopyIODataNodes::CopyTowerInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode) +{ + TowerInfoContainer *from_towerInfo = findNode::getClass(from_topNode, from_towerInfo_name); + TowerInfoContainer *to_towerInfo = findNode::getClass( to_topNode, to_towerInfo_name); + unsigned int ntowers = from_towerInfo->size(); + for (unsigned int ch = 0; ch < ntowers; ++ch) + { + TowerInfo *from_tow = from_towerInfo->get_tower_at_channel(ch); + to_towerInfo->get_tower_at_channel(ch)->copy_tower(from_tow); + } + + if (Verbosity() > 0) + { + std::cout << "From TowerInfoContainer identify()" << std::endl; + from_towerInfo->identify(); + std::cout << "To TowerInfoCOntainer identify()" << std::endl; + to_towerInfo->identify(); + } + + return; +} + + +/** + * @brief Ensure a MbdOut node exists under the destination topology by cloning it from the source. + * + * If a MbdOut object exists in the source topology, this routine ensures the destination contains + * a DST/MBD composite path and attaches a cloned MbdOut wrapped in a PHIODataNode under MBD. + * If no MbdOut is found in the source, the corresponding copy flag (m_CopyMbdOutFlag) is disabled + * and a diagnostic message is emitted. + * + * @param from_topNode Source root node to copy MbdOut from. + * @param to_topNode Destination root node to receive the cloned MbdOut. + */ void CopyIODataNodes::CreateMbdOut(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode) { @@ -330,6 +411,59 @@ void CopyIODataNodes::CreateMbdOut(PHCompositeNode *from_topNode, PHCompositeNod } +/** + * @brief Ensure a TowerInfoContainer exists under the destination node tree by cloning it from the source if necessary. + * + * If a TowerInfoContainer named by the configured source identifier cannot be found on from_topNode, + * the function disables further tower-info copying (m_CopyTowerInfoFlag = false) and returns. + * If the destination does not already contain a TowerInfoContainer with the configured destination name, + * the function ensures a DST composite node exists under to_topNode, clones the source TowerInfoContainer, + * wraps it in a PHIODataNode, and attaches it under DST. + * + * @param from_topNode Root node of the source topology to copy the TowerInfoContainer from. + * @param to_topNode Root node of the destination topology where the TowerInfoContainer will be created if missing. + */ +void CopyIODataNodes::CreateTowerInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode) +{ + std::cout << "copying tower info" << std::endl; + TowerInfoContainer *from_towerInfo = findNode::getClass(from_topNode, from_towerInfo_name); + if (!from_towerInfo) + { + std::cout << "Could not locate TowerInfoContainer on " << from_topNode->getName() << std::endl; + m_CopyTowerInfoFlag = false; + return; + } + TowerInfoContainer *to_towerInfo = findNode::getClass(to_topNode, to_towerInfo_name); + if (!to_towerInfo) + { + PHNodeIterator iter(to_topNode); + PHCompositeNode *dstNode = dynamic_cast(iter.findFirst("PHCompositeNode", "DST")); + if (!dstNode) + { + dstNode = new PHCompositeNode("DST"); + to_topNode->addNode(dstNode); + } + to_towerInfo = dynamic_cast(from_towerInfo->CloneMe()); + PHIODataNode *newNode = new PHIODataNode(to_towerInfo, to_towerInfo_name, "PHObject"); + dstNode->addNode(newNode); + } + return; +} + + + + +/** + * @brief Copy MBD output data from one node tree into another. + * + * Locates the MbdOut object in the source and destination PHCompositeNode trees + * and copies the contents from the source into the destination. If verbosity + * is enabled, prints identify() information for both source and destination + * MbdOut objects. + * + * @param from_topNode Top node of the source tree containing the original MbdOut. + * @param to_topNode Top node of the destination tree to receive the copied MbdOut. + */ void CopyIODataNodes::CopyMbdOut(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode) { MbdOut *from_mbdout = findNode::getClass(from_topNode, "MbdOut"); @@ -385,4 +519,4 @@ void CopyIODataNodes::CopySyncObject(PHCompositeNode *from_topNode, PHCompositeN to_syncobject->identify(); } return; -} +} \ No newline at end of file diff --git a/offline/packages/CaloEmbedding/CopyIODataNodes.h b/offline/packages/CaloEmbedding/CopyIODataNodes.h index 08626bdafb..4336277085 100644 --- a/offline/packages/CaloEmbedding/CopyIODataNodes.h +++ b/offline/packages/CaloEmbedding/CopyIODataNodes.h @@ -35,6 +35,18 @@ class CopyIODataNodes : public SubsysReco void CopyMbdOut(bool flag = true) { m_CopyMbdOutFlag = flag; } void CopyRunHeader(bool flag = true) { m_CopyRunHeaderFlag = flag; } void CopySyncObject(bool flag = true) { m_CopySyncObjectFlag = flag; } + /** + * Configure TowerInfo copying by specifying the source and destination node names and enabling the copy. + * @param set_from_towerInfo_name Name of the source TowerInfo node to copy from. + * @param set_to_towerInfo_name Name of the destination TowerInfo node to copy to. + */ + void set_CopyTowerInfo(const std::string& set_from_towerInfo_name,const std::string& set_to_towerInfo_name) + { + from_towerInfo_name = set_from_towerInfo_name; + to_towerInfo_name = set_to_towerInfo_name; + m_CopyTowerInfoFlag = true; + return; + } private: void CreateCentralityInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode); @@ -56,6 +68,8 @@ class CopyIODataNodes : public SubsysReco void CreateSyncObject(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode); void CopySyncObject(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode); + void CopyTowerInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode); + void CreateTowerInfo(PHCompositeNode *from_topNode, PHCompositeNode *to_topNode); bool m_CopyCentralityInfoFlag = true; bool m_CopyEventHeaderFlag = true; @@ -64,6 +78,10 @@ class CopyIODataNodes : public SubsysReco bool m_CopyMbdOutFlag = true; bool m_CopyRunHeaderFlag = true; bool m_CopySyncObjectFlag = true; + bool m_CopyTowerInfoFlag = false; + + std::string from_towerInfo_name = {}; + std::string to_towerInfo_name = {}; }; -#endif // COPYIODATANODES_H +#endif // COPYIODATANODES_H \ No newline at end of file diff --git a/offline/packages/CaloReco/PhotonClusterBuilder.cc b/offline/packages/CaloReco/PhotonClusterBuilder.cc index 263ae04ef0..048e42ab10 100644 --- a/offline/packages/CaloReco/PhotonClusterBuilder.cc +++ b/offline/packages/CaloReco/PhotonClusterBuilder.cc @@ -272,6 +272,23 @@ void PhotonClusterBuilder::calculate_bdt_score(PhotonClusterv1* photon) photon->set_shower_shape_parameter("bdt_score", bdt_score); } +/** + * @brief Compute and assign shower-shape and isolation parameters for a photon candidate. + * + * Extracts tower-level shower information from the provided RawCluster and populates + * the given PhotonClusterv1 with derived shower-shape quantities (energy sums for multiple + * windows, lateral moments and widths, center-of-gravity offsets, timing, HCAL matching + * sums/indices, and isolation ET values) using the cluster position. + * + * If the cluster contains no shower-shape vector (as returned by RawCluster::get_shower_shapes) + * the function returns immediately without modifying the photon. + * + * @param rc Pointer to the source RawCluster used to compute shower shapes. + * @param photon Pointer to the PhotonClusterv1 object to receive computed parameters. + * Many parameters are set via photon->set_shower_shape_parameter(...). + * @param cluster_eta Eta coordinate of the cluster (used for ET and isolation calculations). + * @param cluster_phi Phi coordinate of the cluster (used for isolation and matching). + */ void PhotonClusterBuilder::calculate_shower_shapes(RawCluster* rc, PhotonClusterv1* photon, float cluster_eta, float cluster_phi) { std::vector showershape = rc->get_shower_shapes(m_shape_min_tower_E); @@ -566,6 +583,7 @@ void PhotonClusterBuilder::calculate_shower_shapes(RawCluster* rc, PhotonCluster photon->set_shower_shape_parameter("et3", showershape[2]); photon->set_shower_shape_parameter("et4", showershape[3]); photon->set_shower_shape_parameter("e11", e11); + photon->set_shower_shape_parameter("e22", showershape[8] + showershape[9] + showershape[10] + showershape[11]); photon->set_shower_shape_parameter("e33", e33); photon->set_shower_shape_parameter("e55", e55); photon->set_shower_shape_parameter("e77", e77); @@ -909,4 +927,4 @@ double PhotonClusterBuilder::deltaR(double eta1, double phi1, double eta2, doubl dphi += 2 * M_PI; } return sqrt(pow(eta1 - eta2, 2) + pow(dphi, 2)); -} +} \ No newline at end of file diff --git a/offline/packages/KFParticle_sPHENIX/KFParticle_sPHENIX.cc b/offline/packages/KFParticle_sPHENIX/KFParticle_sPHENIX.cc index 38b60d55ba..525c0fc99a 100644 --- a/offline/packages/KFParticle_sPHENIX/KFParticle_sPHENIX.cc +++ b/offline/packages/KFParticle_sPHENIX/KFParticle_sPHENIX.cc @@ -132,6 +132,14 @@ int KFParticle_sPHENIX::InitRun(PHCompositeNode *topNode) return 0; } +/** + * @brief Process a single event: locate tracks and vertices, reconstruct decay candidates, and optionally save results. + * + * Accesses track and vertex maps from the provided node tree, skips processing when there are no tracks or when a real primary vertex is required but none exist, constructs mother/intermediate/daughter KFParticle candidates, and — for each candidate — increments the internal candidate counter and conditionally writes outputs. + * + * @param topNode Root node of the event’s PHENIX node tree used to retrieve inputs (track/vertex maps) and to write output nodes/branches. + * @return int Fun4All return code. Returns `Fun4AllReturnCodes::EVENT_OK` in normal operation; event processing is skipped (but still returns `EVENT_OK`) when no tracks are present or when a required primary vertex is missing. + */ int KFParticle_sPHENIX::process_event(PHCompositeNode *topNode) { @@ -148,7 +156,7 @@ int KFParticle_sPHENIX::process_event(PHCompositeNode *topNode) { std::cout << "KFParticle: Event skipped as there are no tracks" << std::endl; } - return Fun4AllReturnCodes::ABORTEVENT; + return Fun4AllReturnCodes::EVENT_OK; } if (!m_use_fake_pv) @@ -162,7 +170,7 @@ int KFParticle_sPHENIX::process_event(PHCompositeNode *topNode) { std::cout << "KFParticle: Event skipped as there are no vertices" << std::endl; } - return Fun4AllReturnCodes::ABORTEVENT; + return Fun4AllReturnCodes::EVENT_OK; } } else @@ -174,10 +182,9 @@ int KFParticle_sPHENIX::process_event(PHCompositeNode *topNode) { std::cout << "KFParticle: Event skipped as there are no vertices" << std::endl; } - return Fun4AllReturnCodes::ABORTEVENT; + return Fun4AllReturnCodes::EVENT_OK; } } - } createDecay(topNode, mother, vertex_kfparticle, daughters, intermediates, nPVs); @@ -569,4 +576,4 @@ void KFParticle_sPHENIX::getField() fieldmap->Delete(); fin->Close(); -} +} \ No newline at end of file diff --git a/offline/packages/PHGenFitPkg/PHGenFit/Fitter.cc b/offline/packages/PHGenFitPkg/PHGenFit/Fitter.cc index 8fe89b5019..a7c89fa83c 100644 --- a/offline/packages/PHGenFitPkg/PHGenFit/Fitter.cc +++ b/offline/packages/PHGenFitPkg/PHGenFit/Fitter.cc @@ -44,6 +44,17 @@ namespace genfit namespace PHGenFit { + /** + * @brief Constructs a Fitter initialized from a TGeo geometry file, a magnetic field, a fitter choice, and an event-display flag. + * + * Imports the geometry from the given TGeo file, registers the provided non-null magnetic field with GenFit, initializes material effects, + * creates an internal GenFit fitter according to the provided choice, and optionally initializes the GenFit event display. + * + * @param tgeo_file_name Path to a TGeo geometry file to import. + * @param field Pointer to a PHField describing the magnetic field (must not be null). + * @param fitter_choice String selecting the fitter to construct; supported values include "KalmanFitterRefTrack", "KalmanFitter", "DafSimple", and "DafRef". + * @param doEventDisplay If true, the GenFit event display is created and enabled for this Fitter. + */ Fitter::Fitter( const std::string& tgeo_file_name, const PHField* field, @@ -51,9 +62,9 @@ namespace PHGenFit const std::string& /*track_rep_choice*/, const bool doEventDisplay) : verbosity(1000) + , _tgeo_manager(new TGeoManager("Default", "Geane geometry")) , _doEventDisplay(doEventDisplay) { - _tgeo_manager = new TGeoManager("Default", "Geane geometry"); TGeoManager::Import(tgeo_file_name.data()); assert(field); @@ -73,26 +84,24 @@ namespace PHGenFit } // init fitter - if (fitter_choice.compare("KalmanFitterRefTrack") == 0) + if (fitter_choice == "KalmanFitterRefTrack") { _fitter = new genfit::KalmanFitterRefTrack(); } - else if (fitter_choice.compare("KalmanFitter") == 0) -// NOLINTNEXTLINE(bugprone-branch-clone) - { + else if (fitter_choice == "KalmanFitter") + { // NOLINT(bugprone-branch-clone) _fitter = new genfit::KalmanFitter(); } - else if (fitter_choice.compare("DafSimple") == 0) + else if (fitter_choice == "DafSimple") { _fitter = new genfit::DAF(false); } - else if (fitter_choice.compare("DafRef") == 0) + else if (fitter_choice == "DafRef") { _fitter = new genfit::DAF(true); } else -// NOLINTNEXTLINE(bugprone-branch-clone) - { + { // NOLINT(bugprone-branch-clone) _fitter = new genfit::KalmanFitter(); } @@ -203,6 +212,19 @@ namespace PHGenFit return new Fitter(tgeo_manager, fieldMap, fitter_choice, track_rep_choice, doEventDisplay); } + /** + * @brief Construct a Fitter configured with an existing geometry and magnetic field. + * + * Initializes GenFit field and material-effect managers, optionally attaches an event + * display, and creates the chosen GenFit fitter implementation. + * + * @param tgeo_manager Pointer to an existing TGeoManager that provides detector geometry. + * @param fieldMap Pointer to a GenFit magnetic field map used for track propagation. + * @param fitter_choice Enum value selecting which fitter implementation to create + * (KalmanFitter, KalmanFitterRefTrack, DafSimple, DafRef). + * @param /*track_rep_choice*/ Unused track-representation selector (kept for API compatibility). + * @param doEventDisplay If true, obtains and stores the GenFit EventDisplay instance. + */ Fitter::Fitter(TGeoManager* tgeo_manager, genfit::AbsBField* fieldMap, const PHGenFit::Fitter::FitterType& fitter_choice, const PHGenFit::Fitter::TrackRepType& /*track_rep_choice*/, @@ -235,7 +257,7 @@ namespace PHGenFit { _fitter = new genfit::KalmanFitterRefTrack(); } - if (fitter_choice == PHGenFit::Fitter::DafSimple) + else if (fitter_choice == PHGenFit::Fitter::DafSimple) { _fitter = new genfit::DAF(false); } @@ -266,6 +288,20 @@ namespace PHGenFit return new Fitter(tgeo_manager, fieldMap, fitter_choice, track_rep_choice, doEventDisplay); } + /** + * @brief Constructs a Fitter using an existing TGeoManager and GenFit magnetic field map. + * + * Initializes GenFit field and material-effect managers, optionally enables the GenFit + * event display, and creates the requested GenFit fitter implementation. + * + * @param tgeo_manager Pointer to the TGeoManager that provides detector geometry; must not be null. + * @param fieldMap GenFit magnetic field map used for propagation and fitting. + * @param fitter_choice String selecting the fitter implementation ("KalmanFitterRefTrack", + * "KalmanFitter", "DafSimple", "DafRef"). If an unrecognized value is provided, + * no fitter is created and an error is logged. + * @param /*track_rep_choice*/ Placeholder parameter for track representation selection (ignored). + * @param doEventDisplay If true, enable and attach the GenFit event display. + */ Fitter::Fitter(TGeoManager* tgeo_manager, genfit::AbsBField* fieldMap, const std::string& fitter_choice, const std::string& /*track_rep_choice*/, const bool doEventDisplay) @@ -289,19 +325,19 @@ namespace PHGenFit } // init fitter - if (fitter_choice.compare("KalmanFitterRefTrack") == 0) + if (fitter_choice == "KalmanFitterRefTrack") { _fitter = new genfit::KalmanFitterRefTrack(); } - else if (fitter_choice.compare("KalmanFitter") == 0) + else if (fitter_choice == "KalmanFitter") { _fitter = new genfit::KalmanFitter(); } - else if (fitter_choice.compare("DafSimple") == 0) + else if (fitter_choice == "DafSimple") { _fitter = new genfit::DAF(false); } - else if (fitter_choice.compare("DafRef") == 0) + else if (fitter_choice == "DafRef") { _fitter = new genfit::DAF(true); } @@ -344,4 +380,4 @@ namespace PHGenFit return new Fitter(tgeo_manager, fieldMap, fitter_choice, track_rep_choice, doEventDisplay); } -} // namespace PHGenFit +} // namespace PHGenFit \ No newline at end of file diff --git a/offline/packages/PHGenFitPkg/PHGenFit/Track.cc b/offline/packages/PHGenFitPkg/PHGenFit/Track.cc index b1643e8cd0..124ee0e624 100644 --- a/offline/packages/PHGenFitPkg/PHGenFit/Track.cc +++ b/offline/packages/PHGenFitPkg/PHGenFit/Track.cc @@ -48,8 +48,8 @@ #define WILD_DOUBLE (-999999) -//#define _DEBUG_ -//#define _PRINT_MATRIX_ +// #define _DEBUG_ +// #define _PRINT_MATRIX_ #ifdef _DEBUG_ #include @@ -59,12 +59,23 @@ ofstream fout_matrix("matrix.txt"); namespace PHGenFit { + /** + * @brief Constructs a Track with a seeded state and underlying GenFit track. + * + * Initializes the underlying GenFit track using the provided track representation and a seed state + * defined by position, momentum, and covariance. + * + * @param rep Pointer to the GenFit track representation used for propagation and fitting. + * @param seed_pos Seed 3D position in detector coordinates. + * @param seed_mom Seed 3D momentum vector. + * @param seed_cov 6x6 covariance matrix describing uncertainties of the seeded state (position and momentum). + * @param v Verbosity level controlling diagnostic output. + */ Track::Track(genfit::AbsTrackRep* rep, const TVector3& seed_pos, const TVector3& seed_mom, const TMatrixDSym& seed_cov, const int v) + : verbosity(v) { // TODO Add input param check - verbosity = v; - genfit::MeasuredStateOnPlane seedMSoP(rep); seedMSoP.setPosMomCov(seed_pos, seed_mom, seed_cov); // const genfit::StateOnPlane seedSoP(seedMSoP); @@ -77,15 +88,33 @@ namespace PHGenFit //_track = NEW(genfit::Track)(rep, seedState, seedCov); } + /** + * @brief Creates a deep copy of a PHGenFit::Track. + * + * Constructs a new Track by cloning the source's underlying GenFit track and copying its + * verbosity, cluster ID list, cluster key list, and vertex identifier. + * + * @param t Source track to copy. + */ Track::Track(const PHGenFit::Track& t) + : verbosity(t.verbosity) + , _track(new genfit::Track(*(t.getGenFitTrack()))) + , _clusterIDs(t.get_cluster_IDs()) + , _clusterkeys(t.get_cluster_keys()) + , _vertex_id(t.get_vertex_id()) { - _track = new genfit::Track(*(t.getGenFitTrack())); - verbosity = t.verbosity; - _clusterIDs = t.get_cluster_IDs(); - _clusterkeys = t.get_cluster_keys(); - _vertex_id = t.get_vertex_id(); } + /** + * @brief Inserts a measurement into the underlying GenFit track and records its cluster identifiers. + * + * The provided Measurement is added to the internal genfit::Track, its cluster ID and cluster key + * are appended to the track's bookkeeping vectors, and the Measurement object is deleted (ownership + * is transferred and consumed). + * + * @param measurement Measurement to insert; this object is consumed and deleted by the call. + * @return int 0 on success. + */ int Track::addMeasurement(PHGenFit::Measurement* measurement) { std::vector msmts; @@ -182,6 +211,17 @@ namespace PHGenFit return pathlenth; } + /** + * @brief Produce a MeasuredStateOnPlane for the track extrapolated to the plane defined by origin O and normal n. + * + * Extrapolates the track state (using the fitter state at the given track point) to the destination plane and returns + * a newly allocated MeasuredStateOnPlane representing the extrapolated state. + * + * @param O Origin point of the destination plane. + * @param n Normal vector of the destination plane. + * @param tr_point_id Index of the track point whose fitter state is used as the starting state for extrapolation. + * @return genfit::MeasuredStateOnPlane* Pointer to a newly allocated MeasuredStateOnPlane containing the extrapolated state, or `nullptr` if extrapolation failed. Caller assumes ownership of the returned pointer. + */ genfit::MeasuredStateOnPlane* Track::extrapolateToPlane(const TVector3& O, const TVector3& n, const int tr_point_id) const { genfit::MeasuredStateOnPlane* state = new genfit::MeasuredStateOnPlane(); @@ -191,12 +231,24 @@ namespace PHGenFit delete state; return nullptr; } - else - { - return state; - } + + return state; } + /** + * @brief Extrapolates the track's backward-updated fitted state to a specified line and stores the resulting measured state. + * + * Extrapolates the backward-updated Kalman fitted state (obtained from the TrackPoint identified by tr_point_id) + * to the closest point on the line defined by line_point and line_direction, and assigns the resulting + * MeasuredStateOnPlane to the provided state parameter. + * + * @param[out] state Measured state on plane to receive the extrapolated state. + * @param line_point A point on the destination line. + * @param line_direction Direction vector of the destination line. + * @param tr_point_id Index of the TrackPoint whose fitter info supplies the backward-updated fitted state. + * @return double Path length along the track from the source fitted state to the extrapolated point on the line; + * `WILD_DOUBLE` if the TrackPoint/fitter info is missing or if an exception occurs during extrapolation. + */ double Track::extrapolateToLine(genfit::MeasuredStateOnPlane& state, const TVector3& line_point, const TVector3& line_direction, const int tr_point_id) const { double pathlenth = WILD_DOUBLE; @@ -231,6 +283,18 @@ namespace PHGenFit return pathlenth; } + /** + * @brief Create and return a MeasuredStateOnPlane extrapolated to a line. + * + * Constructs a new MeasuredStateOnPlane and fills it by extrapolating the track to + * the line through `line_point` with direction `line_direction` for the given + * track point index. + * + * @param line_point A point on the target line. + * @param line_direction Direction vector of the target line. + * @param tr_point_id Index of the track point used as the extrapolation source. + * @return genfit::MeasuredStateOnPlane* Pointer to the allocated MeasuredStateOnPlane containing the extrapolated state, or `nullptr` if extrapolation failed. Caller takes ownership of the returned pointer. + */ genfit::MeasuredStateOnPlane* Track::extrapolateToLine(const TVector3& line_point, const TVector3& line_direction, const int tr_point_id) const { genfit::MeasuredStateOnPlane* state = new genfit::MeasuredStateOnPlane(); @@ -240,12 +304,28 @@ namespace PHGenFit delete state; return nullptr; } - else - { - return state; - } + + return state; } + /** + * @brief Extrapolates the track state to the surface of a cylinder and returns the path length. + * + * The function obtains a reference state from the specified track point's Kalman update (forward when + * `direction == 1`, backward when `direction == -1`) if available; otherwise it uses the track seed state. + * It extrapolates that state to a cylinder defined by `radius` and the line (`line_point`, `line_direction`), + * assigns the resulting measured state to `state`, and returns the path length from the reference state to + * the intersection with the cylinder. + * + * @param[out] state Measured state on the plane at the extrapolated cylinder intersection (assigned on success). + * @param radius Radius of the target cylinder. + * @param line_point A point on the cylinder axis. + * @param line_direction Direction vector of the cylinder axis. + * @param tr_point_id Index of the track point to use as the reference for the fitted state; if that point has + * no fitter update, the track seed state is used. + * @param direction Extrapolation direction: `1` to use the forward update, `-1` to use the backward update. + * @return double Path length from the reference state to the cylinder intersection; `WILD_DOUBLE` on failure. + */ double Track::extrapolateToCylinder(genfit::MeasuredStateOnPlane& state, double radius, const TVector3& line_point, const TVector3& line_direction, const int tr_point_id, const int direction) const { #ifdef _DEBUG_ @@ -351,6 +431,20 @@ namespace PHGenFit return pathlenth; } + /** + * @brief Allocate and extrapolate a measured state to the surface of a cylinder. + * + * Creates a new genfit::MeasuredStateOnPlane, attempts to extrapolate the track to a cylinder + * defined by the given radius and axis (line_point, line_direction), and returns the extrapolated state + * if successful. + * + * @param radius Radius of the cylinder. + * @param line_point A point on the cylinder axis. + * @param line_direction Direction vector of the cylinder axis. + * @param tr_point_id Index of the track point to use as the starting reference. + * @param direction Extrapolation direction: must be `1` (forward) or `-1` (backward). + * @return genfit::MeasuredStateOnPlane* Pointer to the newly allocated extrapolated state on success, `nullptr` on failure. + */ genfit::MeasuredStateOnPlane* Track::extrapolateToCylinder(double radius, const TVector3& line_point, const TVector3& line_direction, const int tr_point_id, const int direction) const { assert(direction == 1 || direction == -1); @@ -361,12 +455,26 @@ namespace PHGenFit delete state; return nullptr; } - else - { - return state; - } + + return state; } + /** + * @brief Evaluate Kalman updates for a set of candidate measurements and record resulting chi2 increments with new track copies. + * + * For each measurement in @p measurements this method clones the current track, attaches the measurement, predicts from a base track point (or seed if no fitter info), + * applies a Kalman update for the measurement(s) on the constructed plane, and if the incremental chi2 is positive inserts the new track into @p incr_chi2s_new_tracks + * keyed by that chi2 increment. + * + * @param measurements Vector of pointers to candidate measurements. Each measurement is consumed by the inserted track (ownership is transferred). + * @param incr_chi2s_new_tracks Map to receive pairs of (chi2 increment, new track) for positive chi2 increments produced by the updates. + * @param base_tp_idx Index of the base TrackPoint (used to obtain the prediction state); negative indices are allowed as in genfit. + * @param direction Direction indicator for prediction/update (forward/backward convention used by the fitter). + * @param blowup_factor Factor by which to enlarge the predicted covariance before updating; values <= 1 leave the covariance unchanged. + * @param use_fitted_state If true, use the fitted state at the base point for prediction; otherwise use the directional update state. + * + * @return int -1 if @p measurements is empty, 0 on completion. + */ int Track::updateOneMeasurementKalman( const std::vector& measurements, std::map >& incr_chi2s_new_tracks, @@ -385,7 +493,7 @@ namespace PHGenFit << std::endl; #endif - if (measurements.size() == 0) + if (measurements.empty()) { return -1; } @@ -437,11 +545,11 @@ namespace PHGenFit #endif continue; } - //#ifdef _DEBUG_ + // #ifdef _DEBUG_ // std::cout << __LINE__ << "\n ###################################################################"<Print(); // std::cout << __LINE__ << "\n ###################################################################"<getFittedState(true)); @@ -579,7 +687,7 @@ namespace PHGenFit // std::cout << err_phi << "\t" << err_z << "\t"; } #endif - for (auto rawMeasurement : rawMeasurements) + for (auto* rawMeasurement : rawMeasurements) { fi->addMeasurementsOnPlane( rawMeasurement->constructMeasurementsOnPlane(*state)); @@ -598,7 +706,7 @@ namespace PHGenFit << ": size of fi's MeasurementsOnPlane: " << measurements_on_plane.size() << std::endl; #endif - for (auto it : measurements_on_plane) + for (auto* it : measurements_on_plane) { const genfit::MeasurementOnPlane& mOnPlane = *it; // const double weight = mOnPlane.getWeight(); @@ -760,6 +868,15 @@ namespace PHGenFit return pathlenth; } + /** + * @brief Extrapolates the track state to a specific point in space. + * + * Extrapolates the track to the point P using the track point identified by tr_point_id and returns the resulting measured state on a plane at that point. + * + * @param P Target point in global coordinates. + * @param tr_point_id Index of the TrackPoint to use as the starting/reference point for extrapolation. + * @return genfit::MeasuredStateOnPlane* Pointer to the extrapolated measured state on a plane at P, or `nullptr` if extrapolation fails. + */ genfit::MeasuredStateOnPlane* Track::extrapolateToPoint(const TVector3& P, const int tr_point_id) const { genfit::MeasuredStateOnPlane* state = new genfit::MeasuredStateOnPlane(); @@ -769,12 +886,16 @@ namespace PHGenFit delete state; return nullptr; } - else - { - return state; - } + return state; } + /** + * @brief Retrieves the fit chi-squared for the track. + * + * Obtains the chi-squared value from the track's fit status for the cardinal representation. + * + * @return double The chi-squared value, or NaN if the cardinal representation or fit status is unavailable. + */ double Track::get_chi2() const { double chi2 = std::numeric_limits::quiet_NaN(); @@ -903,4 +1024,4 @@ namespace PHGenFit return mom; } -} // namespace PHGenFit +} // namespace PHGenFit \ No newline at end of file diff --git a/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc b/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc index b519fd472e..ef6a0a9b3d 100644 --- a/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc +++ b/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.cc @@ -1,4 +1,3 @@ - #include "TriggerDSTSkimmer.h" #include @@ -17,9 +16,30 @@ TriggerDSTSkimmer::TriggerDSTSkimmer(const std::string &name) { } -//____________________________________________________________________________.. +/** + * @brief Evaluate trigger selection for the current event and decide its fate. + * + * Checks configured trigger indices against the GL1 scaled trigger vector and enforces + * the optional maximum accepted-events limit. When an event passes selection the + * accepted-events counter is incremented. + * + * @param topNode Pointer to the event node tree root used to retrieve the Gl1Packet. + * @return int Fun4All return code: + * - Fun4AllReturnCodes::EVENT_OK when the event passes selection and is accepted. + * - Fun4AllReturnCodes::ABORTEVENT when the event is skipped because the maximum + * accepted-events limit is reached (if enabled), the Gl1Packet node is missing, + * or no configured trigger fired. + * - Fun4AllReturnCodes::ABORTRUN when a configured trigger index is outside the + * valid range [0..63]. + */ int TriggerDSTSkimmer::process_event(PHCompositeNode *topNode) { + + if ((accepted_events >= max_accept) && use_max_accept) + { + return Fun4AllReturnCodes::ABORTEVENT; + } + if (Verbosity() > 0) { if (ievent % 1000 == 0) @@ -45,7 +65,7 @@ int TriggerDSTSkimmer::process_event(PHCompositeNode *topNode) if (n_trigger_index != 0) { bool trigger_fired = false; - Gl1Packet *_gl1PacketInfo = findNode::getClass(topNode, "GL1Packet"); + Gl1Packet *_gl1PacketInfo = findNode::getClass(topNode, 14001); int gl1_trigger_vector_scaled[64] = {0}; if (_gl1PacketInfo) { @@ -61,6 +81,7 @@ int TriggerDSTSkimmer::process_event(PHCompositeNode *topNode) std::cout << "TriggerDSTSkimmer::process_event - Error - Can't find Trigger Node Gl1Packet therefore no selection can be made" << std::endl; return Fun4AllReturnCodes::ABORTEVENT; } + for (int it = 0; it < n_trigger_index; ++it) { if (gl1_trigger_vector_scaled[m_trigger_index[it]] == 1) @@ -74,5 +95,8 @@ int TriggerDSTSkimmer::process_event(PHCompositeNode *topNode) return Fun4AllReturnCodes::ABORTEVENT; } } + + accepted_events++; + return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.h b/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.h index dfb2c47a7c..c5dd3cf65e 100644 --- a/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.h +++ b/offline/packages/Skimmers/Trigger/TriggerDSTSkimmer.h @@ -20,12 +20,53 @@ class TriggerDSTSkimmer : public SubsysReco int process_event(PHCompositeNode *topNode) override; - void SetTrigger(std::vector &trigger_vector) {m_trigger_index = trigger_vector;} + /** + * Configure which trigger indices are used to select events for skimming. + * @param trigger_vector Vector of trigger indices to accept; the contents are copied into the internal trigger index list. + */ +void SetTrigger(std::vector &trigger_vector) {m_trigger_index = trigger_vector;} + + /** + * Enable a maximum-accept mode and configure the maximum number of events to accept. + * @param max_events Maximum number of events to accept before further events are rejected. + */ + void set_accept_max(int max_events) + { + use_max_accept = true; + max_accept = max_events; + return; + } private: std::vector m_trigger_index{10}; - int ievent{0}; + /** + * Tracks the number of processed events as a zero-based counter starting at 0. + */ +int ievent{0}; + + /** + * @brief Count of events accepted by the skimmer. + * + * Tracks how many events have passed the skimmer's selection criteria. This + * value is incremented when an event is accepted and is used together with + * `max_accept` and `use_max_accept` to enforce a maximum number of accepted + * events when that mode is enabled. + */ +int accepted_events{0}; + /** + * Maximum number of events to accept when `use_max_accept` is enabled. + * + * Used as the threshold for `accepted_events`; only checked if `use_max_accept` is true. + */ +int max_accept{0}; + /** + * Enable maximum-accept mode for event selection. + * + * When `true`, the skimmer will stop accepting events after `max_accept` events have been accepted. + */ +bool use_max_accept{false}; + }; -#endif // JETDSTSKIMMER_H +#endif // JETDSTSKIMMER_H \ No newline at end of file diff --git a/offline/packages/jetbackground/DetermineTowerBackground.cc b/offline/packages/jetbackground/DetermineTowerBackground.cc index 9823d337f6..c0f8eadd0e 100644 --- a/offline/packages/jetbackground/DetermineTowerBackground.cc +++ b/offline/packages/jetbackground/DetermineTowerBackground.cc @@ -14,6 +14,10 @@ #include #include +#include +#include +#include + #include #include @@ -50,11 +54,88 @@ DetermineTowerBackground::DetermineTowerBackground(const std::string &name) _UE.resize(3, std::vector(1, 0)); } +/** + * @brief Initialize runtime resources for DetermineTowerBackground and prepare output nodes. + * + * When configured to use centrality-based flow (_do_flow == 4), attempts to load average calorimeter + * v2 calibrations before creating the output node structure. + * + * @param topNode Top-level node of the current processing tree used to create or locate output nodes. + * @return int Fun4All return code: EVENT_OK on success, ABORTRUN if calibration loading fails. + */ int DetermineTowerBackground::InitRun(PHCompositeNode *topNode) { + if (_do_flow == 4) + { + if (Verbosity()) + { + std::cout << "Loading the average calo v2" << std::endl; + } + if (LoadCalibrations()) + { + std::cout << "Load calibrations failed." << std::endl; + return Fun4AllReturnCodes::ABORTRUN; + } + + } + return CreateNode(topNode); } +/** + * @brief Load average calorimeter v2 calibrations into the object. + * + * Reads per-centrality-bin `jet_calo_v2` values from the calibration source and fills the internal + * _CENTRALITY_V2 vector (size 100) with those values. The calibration source is taken from the + * configured calibration name and may be overridden by the member path override flag/path. + * + * If no calibration path is available, the function prints an error message and terminates the + * process with exit(-1). + * + * @return int Fun4AllReturnCodes::EVENT_OK on success. + */ +int DetermineTowerBackground::LoadCalibrations() +{ + + CDBTTree *cdbtree_calo_v2 = nullptr; + + std::string calibdir = CDBInterface::instance()->getUrl(m_calibName); + if (m_overwrite_average_calo_v2) + { + calibdir = m_overwrite_average_calo_v2_path; + } + + if (calibdir.empty()) + { + std::cout << "Could not find filename for calo average v2, exiting" << std::endl; + exit(-1); + } + + cdbtree_calo_v2 = new CDBTTree(calibdir); + + + cdbtree_calo_v2->LoadCalibrations(); + + _CENTRALITY_V2.assign(100,0); + + for (int icent = 0; icent < 100; icent++) + { + _CENTRALITY_V2[icent] = cdbtree_calo_v2->GetFloatValue(icent, "jet_calo_v2"); + } + + delete cdbtree_calo_v2; + + return Fun4AllReturnCodes::EVENT_OK; +} + +/** + * @brief Process one event: compute per-layer per-eta background energy densities, extract event-plane (Psi2) and elliptic modulation (v2), and write results to the TowerBackground node. + * + * This routine selects seed jets, builds per-tower energy and bad-tower masks, determines flow according to the configured mode (HIJING truth, sEPD, calorimeter, or centrality-based), applies seed and flow-based exclusions and modulation, computes UE (underlying-event) energy density per eta bin for each calorimeter layer, and fills the TowerBackground output node. + * + * @param topNode Top-level node of the current event tree used to access input containers and to store the TowerBackground output. + * @return int `Fun4AllReturnCodes::EVENT_OK` on success, `Fun4AllReturnCodes::ABORTRUN` if required input objects are missing or processing cannot proceed, or a negative/other error code on fatal failures. + */ int DetermineTowerBackground::process_event(PHCompositeNode *topNode) { @@ -481,7 +562,92 @@ int DetermineTowerBackground::process_event(PHCompositeNode *topNode) } } - if ( _do_flow >= 1 ) + + // Get psi + if (_do_flow == 2) + { // HIJING truth flow extraction + PHG4TruthInfoContainer *truthinfo = findNode::getClass(topNode, "G4TruthInfo"); + + if (!truthinfo) + { + std::cout << "DetermineTowerBackground::process_event: FATAL , G4TruthInfo does not exist , cannot extract truth flow with do_flow = " << _do_flow << std::endl; + return -1; + } + + PHG4TruthInfoContainer::Range range = truthinfo->GetPrimaryParticleRange(); + + float Hijing_Qx = 0; + float Hijing_Qy = 0; + + for (PHG4TruthInfoContainer::ConstIterator iter = range.first; iter != range.second; ++iter) + { + PHG4Particle *g4particle = iter->second; + + if (truthinfo->isEmbeded(g4particle->get_track_id()) != 0) + { + continue; + } + + TLorentzVector t; + t.SetPxPyPzE(g4particle->get_px(), g4particle->get_py(), g4particle->get_pz(), g4particle->get_e()); + + float truth_pt = t.Pt(); + if (truth_pt < 0.4) + { + continue; + } + float truth_eta = t.Eta(); + if (std::fabs(truth_eta) > 1.1) + { + continue; + } + float truth_phi = t.Phi(); + int truth_pid = g4particle->get_pid(); + + if (Verbosity() > 10) + { + std::cout << "DetermineTowerBackground::process_event: determining truth flow, using particle w/ pt / eta / phi " << truth_pt << " / " << truth_eta << " / " << truth_phi << " , embed / PID = " << truthinfo->isEmbeded(g4particle->get_track_id()) << " / " << truth_pid << std::endl; + } + + Hijing_Qx += truth_pt * std::cos(2 * truth_phi); + Hijing_Qy += truth_pt * std::sin(2 * truth_phi); + } + + _Psi2 = std::atan2(Hijing_Qy, Hijing_Qx) / 2.0; + + if (Verbosity() > 0) + { + std::cout << "DetermineTowerBackground::process_event: flow extracted from Hijing truth particles, setting Psi2 = " << _Psi2 << " ( " << _Psi2 / M_PI << " * pi ) " << std::endl; + } + } + else if (_do_flow == 3 || _do_flow == 4) + { // sEPD event plane extraction + // get event plane map + EventplaneinfoMap *epmap = findNode::getClass(topNode, "EventplaneinfoMap"); + if (!epmap) + { + std::cout << "DetermineTowerBackground::process_event: FATAL, EventplaneinfoMap does not exist, cannot extract sEPD flow with do_flow = " << _do_flow << std::endl; + exit(-1); + } + if (!(epmap->empty())) + { + auto *EPDNS = epmap->get(EventplaneinfoMap::sEPDNS); + _Psi2 = EPDNS->get_shifted_psi(2); + } + else + { + _is_flow_failure = true; + _Psi2 = 0; + } + + if (Verbosity() > 0) + { + std::cout << "DetermineTowerBackground::process_event: flow extracted from sEPD, setting Psi2 = " << _Psi2 << " ( " << _Psi2 / M_PI << " * pi ) " << std::endl; + } + + } + + if ( _do_flow >= 1 && _do_flow < 4) { if (Verbosity() > 0) @@ -754,88 +920,6 @@ int DetermineTowerBackground::process_event(PHCompositeNode *topNode) { // Calo event plane _Psi2 = std::atan2(Q_y, Q_x) / 2.0; } - else if (_do_flow == 2) - { // HIJING truth flow extraction - PHG4TruthInfoContainer *truthinfo = findNode::getClass(topNode, "G4TruthInfo"); - - if (!truthinfo) - { - std::cout << "DetermineTowerBackground::process_event: FATAL , G4TruthInfo does not exist , cannot extract truth flow with do_flow = " << _do_flow << std::endl; - return -1; - } - - PHG4TruthInfoContainer::Range range = truthinfo->GetPrimaryParticleRange(); - - float Hijing_Qx = 0; - float Hijing_Qy = 0; - - for (PHG4TruthInfoContainer::ConstIterator iter = range.first; iter != range.second; ++iter) - { - PHG4Particle *g4particle = iter->second; - - if (truthinfo->isEmbeded(g4particle->get_track_id()) != 0) - { - continue; - } - - TLorentzVector t; - t.SetPxPyPzE(g4particle->get_px(), g4particle->get_py(), g4particle->get_pz(), g4particle->get_e()); - - float truth_pt = t.Pt(); - if (truth_pt < 0.4) - { - continue; - } - float truth_eta = t.Eta(); - if (std::fabs(truth_eta) > 1.1) - { - continue; - } - float truth_phi = t.Phi(); - int truth_pid = g4particle->get_pid(); - - if (Verbosity() > 10) - { - std::cout << "DetermineTowerBackground::process_event: determining truth flow, using particle w/ pt / eta / phi " << truth_pt << " / " << truth_eta << " / " << truth_phi << " , embed / PID = " << truthinfo->isEmbeded(g4particle->get_track_id()) << " / " << truth_pid << std::endl; - } - - Hijing_Qx += truth_pt * std::cos(2 * truth_phi); - Hijing_Qy += truth_pt * std::sin(2 * truth_phi); - } - - _Psi2 = std::atan2(Hijing_Qy, Hijing_Qx) / 2.0; - - if (Verbosity() > 0) - { - std::cout << "DetermineTowerBackground::process_event: flow extracted from Hijing truth particles, setting Psi2 = " << _Psi2 << " ( " << _Psi2 / M_PI << " * pi ) " << std::endl; - } - } - else if (_do_flow == 3) - { // sEPD event plane extraction - // get event plane map - EventplaneinfoMap *epmap = findNode::getClass(topNode, "EventplaneinfoMap"); - if (!epmap) - { - std::cout << "DetermineTowerBackground::process_event: FATAL, EventplaneinfoMap does not exist, cannot extract sEPD flow with do_flow = " << _do_flow << std::endl; - exit(-1); - } - if (!(epmap->empty())) - { - auto *EPDNS = epmap->get(EventplaneinfoMap::sEPDNS); - _Psi2 = EPDNS->get_shifted_psi(2); - } - else - { - _is_flow_failure = true; - _Psi2 = 0; - } - - if (Verbosity() > 0) - { - std::cout << "DetermineTowerBackground::process_event: flow extracted from sEPD, setting Psi2 = " << _Psi2 << " ( " << _Psi2 / M_PI << " * pi ) " << std::endl; - } - - } if (std::isnan(_Psi2) || std::isinf(_Psi2)) { @@ -890,7 +974,30 @@ int DetermineTowerBackground::process_event(PHCompositeNode *topNode) std::cout << "DetermineTowerBackground::process_event: flow extraction successful, Psi2 = " << _Psi2 << " ( " << _Psi2 / M_PI << " * pi ) , v2 = " << _v2 << std::endl; } } // if do flow + else if (_do_flow == 4) + { + CentralityInfo *centinfo = findNode::getClass(topNode, "CentralityInfo"); + + if (!centinfo) + { + std::cout << "DetermineTowerBackground::process_event: FATAL, CentralityInfo does not exist, cannot extract centrality with do_flow = " << _do_flow << std::endl; + exit(-1); + } + + int centrality_bin = centinfo->get_centrality_bin(CentralityInfo::PROP::mbd_NS); + + if (centrality_bin > 0 && centrality_bin < 95) + { + _v2 = _CENTRALITY_V2[centrality_bin]; + } + else + { + _v2 = 0; + _is_flow_failure = true; + _Psi2 = 0; + } + } // now calculate energy densities... _nTowers = 0; // store how many towers were used to determine bkg @@ -1110,4 +1217,3 @@ void DetermineTowerBackground::FillNode(PHCompositeNode *topNode) - diff --git a/offline/packages/jetbackground/DetermineTowerBackground.h b/offline/packages/jetbackground/DetermineTowerBackground.h index a8a6d0209c..5c9c9422c7 100644 --- a/offline/packages/jetbackground/DetermineTowerBackground.h +++ b/offline/packages/jetbackground/DetermineTowerBackground.h @@ -13,6 +13,7 @@ #include #include #include +#include // forward declarations class PHCompositeNode; @@ -34,13 +35,47 @@ class DetermineTowerBackground : public SubsysReco int InitRun(PHCompositeNode *topNode) override; int process_event(PHCompositeNode *topNode) override; - void SetBackgroundOutputName(const std::string &name) { _backgroundName = name; } - void SetSeedType(int seed_type) { _seed_type = seed_type; } - void SetFlow(int do_flow) { _do_flow = do_flow; }; - - void SetSeedJetD(float D) { _seed_jet_D = D; }; - void SetSeedJetPt(float pt) { _seed_jet_pt = pt; }; - void SetSeedMaxConst(float max_const) { _seed_max_const = max_const; }; + /** + * Set the name used for the background output object. + * @param name Name to assign to the background output object. + */ +void SetBackgroundOutputName(const std::string &name) { _backgroundName = name; } + /** + * Configure how seed jets are handled. + * @param seed_type Seed-type code that selects the module's seed-jet handling behavior. + */ +void SetSeedType(int seed_type) { _seed_type = seed_type; } + /** + * Enable or disable flow-related processing for background determination. + * + * @param do_flow Integer flag controlling flow processing: 0 disables flow processing, non-zero enables it. + */ +void SetFlow(int do_flow) { _do_flow = do_flow; }; + /** + * Enable overwriting the average calorimeter v2 with a calibration file at the given path or URL. + * @param url Path or URL to the calibration resource to use for the average calo v2 override. + */ + void SetOverwriteCaloV2(std::string &url) + { + m_overwrite_average_calo_v2 = true; + m_overwrite_average_calo_v2_path = url; + } + /** + * Set the radial distance parameter used when handling seed jets. + * + * @param D Radial distance in eta-phi space (R) used to define the seed-jet radius. + */ +void SetSeedJetD(float D) { _seed_jet_D = D; }; + /** + * Set seed jet pT threshold used to identify seed jets. + * @param pt Threshold transverse momentum for seed jets (GeV/c). + */ +void SetSeedJetPt(float pt) { _seed_jet_pt = pt; }; + /** + * Set the maximum constant used when evaluating seed jets. + * @param max_const Maximum allowed constant for seed-related calculations. + */ +void SetSeedMaxConst(float max_const) { _seed_max_const = max_const; }; void UseReweighting(bool do_reweight ) { _do_reweight = do_reweight; } @@ -55,9 +90,34 @@ class DetermineTowerBackground : public SubsysReco int CreateNode(PHCompositeNode *topNode); void FillNode(PHCompositeNode *topNode); - int _do_flow{0}; - float _v2{0}; - float _Psi2{0}; + int LoadCalibrations(); + + std::vector _CENTRALITY_V2; + std::string m_calibName = "JET_AVERAGE_CALO_V2_SEPD_PSI2"; + bool m_overwrite_average_calo_v2{false}; + std::string m_overwrite_average_calo_v2_path; + + /** + * Flag controlling flow-related processing. + * + * When zero, flow calculations are disabled; when non-zero, flow-related + * processing (v2/Psi2 handling and related corrections) is performed. + */ +int _do_flow{0}; + /** + * @brief Event or calibration v2 coefficient used for flow modulation. + * + * Stores the second-order azimuthal anisotropy (v2) value retrieved or computed + * for the current event and used to modulate tower background estimations. + * Defaults to 0. + */ +float _v2{0}; + /** + * Event-plane angle Ψ2 for the current event, expressed in radians. + * + * Stored value used for flow-related background modulation when flow processing is enabled. + */ +float _Psi2{0}; std::vector > _UE; int _nStrips{0}; int _nTowers{0}; @@ -106,4 +166,4 @@ class DetermineTowerBackground : public SubsysReco std::string OHTowerName; }; -#endif +#endif \ No newline at end of file diff --git a/offline/packages/mbd/MbdCalib.cc b/offline/packages/mbd/MbdCalib.cc index 22b1e9930d..a95095181e 100644 --- a/offline/packages/mbd/MbdCalib.cc +++ b/offline/packages/mbd/MbdCalib.cc @@ -1213,6 +1213,15 @@ int MbdCalib::Download_SlewCorr(const std::string& dbase_location) return 1; } +/** + * @brief Load time-RMS calibration data for all channels from a calibration source. + * + * Reads per-channel time-RMS calibration points from the specified CDB (.root) or text (.calib) file, + * validates presence of data, and builds per-channel interpolated lookup vectors for ADC→time-RMS. + * + * @param dbase_location Path to the calibration source (CDB .root file or text .calib file). + * @return int Status code: `1` on success; `-1` if calibration data are missing; `-2` on invalid channel entries in the input; `-3` if the text file could not be opened. + */ int MbdCalib::Download_TimeRMS(const std::string& dbase_location) { //Verbosity(100); @@ -1331,7 +1340,7 @@ int MbdCalib::Download_TimeRMS(const std::string& dbase_location) if ( _trms_y[0].empty() ) { - std::cout << PHWHERE << ", ERROR, unknown file type, " << dbase_location << std::endl; + std::cout << PHWHERE << ", WARNING, trms calib missing " << dbase_location << std::endl; _status = -1; return _status; // file not found } @@ -2418,4 +2427,3 @@ TGraph *MbdCalib::get_lut_graph(const int pmtch, std::string_view type) return g; } - diff --git a/offline/packages/mbd/MbdEvent.cc b/offline/packages/mbd/MbdEvent.cc index d8c72b4395..4b653aa1d2 100644 --- a/offline/packages/mbd/MbdEvent.cc +++ b/offline/packages/mbd/MbdEvent.cc @@ -402,7 +402,14 @@ bool MbdEvent::isbadtch(const int ipmtch) #ifndef ONLINE -// Get raw data from event combined DSTs +/** + * Extracts MBD ADC samples and clock information from two CaloPacket DSTs, validates event-level triggers and packet integrity, fills per-channel sample buffers, and invokes packet- and raw-container processing to produce MBD raw and PMT outputs. + * + * @param dstp Array of two pointers to CaloPacket objects containing the combined DST data (expected packet ids 1001 and 1002). + * @param bbcraws Destination MbdRawContainer to receive per-packet/raw channel data used during processing. + * @param bbcpmts Destination MbdPmtContainer to receive per-PMT calibrated timing and charge results. + * @param gl1raw Optional Gl1Packet used for trigger-based validation when present. + * @return int Status code: Fun4AllReturnCodes::DISCARDEVENT if no input packets are present; Fun4AllReturnCodes::ABORTEVENT for missing, empty, or invalid packets or when trigger validation fails; otherwise the result returned by subsequent processing (normal processing/event index). */ int MbdEvent::SetRawData(std::array< CaloPacket *,2> &dstp, MbdRawContainer *bbcraws, MbdPmtContainer *bbcpmts, Gl1Packet *gl1raw) { //std::cout << "MbdEvent::SetRawData()" << std::endl; @@ -451,6 +458,7 @@ int MbdEvent::SetRawData(std::array< CaloPacket *,2> &dstp, MbdRawContainer *bbc if (dstp[ipkt]) { _nsamples = dstp[ipkt]->iValue(0, "SAMPLES"); + { static bool printcount{true}; if ( printcount && Verbosity() > 0) @@ -460,6 +468,13 @@ int MbdEvent::SetRawData(std::array< CaloPacket *,2> &dstp, MbdRawContainer *bbc } } + // skip empty packets, corrupt event + if ( _nsamples == 0 ) + { + std::cout << PHWHERE << " ERROR, evt " << m_evt << " no samples in Packet " << pktid << std::endl; + return Fun4AllReturnCodes::ABORTEVENT; + } + m_xmitclocks[ipkt] = static_cast(dstp[ipkt]->iValue(0, "CLOCK")); m_femclocks[ipkt][0] = static_cast(dstp[ipkt]->iValue(0, "FEMCLOCK")); @@ -484,9 +499,17 @@ int MbdEvent::SetRawData(std::array< CaloPacket *,2> &dstp, MbdRawContainer *bbc } _mbdsig[feech].SetNSamples( _nsamples ); - _mbdsig[feech].SetXY(m_samp[feech], m_adc[feech]); - + + if ( _nsamples > 0 && _nsamples <= 30 ) + { + _mbdsig[feech].SetXY(m_samp[feech], m_adc[feech]); + } /* + else + { + std::cout << PHWHERE << " empty feech " << feech << std::endl; + } + std::cout << "feech " << feech << std::endl; _mbdsig[feech].Print(); */ @@ -516,7 +539,22 @@ int MbdEvent::SetRawData(std::array< CaloPacket *,2> &dstp, MbdRawContainer *bbc return status; } -#endif // ONLINE +#endif /** + * @brief Extracts MBD raw ADC/sample data from an Event and populates raw and PMT containers. + * + * Reads two packet payloads from the provided Event, transfers per-channel ADC samples into + * internal buffers, and then invokes packet- and raw-container processing to fill bbcraws and + * bbcpmts accordingly. Validates event and packet presence and aborts processing on missing or + * empty packets. + * + * @param event Source Event containing packet ids 1001 and 1002. + * @param bbcraws Destination MbdRawContainer to receive per-packet/raw data. + * @param bbcpmts Destination MbdPmtContainer to receive per-PMT time/charge results. + * @return int Status code indicating outcome: + * - Fun4AllReturnCodes::DISCARDEVENT (offline) or 1 (online) when event is null or not DATAEVENT. + * - Fun4AllReturnCodes::ABORTEVENT (offline) or -1 (online) when a required packet is missing or contains zero samples. + * - Otherwise returns the processing status produced by ProcessPackets/ProcessRawContainer (positive event-processing code). + */ int MbdEvent::SetRawData(Event *event, MbdRawContainer *bbcraws, MbdPmtContainer *bbcpmts) { @@ -565,6 +603,7 @@ int MbdEvent::SetRawData(Event *event, MbdRawContainer *bbcraws, MbdPmtContainer if (p[ipkt]) { _nsamples = p[ipkt]->iValue(0, "SAMPLES"); + { static int counter = 0; if ( counter<1 ) @@ -574,6 +613,15 @@ int MbdEvent::SetRawData(Event *event, MbdRawContainer *bbcraws, MbdPmtContainer counter++; } + // If packets are missing, stop processing event + if ( _nsamples == 0 ) + { + std::cout << PHWHERE << " ERROR, skipping evt " << m_evt << " nsamples = 0 " << pktid << std::endl; + delete p[ipkt]; + p[ipkt] = nullptr; + return Fun4AllReturnCodes::ABORTEVENT; + } + m_xmitclocks[ipkt] = static_cast(p[ipkt]->iValue(0, "CLOCK")); m_femclocks[ipkt][0] = static_cast(p[ipkt]->iValue(0, "FEMCLOCK")); @@ -631,13 +679,30 @@ int MbdEvent::SetRawData(Event *event, MbdRawContainer *bbcraws, MbdPmtContainer return status; } +/** + * @brief Process per-packet waveform data and populate the raw PMT container. + * + * Processes stored per-channel sample graphs to extract timing (ttdc) for T-channels + * and charge timing/amplitude (qtdc, ampl) for Q-channels, applies basic quality + * cuts, and copies results into the provided MbdRawContainer. If sample-max + * calibration is being accumulated (calpass == 1 or _no_sampmax > 0), fills sampmax + * histograms and stops processing the event. + * + * Populates each PMT via bbcraws->get_pmt(ipmt)->set_pmt(...), sets container metadata + * (npmt and clocks), updates the internal event counter, and may mark invalid timings + * with NaN when thresholds, bad-channel flags, or calibration offsets indicate no hit. + * + * @param bbcraws Output raw container to be filled with per-PMT amplitude and timing. + * @return int Current event index after processing; returns -1001 when sampmax calibration + * was filled and normal event processing was intentionally halted. + */ int MbdEvent::ProcessPackets(MbdRawContainer *bbcraws) { //std::cout << "In ProcessPackets" << std::endl; // Do a quick sanity check that all fem counters agree if (m_xmitclocks[0] != m_xmitclocks[1]) { - std::cout << __FILE__ << ":" << __LINE__ << " ERROR, xmitclocks don't agree" << std::endl; + std::cout << __FILE__ << ":" << __LINE__ << " ERROR, xmitclocks don't agree, evt " << m_evt << std::endl; } /* // format changed in run2024, need to update check @@ -673,6 +738,11 @@ int MbdEvent::ProcessPackets(MbdRawContainer *bbcraws) int pmtch = _mbdgeom->get_pmt(ifeech); int type = _mbdgeom->get_type(ifeech); // 0 = T-channel, 1 = Q-channel + if ( _mbdsig[ifeech].GetNSamples()==0 ) + { + continue; + } + // time channel if (type == 0) { @@ -731,6 +801,18 @@ int MbdEvent::ProcessPackets(MbdRawContainer *bbcraws) return m_evt; } +/** + * @brief Convert per-channel raw ADC/sample data into calibrated PMT time and charge and populate the output PMT container. + * + * Processes each frontend channel present in the raw container: applies time corrections, converts sample-based time to nanoseconds, + * applies per-channel charge gain and thresholds, and copies resulting per-PMT time/charge values into the output container. + * Also updates event clocks, runs post-processing on PMT channels, increments the internal event counter, and (for calibration pass 2) + * fills diagnostic trange histograms with raw and pedestal-subtracted timing information. + * + * @param bbcraws Input raw data container holding per-channel ADC/sample and uncalibrated T/Q timing values. + * @param bbcpmts Output PMT container that will be filled with per-PMT charge and time (q, tt, tq). + * @return int The updated internal event counter (m_evt) after processing this event. + */ int MbdEvent::ProcessRawContainer(MbdRawContainer *bbcraws, MbdPmtContainer *bbcpmts) { //std::cout << "In ProcessRawContainer" << std::endl; @@ -739,6 +821,11 @@ int MbdEvent::ProcessRawContainer(MbdRawContainer *bbcraws, MbdPmtContainer *bbc int pmtch = _mbdgeom->get_pmt(ifeech); int type = _mbdgeom->get_type(ifeech); // 0 = T-channel, 1 = Q-channel + if ( _mbdsig[ifeech].GetNSamples()==0 ) + { + continue; + } + // time channel if (type == 0) { @@ -854,8 +941,14 @@ int MbdEvent::ProcessRawContainer(MbdRawContainer *bbcraws, MbdPmtContainer *bbc */ TGraphErrors *gsubpulse = _mbdsig[ifeech].GetGraph(); - Double_t *y = gsubpulse->GetY(); - h2_trange->Fill( y[samp_max], pmtch ); // fill ped-subtracted tdc + if ( gsubpulse ) + { + Double_t *y = gsubpulse->GetY(); + if ( y ) + { + h2_trange->Fill( y[samp_max], pmtch ); // fill ped-subtracted tdc + } + } } } @@ -1550,4 +1643,4 @@ PHG4VtxPoint *MbdEvent::GetPrimaryVtx(PHCompositeNode *topNode) return _vtxp; } -#endif +#endif \ No newline at end of file diff --git a/offline/packages/mbd/MbdReco.cc b/offline/packages/mbd/MbdReco.cc index c19ed37ba9..8cb10bd560 100644 --- a/offline/packages/mbd/MbdReco.cc +++ b/offline/packages/mbd/MbdReco.cc @@ -70,7 +70,15 @@ int MbdReco::InitRun(PHCompositeNode *topNode) return ret; } -//____________________________________________________________________________.. +/** + * @brief Process a single event: validate inputs, ingest raw MBD data, run calibration and calculation, and produce MBD outputs. + * + * @param topNode Top-level node of the event node tree used to locate input and output nodes. + * @return int One of Fun4AllReturnCodes: + * - `Fun4AllReturnCodes::ABORTEVENT` if required nodes are missing or SetRawData requested an abort. + * - `Fun4AllReturnCodes::DISCARDEVENT` if SetRawData requested the event be discarded (including sampmax-only events). + * - `Fun4AllReturnCodes::EVENT_OK` when processing completed or the event was skipped (e.g., missing packet pair or non-fatal negative status). + */ int MbdReco::process_event(PHCompositeNode *topNode) { getNodes(topNode); @@ -103,7 +111,8 @@ int MbdReco::process_event(PHCompositeNode *topNode) int status = Fun4AllReturnCodes::EVENT_OK; if ( m_evtheader!=nullptr ) { - m_mbdevent->set_EventNumber( m_evtheader->get_EvtSequence() ); + _evtnum = m_evtheader->get_EvtSequence(); + m_mbdevent->set_EventNumber( _evtnum ); } if ( m_event!=nullptr ) @@ -125,7 +134,7 @@ int MbdReco::process_event(PHCompositeNode *topNode) static int counter = 0; if ( counter<3 ) { - std::cout << PHWHERE << " Warning, MBD discarding event " << std::endl; + std::cout << PHWHERE << " Warning, MBD discarding event " << _evtnum << std::endl; counter++; } return Fun4AllReturnCodes::DISCARDEVENT; @@ -135,7 +144,7 @@ int MbdReco::process_event(PHCompositeNode *topNode) static int counter = 0; if ( counter<3 ) { - std::cout << PHWHERE << " Warning, MBD aborting event " << std::endl; + std::cout << PHWHERE << " Warning, MBD aborting event " << _evtnum << std::endl; counter++; } return Fun4AllReturnCodes::ABORTEVENT; @@ -365,4 +374,4 @@ int MbdReco::getNodes(PHCompositeNode *topNode) } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/mbd/MbdSig.h b/offline/packages/mbd/MbdSig.h index 0b8b420ffa..5b282d4364 100644 --- a/offline/packages/mbd/MbdSig.h +++ b/offline/packages/mbd/MbdSig.h @@ -32,9 +32,20 @@ class MbdSig void SetY(const Float_t *y, const int invert = 1); void SetXY(const Float_t *x, const Float_t *y, const int invert = 1); + /** + * Get the number of samples stored in the signal. + * + * @returns The number of samples in the signal. + */ +int GetNSamples() { return _nsamples; } + void SetCalib(MbdCalib *mcal); - TH1 *GetHist() { return hpulse; } + /** + * Return the histogram representing the channel pulse. + * @returns Pointer to the pulse TH1 histogram (may be nullptr if not initialized). + */ +TH1 *GetHist() { return hpulse; } TGraphErrors *GetGraph() { return gpulse; } Double_t GetAmpl() { return f_ampl; } Double_t GetTime() { return f_time; } @@ -197,4 +208,4 @@ class MbdSig int _verbose{0}; }; -#endif // __MBDSIG_H__ +#endif // __MBDSIG_H__ \ No newline at end of file diff --git a/offline/packages/micromegas/MicromegasClusterizer.cc b/offline/packages/micromegas/MicromegasClusterizer.cc index c57d6ed3f5..812e68ff04 100644 --- a/offline/packages/micromegas/MicromegasClusterizer.cc +++ b/offline/packages/micromegas/MicromegasClusterizer.cc @@ -150,15 +150,34 @@ int MicromegasClusterizer::InitRun(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } -//_______________________________________________________________________________ +/** + * @brief Build MICROMEGAS clusters from hits found in the event node tree. + * + * Reads MICROMEGAS geometry and hitset containers from the provided top-level + * node, groups time-ordered hits by contiguous strip number into clusters, + * computes pedestal-subtracted ADC sums, weighted local positions and position + * errors (using per-strip calibration or a default pedestal), creates + * TrkrClusterv5 objects, and records cluster<->hit associations. Single-strip + * clusters may be dropped according to configuration. Cluster counts per + * hitset are accumulated for diagnostics. + * + * @param topNode Top-level PHCompositeNode containing geometry, hitsets, + * cluster containers, and ActsGeometry required for processing. + * @returns Fun4AllReturnCodes::EVENT_OK on success. + */ int MicromegasClusterizer::process_event(PHCompositeNode *topNode) { // geometry PHG4CylinderGeomContainer* geonode = nullptr; for( std::string geonodename: {"CYLINDERGEOM_MICROMEGAS_FULL", "CYLINDERGEOM_MICROMEGAS" } ) - { if(( geonode = findNode::getClass(topNode, geonodename.c_str()) )) { break; -}} + { + // try load node and test + geonode = findNode::getClass(topNode, geonodename); + if( geonode ) { break;} + } + + //ma assert(geonode); // hitset container @@ -182,8 +201,8 @@ int MicromegasClusterizer::process_event(PHCompositeNode *topNode) for( auto hitset_it = hitset_range.first; hitset_it != hitset_range.second; ++hitset_it ) { // get hitset, key and layer - TrkrHitSet* hitset = hitset_it->second; - const TrkrDefs::hitsetkey hitsetkey = hitset_it->first; + const auto& [hitsetkey, hitset] = *hitset_it; + const auto layer = TrkrDefs::getLayer(hitsetkey); const auto tileid = MicromegasDefs::getTileId(hitsetkey); @@ -215,17 +234,42 @@ int MicromegasClusterizer::process_event(PHCompositeNode *topNode) using range_list_t = std::vector; range_list_t ranges; - // loop over hits - const auto hit_range = hitset->getHits(); + // Make a local copy of hitsets, sorted along strips + /* when there are multiple hits on the same strip, only the first one (in time) is kept */ + class StripSortFtor + { + public: + /** + * @brief Orders two hit keys by their Micromegas strip index in ascending order. + * + * Compares the strip numbers extracted from each hit key and establishes an + * ordering based on the numeric strip index. + * + * @param first Hit key of the first hit to compare. + * @param second Hit key of the second hit to compare. + * @return true if the strip index of `first` is less than that of `second`, false otherwise. + */ + bool operator() ( const TrkrDefs::hitkey& first, const TrkrDefs::hitkey& second ) const + { return MicromegasDefs::getStrip(first) < MicromegasDefs::getStrip(second); } + }; + + using LocalMap = std::map; + LocalMap local_hitmap; + + { + // loop over hits + const auto hit_range = hitset->getHits(); + std::copy( hit_range.first, hit_range.second, std::inserter(local_hitmap, local_hitmap.end()) ); + } // keep track of first iterator of runing cluster - auto begin = hit_range.first; + auto begin = local_hitmap.begin(); // keep track of previous strip uint16_t previous_strip = 0; bool first = true; - for( auto hit_it = hit_range.first; hit_it != hit_range.second; ++hit_it ) + for( auto hit_it = local_hitmap.begin(); hit_it != local_hitmap.end(); ++hit_it ) { // get hit key @@ -233,18 +277,11 @@ int MicromegasClusterizer::process_event(PHCompositeNode *topNode) // get strip number const auto strip = MicromegasDefs::getStrip( hitkey ); - - if( first ) + if( !first && (strip - previous_strip > 1 ) ) { - previous_strip = strip; - first = false; - continue; - - } else if( strip - previous_strip > 1 ) { - // store current cluster range - ranges.push_back( std::make_pair( begin, hit_it ) ); + ranges.emplace_back( begin, hit_it ); // reinitialize begin of next cluster range begin = hit_it; @@ -252,13 +289,13 @@ int MicromegasClusterizer::process_event(PHCompositeNode *topNode) } // update previous strip + first = false; previous_strip = strip; } // store last cluster - if( begin != hit_range.second ) { ranges.push_back( std::make_pair( begin, hit_range.second ) ); -} + if( begin != local_hitmap.end() ) { ranges.emplace_back( begin, local_hitmap.end() ); } // initialize cluster count int cluster_count = 0; @@ -419,4 +456,4 @@ int MicromegasClusterizer::End(PHCompositeNode* /*topNode*/) } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/micromegas/MicromegasCombinedDataDecoder.cc b/offline/packages/micromegas/MicromegasCombinedDataDecoder.cc index a5269617f7..779e91ffca 100644 --- a/offline/packages/micromegas/MicromegasCombinedDataDecoder.cc +++ b/offline/packages/micromegas/MicromegasCombinedDataDecoder.cc @@ -117,7 +117,16 @@ int MicromegasCombinedDataDecoder::InitRun(PHCompositeNode* topNode) return Fun4AllReturnCodes::EVENT_OK; } -//___________________________________________________________________________ +/** + * @brief Decode Micromegas raw hits and populate the TRKR_HITSET with validated hits. + * + * Maps raw FEE/channel data to detector coordinates, filters permanently masked and hot channels, + * applies calibration (pedestal and RMS) and amplitude thresholds, selects the peak sample per channel, + * and inserts non-duplicated TrkrHitv2 entries into the TRKR_HITSET container. + * + * @param topNode Root PHCompositeNode containing the MicromegasRawHitContainer and TRKR_HITSET. + * @return int Fun4AllReturnCodes::EVENT_OK on success. + */ int MicromegasCombinedDataDecoder::process_event(PHCompositeNode* topNode) { // load relevant nodes @@ -203,13 +212,14 @@ int MicromegasCombinedDataDecoder::process_event(PHCompositeNode* topNode) // loop over sample_range find maximum const auto sample_range = std::make_pair(rawhit->get_sample_begin(), rawhit->get_sample_end()); - std::vector adc_list; + using sample_pair_t = std::pair; + std::vector adc_list; for (auto is = std::max(m_sample_min, sample_range.first); is < std::min(m_sample_max, sample_range.second); ++is) { const uint16_t adc = rawhit->get_adc(is); if (adc != MicromegasDefs::m_adc_invalid) { - adc_list.push_back(adc); + adc_list.emplace_back(is, adc); } } @@ -220,16 +230,18 @@ int MicromegasCombinedDataDecoder::process_event(PHCompositeNode* topNode) // get max adc value in range /* TODO: use more advanced signal processing */ - auto max_adc = *std::max_element(adc_list.begin(), adc_list.end()); + auto max_adc = *std::max_element(adc_list.begin(), adc_list.end(), + [](const sample_pair_t& first, const sample_pair_t& second) + { return first.second < second.second; } ); // compare to hard min_adc value - if (max_adc < m_min_adc) + if (max_adc.second < m_min_adc) { continue; } // compare to threshold - if (max_adc < pedestal + m_n_sigma * rms) + if (max_adc.second < pedestal + m_n_sigma * rms) { continue; } @@ -243,7 +255,8 @@ int MicromegasCombinedDataDecoder::process_event(PHCompositeNode* topNode) << " tile: " << tile << " channel: " << channel << " strip: " << strip - << " adc: " << max_adc + << " sample: " << max_adc.first + << " adc: " << max_adc.second << std::endl; } @@ -251,19 +264,19 @@ int MicromegasCombinedDataDecoder::process_event(PHCompositeNode* topNode) const auto hitset_it = trkrhitsetcontainer->findOrAddHitSet(hitsetkey); // generate hit key - const TrkrDefs::hitkey hitkey = MicromegasDefs::genHitKey(strip); + const TrkrDefs::hitkey hitkey = MicromegasDefs::genHitKey(strip, max_adc.first); // find existing hit, or create - auto hit = hitset_it->second->getHit(hitkey); + auto* hit = hitset_it->second->getHit(hitkey); if (hit) { - // std::cout << "MicromegasCombinedDataDecoder::process_event - duplicated hit, hitsetkey: " << hitsetkey << " strip: " << strip << std::endl; + std::cout << "MicromegasCombinedDataDecoder::process_event - duplicated hit, hitsetkey: " << hitsetkey << " strip: " << strip << std::endl; continue; } // create hit, assign adc and insert in hitset hit = new TrkrHitv2; - hit->setAdc(max_adc); + hit->setAdc(max_adc.second); hitset_it->second->addHitSpecificKey(hitkey, hit); // increment counter @@ -291,4 +304,4 @@ int MicromegasCombinedDataDecoder::End(PHCompositeNode* /*topNode*/) } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/micromegas/MicromegasCombinedDataDecoder.h b/offline/packages/micromegas/MicromegasCombinedDataDecoder.h index 32eebc56e0..dc2855513d 100644 --- a/offline/packages/micromegas/MicromegasCombinedDataDecoder.h +++ b/offline/packages/micromegas/MicromegasCombinedDataDecoder.h @@ -17,7 +17,66 @@ class PHCompositeNode; -/// micromegas raw data decoder +/** + * MicromegasCombinedDataDecoder decodes Micromegas raw data, applies calibration, + * hot-channel masking and channel-to-channel mapping, and produces reconstructed hits. + */ + +/** + * Initialize global resources required by the decoder. + * @param topNode Top-level node of the framework's node tree. + * @returns `0` on success, non-zero on failure. + */ + +/** + * Perform per-run initialization (e.g., load calibration and hot-channel map). + * @param topNode Top-level node of the framework's node tree. + * @returns `0` on success, non-zero on failure. + */ + +/** + * Decode and process Micromegas raw data for a single event, producing calibrated + * and mapped hits while applying thresholds and hot-channel masking. + * @param topNode Top-level node of the framework's node tree. + * @returns `0` on success, non-zero on failure. + */ + +/** + * Finalize processing and release any allocated resources. + * @param topNode Top-level node of the framework's node tree. + * @returns `0` on success, non-zero on failure. + */ + +/** + * Set the calibration file path to use for pedestal and RMS information. + * @param value Path to the calibration file. + */ + +/** + * Set the hot-channel map file path used to identify and mask noisy channels. + * @param value Path to the hot-channel map file. + */ + +/** + * Set the number of RMS sigmas used to compute the static per-channel threshold. + * @param value Number of RMS sigmas. + */ + +/** + * Set the minimum ADC value (after pedestal subtraction and RMS consideration). + * Channels with ADC below this value are treated as faulty and ignored. + * @param value Minimum ADC threshold. + */ + +/** + * Set the minimum sample index considered when searching for signal hits. + * @param value Minimum sample index (inclusive). + */ + +/** + * Set the maximum sample index considered when searching for signal hits. + * @param value Maximum sample index (inclusive). + */ class MicromegasCombinedDataDecoder : public SubsysReco { public: @@ -46,13 +105,24 @@ class MicromegasCombinedDataDecoder : public SubsysReco void set_n_sigma(double value) { m_n_sigma = value; } /// set minimum ADC value, disregarding pedestal and RMS. - /** This removes faulty channels for which calibration has failed */ + /** + * Set the minimum ADC threshold used to ignore channels with unreliable pedestal or RMS. + * @param value Minimum ADC value; channels with pedestal or RMS below this threshold are treated as faulty and ignored. + */ void set_min_adc(double value) { m_min_adc = value; } - /// set min sample for noise estimation + /** + * Set the minimum sample index considered for signal hits. + * This value defines the lower bound (inclusive) of the sample window used when identifying signal samples. + * + * @param value Minimum sample index to accept as part of a signal. + */ void set_sample_min(uint16_t value) { m_sample_min = value; } - /// set min sample for noise estimation + /** + * Set the maximum sample index considered when selecting signal hits. + * @param value Maximum sample index (inclusive) to include in signal-hit selection. + */ void set_sample_max(uint16_t value) { m_sample_max = value; } private: @@ -85,11 +155,11 @@ class MicromegasCombinedDataDecoder : public SubsysReco uint16_t m_sample_min = 0; /// max sample for signal - uint16_t m_sample_max = 100; + uint16_t m_sample_max = 1024; /// keep track of number of hits per hitsetid using hitcountmap_t = std::map; hitcountmap_t m_hitcounts; }; -#endif +#endif \ No newline at end of file diff --git a/offline/packages/micromegas/MicromegasDefs.cc b/offline/packages/micromegas/MicromegasDefs.cc index 0766d78b8e..529d2b8146 100644 --- a/offline/packages/micromegas/MicromegasDefs.cc +++ b/offline/packages/micromegas/MicromegasDefs.cc @@ -25,57 +25,104 @@ namespace * 8 - 16 segmentation type * 0 - 8 tile id */ - static constexpr unsigned int kBitShiftSegmentation = 8; - static constexpr unsigned int kBitShiftTileId = 0; + constexpr unsigned int kBitShiftSegmentation = 8; + constexpr unsigned int kBitShiftTileId = 0; //! bit shift for hit key - static constexpr unsigned int kBitShiftStrip = 0; + constexpr unsigned int kBitShiftStrip = 0; + constexpr unsigned int kBitShiftSample = 8; } namespace MicromegasDefs { - //________________________________________________________________ + /** + * @brief Construct a Micromegas hitset key encoding layer, segmentation, and tile. + * + * Builds a TrkrDefs::hitsetkey for a Micromegas detector layer with the given + * segmentation type and tile identifier encoded into the key. + * + * @param layer Micromegas layer number. + * @param type Segmentation type to embed in the hitset key. + * @param tile Tile identifier (0–255) to embed in the hitset key. + * @return TrkrDefs::hitsetkey Hitset key with the layer, segmentation type, and tile id encoded. + */ TrkrDefs::hitsetkey genHitSetKey(uint8_t layer, SegmentationType type, uint8_t tile ) { TrkrDefs::hitsetkey key = TrkrDefs::genHitSetKey(TrkrDefs::TrkrId::micromegasId, layer); - TrkrDefs::hitsetkey tmp = to_underlying_type(type); + TrkrDefs::hitsetkey tmp = to_underlying_type(type)&0x1U; key |= (tmp << kBitShiftSegmentation); - tmp = tile; + tmp = tile&0xFFU; key |= (tmp << kBitShiftTileId); return key; } - //________________________________________________________________ + /** + * @brief Retrieve the segmentation type encoded in a Micromegas hitset key. + * + * @param key Hitset key from which the segmentation type is extracted. + * @return SegmentationType The segmentation type stored at the `kBitShiftSegmentation` bit field of the key. + */ SegmentationType getSegmentationType(TrkrDefs::hitsetkey key) { TrkrDefs::hitsetkey tmp = (key >> kBitShiftSegmentation); - return static_cast(tmp); + return static_cast(tmp&0x1U); } - //________________________________________________________________ + /** + * @brief Extracts the tile identifier encoded in a Micromegas hitset key. + * + * @param key Hitset key containing the encoded tile id. + * @return uint8_t Tile identifier (0–255) extracted from the key. + */ uint8_t getTileId(TrkrDefs::hitsetkey key) { TrkrDefs::hitsetkey tmp = (key >> kBitShiftTileId); - return tmp; + return tmp&0xFFU; } - //________________________________________________________________ - TrkrDefs::hitkey genHitKey(uint16_t strip) + /** + * @brief Compose a hit key encoding a strip index and a sample identifier. + * + * Encodes the lower 8 bits of |strip| and the lower 16 bits of |sample| into a single TrkrDefs::hitkey. + * + * @param strip Lower 8-bit strip index to embed in the key. + * @param sample 16-bit sample identifier to embed in the key. + * @return TrkrDefs::hitkey Hit key with strip in the low 8 bits and sample placed at the sample bit field. + */ + TrkrDefs::hitkey genHitKey(uint16_t strip, uint16_t sample) { - TrkrDefs::hitkey key = strip << kBitShiftStrip; - return key; + const TrkrDefs::hitkey key = (strip&0xFFU) << kBitShiftStrip; + const TrkrDefs::hitkey tmp = (sample&0xFFFFU) << kBitShiftSample; + return key|tmp; } - //________________________________________________________________ - uint16_t getStrip( TrkrDefs::hitkey key ) + /** + * @brief Retrieves the strip index encoded in a hit key. + * + * @param key Hit key containing the encoded strip and sample fields. + * @return uint8_t Strip index (0–255). + */ + uint8_t getStrip( TrkrDefs::hitkey key ) { TrkrDefs::hitkey tmp = (key >> kBitShiftStrip); - return tmp; + return tmp & 0xFFU; + } + + /** + * @brief Extracts the sample index encoded in a hit key. + * + * @param key Hit key containing an encoded sample field. + * @return uint16_t Sample index extracted from the hit key (16-bit value). + */ + uint16_t getSample( TrkrDefs::hitkey key ) + { + TrkrDefs::hitkey tmp = (key >> kBitShiftSample); + return tmp & 0xFFFFU; } //________________________________________________________________ @@ -92,4 +139,4 @@ namespace MicromegasDefs return getTileId( tmp ); } -} +} \ No newline at end of file diff --git a/offline/packages/micromegas/MicromegasDefs.h b/offline/packages/micromegas/MicromegasDefs.h index c95fdffd72..5e871d4863 100644 --- a/offline/packages/micromegas/MicromegasDefs.h +++ b/offline/packages/micromegas/MicromegasDefs.h @@ -25,7 +25,60 @@ namespace MicromegasDefs }; //! tells the drift direction for a given micromegas layer - /*! this is needed for properly implementing transverse diffusion in the layer */ + /** + * Direction of electron drift used for transverse diffusion handling in a layer. + */ + +/** + * @brief Generate a hitset key for a Micromegas layer. + * @param layer Layer index. + * @param segmentation Segmentation direction along the cylinder. + * @param tile Tile index within the layer. + * @returns The generated TrkrDefs::hitsetkey for the specified layer, segmentation, and tile. + */ + +/** + * @brief Extract the segmentation type encoded in a hitset key. + * @param key Hitset key to decode. + * @returns The SegmentationType stored in the provided hitset key. + */ + +/** + * @brief Extract the tile id encoded in a hitset key. + * @param key Hitset key to decode. + * @returns The tile index stored in the provided hitset key. + */ + +/** + * @brief Generate a hit key from strip and sample indices within a tile. + * @param strip Strip index inside the tile. + * @param sample Sample index (defaults to 0). + * @returns The generated TrkrDefs::hitkey encoding the strip and sample. + */ + +/** + * @brief Retrieve the strip index from a hit key. + * @param key Hit key to decode. + * @returns The strip index encoded in the hit key. + */ + +/** + * @brief Retrieve the sample index from a hit key. + * @param key Hit key to decode. + * @returns The sample index encoded in the hit key. + */ + +/** + * @brief Extract the segmentation type encoded in a cluster key. + * @param key Cluster key to decode. + * @returns The SegmentationType stored in the provided cluster key. + */ + +/** + * @brief Extract the tile id encoded in a cluster key. + * @param key Cluster key to decode. + * @returns The tile index stored in the provided cluster key. + */ enum class DriftDirection: uint8_t { INWARD, @@ -60,11 +113,15 @@ namespace MicromegasDefs /*! * @brief Generate a hitkey from strip index inside tile * @param[in] strip strip index + * @param[in] sample sample index */ - TrkrDefs::hitkey genHitKey(uint16_t strip ); + TrkrDefs::hitkey genHitKey(uint16_t strip, uint16_t sample = 0 ); //! get strip from hit key - uint16_t getStrip(TrkrDefs::hitkey); + uint8_t getStrip(TrkrDefs::hitkey); + + //! get sample from hit key + uint16_t getSample(TrkrDefs::hitkey); /*! * @brief Get the segmentation type from cluster key @@ -128,4 +185,4 @@ namespace MicromegasDefs } -#endif +#endif \ No newline at end of file diff --git a/offline/packages/mvtx/CylinderGeom_Mvtx.cc b/offline/packages/mvtx/CylinderGeom_Mvtx.cc index e220d1474d..d0bd004350 100644 --- a/offline/packages/mvtx/CylinderGeom_Mvtx.cc +++ b/offline/packages/mvtx/CylinderGeom_Mvtx.cc @@ -8,9 +8,22 @@ #include #include // for operator<<, basic_ostream::operator<<, basic_... -using namespace std; using Segmentation = SegmentationAlpide; +/** + * @brief Initialize cylinder-based MVTX geometry for a specific layer. + * + * Populates geometric constants and local reference positions for chips, + * modules, half-staves, and staves (values derived from mvtx_stave_v1.gdml). + * Also sets pixel pitch and sensor thickness from the segmentation definition. + * + * @param in_layer Layer index for this geometry. + * @param in_N_staves Number of staves in the layer. + * @param in_layer_nominal_radius Nominal radius of the layer (cm). + * @param in_phistep Angular step between consecutive staves (radians). + * @param in_phitilt Stave tilt angle (radians). + * @param in_phi0 Reference phi offset for stave 0 (radians). + */ CylinderGeom_Mvtx::CylinderGeom_Mvtx( int in_layer, int in_N_staves, @@ -72,6 +85,13 @@ CylinderGeom_Mvtx::CylinderGeom_Mvtx( return; } +/** + * Map a world-space position to the corresponding MVTX stave and chip indices. + * + * @param world Three-element vector {x, y, z} containing the point in the detector (world) coordinate frame. + * @param stave_index Output parameter set to the stave index determined from the azimuthal angle (phi) of the world position. + * @param chip_index Output parameter set to the chip index determined from the z coordinate of the world position (expected range 0–9). + */ void CylinderGeom_Mvtx::get_sensor_indices_from_world_coords(std::vector& world, unsigned int& stave_index, unsigned int& chip_index) { // stave number is fom phi @@ -89,12 +109,24 @@ void CylinderGeom_Mvtx::get_sensor_indices_from_world_coords(std::vector double chip_delta_z = (inner_loc_chip_in_module[8][2] - inner_loc_chip_in_module[0][2]) / 8.0; // int chip_tmp = (int) (world[2]/chip_delta_z) + 4; // 0-9 int chip_tmp = round(world[2] / chip_delta_z) + 4; // 0-9 - // std::cout << " z " << world[2] << " chip_delta_z " << chip_delta_z << " chip_tmp " << chip_tmp << endl; + // std::cout << " z " << world[2] << " chip_delta_z " << chip_delta_z << " chip_tmp " << chip_tmp << std::endl; stave_index = stave_tmp; chip_index = chip_tmp; } +/** + * @brief Map sensor-local coordinates to detector pixel indices, with edge clamping. + * + * Adjusts coordinates that lie within 5e-6 units of the active matrix edges to the nearest valid edge, + * converts the adjusted sensor-local position into chip-local coordinates, and computes the corresponding + * detector row and column. + * + * @param sensor_local Sensor-local position (TVector3). + * @param iRow Output row index within the detector (modified on success). + * @param iCol Output column index within the detector (modified on success). + * @return bool `true` if the position maps to a valid pixel and `iRow`/`iCol` were set, `false` otherwise. + */ bool CylinderGeom_Mvtx::get_pixel_from_local_coords(TVector3 sensor_local, int& iRow, int& iCol) { // YCM (2020-01-02): It seems that due some round issues, local coords of hits at the edge of the sensor volume @@ -102,15 +134,15 @@ bool CylinderGeom_Mvtx::get_pixel_from_local_coords(TVector3 sensor_local, int& double EPS = 5e-6; if (fabs(fabs(sensor_local.X()) - SegmentationAlpide::ActiveMatrixSizeRows / 2.F) < EPS) { - // cout << " Adjusting X, before X= " << sensor_local.X() << endl; + // std::cout << " Adjusting X, before X= " << sensor_local.X() << std::endl; sensor_local.SetX(((sensor_local.X() < 0) ? -1 : 1) * (SegmentationAlpide::ActiveMatrixSizeRows / 2.F - EPS)); - // cout << " Adjusting X, after X= " << sensor_local.X() << endl; + // std::cout << " Adjusting X, after X= " << sensor_local.X() << std::endl; } if (fabs(fabs(sensor_local.Z()) - SegmentationAlpide::ActiveMatrixSizeCols / 2.F) < EPS) { - // cout << " Adjusting Z, before Z= " << sensor_local.Z() << endl; + // std::cout << " Adjusting Z, before Z= " << sensor_local.Z() << std::endl; sensor_local.SetZ(((sensor_local.Z() < 0) ? -1 : 1) * (SegmentationAlpide::ActiveMatrixSizeCols / 2.F - EPS)); - // cout << " Adjusting Z, after Z= " << sensor_local.Z() << endl; + // std::cout << " Adjusting Z, after Z= " << sensor_local.Z() << std::endl; } // YCM (2020-01-02): go from sensor to chip local coords TVector3 in_chip = sensor_local; @@ -120,23 +152,38 @@ bool CylinderGeom_Mvtx::get_pixel_from_local_coords(TVector3 sensor_local, int& return SegmentationAlpide::localToDetector(in_chip.X(), in_chip.Z(), iRow, iCol); } +/** + * @brief Convert sensor-local coordinates to a linear (0-based) pixel index. + * + * Translates a point in sensor-local coordinates to row/column pixel indices and returns + * the corresponding linear index computed as (col * get_NX() + row). + * + * @param sensor_local Point in sensor-local coordinate system. + * @return int Linear pixel index (0-based). + * + * Notes: + * - If the point lies outside the active sensor area, the function prints a diagnostic + * message and still returns the computed index. + * - If the derived row or column is outside valid ranges, a diagnostic message is printed. + */ int CylinderGeom_Mvtx::get_pixel_from_local_coords(const TVector3& sensor_local) { - int Ngridx, Ngridz; + int Ngridx; + int Ngridz; bool px_in = get_pixel_from_local_coords(sensor_local, Ngridx, Ngridz); if (!px_in) { - cout << PHWHERE + std::cout << PHWHERE << " Pixel is out sensor. (" << sensor_local.X() << ", " << sensor_local.Y() << ", " << sensor_local.Z() << ")." - << endl; + << std::endl; } if (Ngridx < 0 || Ngridx >= get_NX() || Ngridz < 0 || Ngridz >= get_NZ()) { - cout << PHWHERE << "Wrong pixel value X= " << Ngridx << " and Z= " << Ngridz << endl; + std::cout << PHWHERE << "Wrong pixel value X= " << Ngridx << " and Z= " << Ngridz << std::endl; } // numbering starts at zero @@ -151,14 +198,27 @@ TVector3 CylinderGeom_Mvtx::get_local_coords_from_pixel(int NXZ) return get_local_coords_from_pixel(Ngridx, Ngridz); } +/** + * @brief Convert detector pixel indices to local sensor coordinates. + * + * Translates a pixel row/column in detector (chip) indices to the corresponding + * position in the sensor-local coordinate system. + * + * @param iRow Detector row index (chip-local). + * @param iCol Detector column index (chip-local). + * @return TVector3 Local coordinates in the sensor frame corresponding to the pixel. + * + * If the provided indices are outside the detector range, a diagnostic message + * is written to std::cout and the function still returns the transformed coordinate. + */ TVector3 CylinderGeom_Mvtx::get_local_coords_from_pixel(int iRow, int iCol) { TVector3 local; bool check = SegmentationAlpide::detectorToLocal((float) iRow, (float) iCol, local); if (!check) { - cout << PHWHERE << "Pixel coord ( " << iRow << ", " << iCol << " )" - << "out of range" << endl; + std::cout << PHWHERE << "Pixel coord ( " << iRow << ", " << iCol << " )" + << "out of range" << std::endl; } // Transform location in chip to location in sensors TVector3 trChipToSens(loc_sensor_in_chip[0], @@ -168,6 +228,13 @@ TVector3 CylinderGeom_Mvtx::get_local_coords_from_pixel(int iRow, int iCol) return local; } +/** + * @brief Writes a one-line summary of this CylinderGeom_Mvtx to the provided output stream. + * + * The summary includes layer, layer_radius, N_staves, N_half_staves, pixel_x, pixel_z, and pixel_thickness. + * + * @param os Output stream that receives the summary line. + */ void CylinderGeom_Mvtx::identify(std::ostream& os) const { os << "CylinderGeom_Mvtx: layer: " << layer @@ -177,7 +244,7 @@ void CylinderGeom_Mvtx::identify(std::ostream& os) const << ", pixel_x: " << pixel_x << ", pixel_z: " << pixel_z << ", pixel_thickness: " << pixel_thickness - << endl; + << std::endl; return; } @@ -187,22 +254,48 @@ int CylinderGeom_Mvtx::get_NZ() const return SegmentationAlpide::NCols; } +/** + * @brief Get the number of sensor rows (pixel count in X / row direction). + * + * @return int Number of rows per sensor (NRows). + */ int CylinderGeom_Mvtx::get_NX() const { return SegmentationAlpide::NRows; } -int CylinderGeom_Mvtx::get_pixel_X_from_pixel_number(int NXZ) +/** + * @brief Get the sensor row index (X) corresponding to a linear pixel index. + * + * @param NXZ Linear pixel index (0-based). + * @return int Row index (X) within [0, get_NX() - 1]. + */ +int CylinderGeom_Mvtx::get_pixel_X_from_pixel_number(int NXZ) const { return NXZ % get_NX(); } -int CylinderGeom_Mvtx::get_pixel_Z_from_pixel_number(int NXZ) +/** + * @brief Compute the Z (column) index for a pixel given its linear pixel number. + * + * @param NXZ Linear pixel index where pixels are numbered row-major (x varies fastest). + * @return int Z (column) index corresponding to the linear pixel number. + */ +int CylinderGeom_Mvtx::get_pixel_Z_from_pixel_number(int NXZ) const { return NXZ / get_NX(); } -int CylinderGeom_Mvtx::get_pixel_number_from_xbin_zbin(int xbin, int zbin) // obsolete +/** + * Compute the linear pixel index from x (row) and z (column) indices. + * + * @param xbin X (row) pixel index (0-based). + * @param zbin Z (column) pixel index (0-based). + * @return int Linear pixel index in row-major order: `xbin + zbin * get_NX()`. + * + * @deprecated Obsolete; retained for backward compatibility. + */ +int CylinderGeom_Mvtx::get_pixel_number_from_xbin_zbin(int xbin, int zbin) const // obsolete { return xbin + zbin * get_NX(); -} +} \ No newline at end of file diff --git a/offline/packages/mvtx/CylinderGeom_Mvtx.h b/offline/packages/mvtx/CylinderGeom_Mvtx.h index e28da2c594..70e1e1320b 100644 --- a/offline/packages/mvtx/CylinderGeom_Mvtx.h +++ b/offline/packages/mvtx/CylinderGeom_Mvtx.h @@ -8,12 +8,89 @@ #include +/** + * Construct MVTX cylinder geometry for a given layer. + * @param layer Layer index for this cylinder geometry. + * @param in_N_staves Number of staves in this layer. + * @param in_layer_nominal_radius Nominal radius of the layer. + * @param in_phistep Angular step in phi between adjacent staves (radians). + * @param in_phitilt Stave phi tilt angle (radians). + * @param in_phi0 Base phi offset for stave 0 (radians). + */ + +/** + * Print a short identification of this geometry to the provided output stream. + * @param os Output stream to write the identification to. + */ + +/** + * Determine stave and chip indices from a world-coordinate position. + * @param world Three-element vector containing world coordinates (x,y,z). + * @param stave Output index of the stave corresponding to the world position. + * @param chip Output index of the chip within the stave corresponding to the world position. + */ + +/** + * Map local sensor coordinates to pixel row/column indices. + * @param sensor_local Local coordinates in the sensor frame. + * @param iRow Output pixel row index. + * @param iCol Output pixel column index. + * @returns `true` if the local coordinates map to a valid pixel, `false` otherwise. + */ + +/** + * Map local sensor coordinates to a single pixel number. + * @param sensor_local Local coordinates in the sensor frame. + * @returns Pixel number corresponding to the provided local coordinates, or a sentinel/invalid value if out of range. + */ + +/** + * Get local sensor coordinates for a given pixel number. + * @param NXZ Pixel number. + * @returns Local coordinates (x,y,z) at the center of the specified pixel. + */ + +/** + * Get local sensor coordinates for a given pixel row and column. + * @param iRow Pixel row index. + * @param iCol Pixel column index. + * @returns Local coordinates (x,y,z) at the center of the specified pixel. + */ + +/** + * Extract the X (column) index from a combined pixel number. + * @param NXZ Combined pixel number. + * @returns X (column) index for the given pixel number. + */ + +/** + * Extract the Z (row) index from a combined pixel number. + * @param NXZ Combined pixel number. + * @returns Z (row) index for the given pixel number. + */ + +/** + * Map x-bin and z-bin indices to a combined pixel number. (Obsolete) + * @param xbin X (column) bin index. + * @param zbin Z (row) bin index. + * @returns Combined pixel number corresponding to the provided x/z indices. + */ + +/** + * Number of pixels in Z (rows). + * @returns Number of Z bins (pixel rows). + */ + +/** + * Number of pixels in X (columns). + * @returns Number of X bins (pixel columns). + */ class CylinderGeom_Mvtx : public PHG4CylinderGeom { public: CylinderGeom_Mvtx( int layer, - int in_Nstaves, + int in_N_staves, double in_layer_nominal_radius, double in_phistep, double in_phitilt, @@ -31,7 +108,7 @@ class CylinderGeom_Mvtx : public PHG4CylinderGeom { } - ~CylinderGeom_Mvtx() override {} + ~CylinderGeom_Mvtx() override = default; // from PHObject void identify(std::ostream& os = std::cout) const override; @@ -53,11 +130,11 @@ class CylinderGeom_Mvtx : public PHG4CylinderGeom TVector3 get_local_coords_from_pixel(int NXZ); TVector3 get_local_coords_from_pixel(int iRow, int iCol); - int get_pixel_X_from_pixel_number(int NXZ); + int get_pixel_X_from_pixel_number(int NXZ) const; - int get_pixel_Z_from_pixel_number(int NXZ); + int get_pixel_Z_from_pixel_number(int NXZ) const; - int get_pixel_number_from_xbin_zbin(int xbin, int zbin); // obsolete + int get_pixel_number_from_xbin_zbin(int xbin, int zbin) const; // obsolete double get_stave_phi_tilt() const { return stave_phi_tilt; } double get_stave_phi_0() const { return stave_phi_0; } @@ -97,4 +174,4 @@ class CylinderGeom_Mvtx : public PHG4CylinderGeom ClassDefOverride(CylinderGeom_Mvtx, 2) }; -#endif +#endif \ No newline at end of file diff --git a/offline/packages/mvtx/MvtxClusterPruner.cc b/offline/packages/mvtx/MvtxClusterPruner.cc index 24d41db63d..c85eadbc21 100644 --- a/offline/packages/mvtx/MvtxClusterPruner.cc +++ b/offline/packages/mvtx/MvtxClusterPruner.cc @@ -12,108 +12,162 @@ #include #include -#include #include +#include -#include #include +#include namespace { //! range adaptor to be able to use range-based for loop - template class range_adaptor + template + class range_adaptor { - public: - range_adaptor( const T& range ):m_range(range){} - const typename T::first_type& begin() {return m_range.first;} - const typename T::second_type& end() {return m_range.second;} - private: + public: + /** + * @brief Construct a range_adaptor that provides begin/end access for a pair-like range. + * + * Wraps the provided range by reference so callers can iterate over the range's first and + * second elements without copying the underlying range. + * + * @param range The pair-like range to adapt; referenced (not copied) by the adaptor. + */ + explicit range_adaptor(const T& range) + : m_range(range) + { + } + /** + * @brief Accesses the beginning element of the adapted range. + * + * @return const typename T::first_type& Const reference to the range's first element. + */ +const typename T::first_type& begin() { return m_range.first; } + /** + * @brief Access the second element of the wrapped range. + * + * @return const typename T::second_type& The second element of the underlying pair (typically the end iterator or upper bound of the range). + */ +const typename T::second_type& end() { return m_range.second; } + + private: T m_range; }; - // print cluster information - void print_cluster_information( TrkrDefs::cluskey ckey, TrkrCluster* cluster ) + /** + * @brief Prints basic identifying information about an MVTX cluster to standard output. + * + * When `cluster` is non-null, prints the cluster key, local (x,y) position, size, + * layer, stave id, chip id, strobe id, and cluster index. When `cluster` is null, + * prints the cluster key along with layer, stave id, chip id, strobe id, and index. + * + * @param ckey The TrkrDefs::cluskey identifying the cluster. + * @param cluster Pointer to the TrkrCluster; may be null to indicate only key-derived fields should be printed. + */ + void print_cluster_information(TrkrDefs::cluskey ckey, TrkrCluster* cluster) { - if( cluster ) + if (cluster) { std::cout << " MVTX cluster: " << ckey - << " position: (" << cluster->getLocalX() << ", " << cluster->getLocalY() << ")" - << " size: " << (int)cluster->getSize() - << " layer: " << (int)TrkrDefs::getLayer(ckey) - << " stave: " << (int) MvtxDefs::getStaveId(ckey) - << " chip: " << (int)MvtxDefs::getChipId(ckey) - << " strobe: " << (int)MvtxDefs::getStrobeId(ckey) - << " index: " << (int)TrkrDefs::getClusIndex(ckey) - << std::endl; - } else { + << " position: (" << cluster->getLocalX() << ", " << cluster->getLocalY() << ")" + << " size: " << (int) cluster->getSize() + << " layer: " << (int) TrkrDefs::getLayer(ckey) + << " stave: " << (int) MvtxDefs::getStaveId(ckey) + << " chip: " << (int) MvtxDefs::getChipId(ckey) + << " strobe: " << MvtxDefs::getStrobeId(ckey) + << " index: " << (int) TrkrDefs::getClusIndex(ckey) + << std::endl; + } + else + { std::cout << " MVTX cluster: " << ckey - << " layer: " << (int)TrkrDefs::getLayer(ckey) - << " stave: " << (int) MvtxDefs::getStaveId(ckey) - << " chip: " << (int)MvtxDefs::getChipId(ckey) - << " strobe: " << (int)MvtxDefs::getStrobeId(ckey) - << " index: " << (int)TrkrDefs::getClusIndex(ckey) - << std::endl; + << " layer: " << (int) TrkrDefs::getLayer(ckey) + << " stave: " << (int) MvtxDefs::getStaveId(ckey) + << " chip: " << (int) MvtxDefs::getChipId(ckey) + << " strobe: " << MvtxDefs::getStrobeId(ckey) + << " index: " << (int) TrkrDefs::getClusIndex(ckey) + << std::endl; } } using hitkeyset_t = std::set; - using clustermap_t = std::map; + using clustermap_t = std::map; -} +} // namespace -//_____________________________________________________________________________ -MvtxClusterPruner::MvtxClusterPruner(const std::string &name) +/** + * @brief Constructs an MvtxClusterPruner SubsysReco module. + * + * @param name Name assigned to this module instance and forwarded to SubsysReco. + */ +MvtxClusterPruner::MvtxClusterPruner(const std::string& name) : SubsysReco(name) { } -//_____________________________________________________________________________ -int MvtxClusterPruner::InitRun(PHCompositeNode * /*topNode*/) +/** + * @brief Initialize per-run state and report the configured matching mode. + * + * Prints the current value of m_use_strict_matching to standard output. + * + * @return int Fun4All return code: `Fun4AllReturnCodes::EVENT_OK`. + */ +int MvtxClusterPruner::InitRun(PHCompositeNode* /*topNode*/) { std::cout << "MvtxClusterPruner::InitRun - m_use_strict_matching: " << m_use_strict_matching << std::endl; return Fun4AllReturnCodes::EVENT_OK; } -//_____________________________________________________________________________ -int MvtxClusterPruner::process_event(PHCompositeNode *topNode) +/** + * @brief Prunes MVTX clusters by comparing clusters across consecutive strobe steps. + * + * Compares clusters in each MVTX hitset with clusters from the next strobe and removes + * redundant clusters according to the configured matching mode: exact match (strict) + * or subset-based (removes the cluster whose hit set is a subset of the other). + * + * @param topNode Root node of the event node tree used to access cluster containers. + * @return int Fun4AllReturnCodes::EVENT_OK on success. + */ +int MvtxClusterPruner::process_event(PHCompositeNode* topNode) { // load relevant nodes - auto trkrclusters = findNode::getClass(topNode, "TRKR_CLUSTER"); - if( !trkrclusters ) + auto* trkrclusters = findNode::getClass(topNode, "TRKR_CLUSTER"); + if (!trkrclusters) { std::cout << "MvtxClusterPruner::process_event - TRKR_CLUSTER not found. Doing nothing" << std::endl; return Fun4AllReturnCodes::EVENT_OK; } - auto clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); - if( !clusterhitassoc ) + auto* clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); + if (!clusterhitassoc) { std::cout << "MvtxClusterPruner::process_event - TRKR_CLUSTERHITASSOC not found. Doing nothing" << std::endl; return Fun4AllReturnCodes::EVENT_OK; } // lambda method to create map of cluster keys and associated hits - auto get_cluster_map = [trkrclusters,clusterhitassoc]( TrkrDefs::hitsetkey key ) + auto get_cluster_map = [trkrclusters, clusterhitassoc](TrkrDefs::hitsetkey key) { clustermap_t out; // get all clusters for this hitsetkey - const auto cluster_range= trkrclusters->getClusters(key); - for( const auto& [ckey,cluster]:range_adaptor(cluster_range) ) + const auto cluster_range = trkrclusters->getClusters(key); + for (const auto& [ckey, cluster] : range_adaptor(cluster_range)) { // get associated hits const auto& hit_range = clusterhitassoc->getHits(ckey); hitkeyset_t hitkeys; - std::transform(hit_range.first, hit_range.second, std::inserter(hitkeys,hitkeys.end()), - [](const TrkrClusterHitAssoc::Map::value_type& pair ){ return pair.second; }); - out.emplace(ckey,std::move(hitkeys)); + std::transform(hit_range.first, hit_range.second, std::inserter(hitkeys, hitkeys.end()), + [](const TrkrClusterHitAssoc::Map::value_type& pair) + { return pair.second; }); + out.emplace(ckey, std::move(hitkeys)); } return out; }; // loop over MVTX hitset keys const auto hitsetkeys = trkrclusters->getHitSetKeys(TrkrDefs::mvtxId); - for( const auto& hitsetkey:hitsetkeys ) + for (const auto& hitsetkey : hitsetkeys) { // get layer, stave, chip and current strobe const auto layer = TrkrDefs::getLayer(hitsetkey); @@ -125,111 +179,116 @@ int MvtxClusterPruner::process_event(PHCompositeNode *topNode) const auto cluster_map1 = get_cluster_map(hitsetkey); // get clusters for the next strobe - int next_strobe = current_strobe+1; + int next_strobe = current_strobe + 1; const auto hitsetkey_next_strobe = MvtxDefs::genHitSetKey(layer, stave, chip, next_strobe); const auto clusterk_map2 = get_cluster_map(hitsetkey_next_strobe); // loop over clusters from first range - for( auto [ckey1,hitkeys1]:cluster_map1) + for (auto [ckey1, hitkeys1] : cluster_map1) { // increment counter ++m_cluster_counter_total; // get correcponding cluser - auto cluster1 = Verbosity() ? trkrclusters->findCluster(ckey1):nullptr; + auto* cluster1 = Verbosity() ? trkrclusters->findCluster(ckey1) : nullptr; // loop over clusters from second range - for( auto [ckey2,hitkeys2]:clusterk_map2) + for (auto [ckey2, hitkeys2] : clusterk_map2) { - auto cluster2 = Verbosity() ? trkrclusters->findCluster(ckey2):nullptr; + auto* cluster2 = Verbosity() ? trkrclusters->findCluster(ckey2) : nullptr; - if( m_use_strict_matching ) + if (m_use_strict_matching) { // see if hitsets are identical - if(hitkeys1 == hitkeys2) + if (hitkeys1 == hitkeys2) { // increment counter ++m_cluster_counter_deleted; - if( Verbosity() ) + if (Verbosity()) { std::cout << "Removing cluster "; - print_cluster_information( ckey2, cluster2); + print_cluster_information(ckey2, cluster2); std::cout << "Keeping cluster "; - print_cluster_information( ckey1, cluster1); + print_cluster_information(ckey1, cluster1); } // always remove second cluster trkrclusters->removeCluster(ckey2); break; } - - } else { - + } + else + { // make sure first set is larger than second const bool swapped = hitkeys2.size() > hitkeys1.size(); - if( swapped ) { std::swap(hitkeys2,hitkeys1); } + if (swapped) + { + std::swap(hitkeys2, hitkeys1); + } // see if hitkeys2 is a subset of hitkeys1 - if( std::includes(hitkeys1.begin(), hitkeys1.end(), hitkeys2.begin(), hitkeys2.end()) ) + if (std::includes(hitkeys1.begin(), hitkeys1.end(), hitkeys2.begin(), hitkeys2.end())) { // increment counter ++m_cluster_counter_deleted; - if( swapped ) + if (swapped) { - - if( Verbosity() ) + if (Verbosity()) { std::cout << "Removing cluster "; - print_cluster_information( ckey1, cluster1); + print_cluster_information(ckey1, cluster1); std::cout << "Keeping cluster "; - print_cluster_information( ckey2, cluster2); + print_cluster_information(ckey2, cluster2); } // remove first cluster trkrclusters->removeCluster(ckey1); break; - } else { - - if( Verbosity() ) - { - std::cout << "Removing cluster "; - print_cluster_information( ckey2, cluster2); - - std::cout << "Keeping cluster "; - print_cluster_information( ckey1, cluster1); - } + } + if (Verbosity()) + { + std::cout << "Removing cluster "; + print_cluster_information(ckey2, cluster2); - // remove second cluster - trkrclusters->removeCluster(ckey2); + std::cout << "Keeping cluster "; + print_cluster_information(ckey1, cluster1); } + + // remove second cluster + trkrclusters->removeCluster(ckey2); } - } // strict matching + } // strict matching - } // second cluster loop - } // first cluster loop - } // hitsetkey loop + } // second cluster loop + } // first cluster loop + } // hitsetkey loop return Fun4AllReturnCodes::EVENT_OK; - } -//_____________________________________________________________________________ -int MvtxClusterPruner::End(PHCompositeNode * /*topNode*/) +/** + * @brief Print final cluster pruning statistics. + * + * Outputs the total number of clusters processed, the number of clusters deleted, + * and the deleted fraction to standard output. + * + * @return Fun4AllReturnCodes::EVENT_OK + */ +int MvtxClusterPruner::End(PHCompositeNode* /*topNode*/) { - std::cout << "MvtxClusterPruner::End -" - << " m_cluster_counter_total: " << m_cluster_counter_total - << std::endl; - std::cout << "MvtxClusterPruner::End -" - << " m_cluster_counter_deleted: " << m_cluster_counter_deleted - << " fraction: " << double( m_cluster_counter_deleted )/m_cluster_counter_total - << std::endl; + << " m_cluster_counter_total: " << m_cluster_counter_total + << std::endl; + std::cout << "MvtxClusterPruner::End -" + << " m_cluster_counter_deleted: " << m_cluster_counter_deleted + << " fraction: " << double(m_cluster_counter_deleted) / m_cluster_counter_total + << std::endl; return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/mvtx/MvtxClusterizer.cc b/offline/packages/mvtx/MvtxClusterizer.cc index 7970b369eb..9423014411 100644 --- a/offline/packages/mvtx/MvtxClusterizer.cc +++ b/offline/packages/mvtx/MvtxClusterizer.cc @@ -63,80 +63,107 @@ namespace /// convenience square method template - inline constexpr T square(const T &x) + /** + * @brief Computes the square of a value. + * + * @tparam T Numeric or multiplicative type that supports operator*. + * @param x Value to be squared. + * @return T The product of `x` with itself. + */ + constexpr T square(const T &x) { return x * x; } -} // namespace +} /** + * @brief Determines whether two MVTX hits are adjacent for clustering. + * + * When Z-dimension clustering is enabled, hits are adjacent if their column indices differ by + * at most 1 and their row indices differ by at most 1. When Z-dimension clustering is disabled, + * hits are adjacent only if they are in the same column and their row indices differ by at most 1. + * + * @param lhs Pair of hit key and TrkrHit for the first hit. + * @param rhs Pair of hit key and TrkrHit for the second hit. + * @return true if the hits are adjacent according to the above rules, false otherwise. + */ bool MvtxClusterizer::are_adjacent( const std::pair &lhs, - const std::pair &rhs) + const std::pair &rhs) const { if (GetZClustering()) { return - // column adjacent - ( (MvtxDefs::getCol(lhs.first) > MvtxDefs::getCol(rhs.first)) ? - MvtxDefs::getCol(lhs.first)<=MvtxDefs::getCol(rhs.first)+1: - MvtxDefs::getCol(rhs.first)<=MvtxDefs::getCol(lhs.first)+1) && - - // row adjacent - ( (MvtxDefs::getRow(lhs.first) > MvtxDefs::getRow(rhs.first)) ? - MvtxDefs::getRow(lhs.first)<=MvtxDefs::getRow(rhs.first)+1: - MvtxDefs::getRow(rhs.first)<=MvtxDefs::getRow(lhs.first)+1); - - } else { + // column adjacent + ((MvtxDefs::getCol(lhs.first) > MvtxDefs::getCol(rhs.first)) ? MvtxDefs::getCol(lhs.first) <= MvtxDefs::getCol(rhs.first) + 1 : MvtxDefs::getCol(rhs.first) <= MvtxDefs::getCol(lhs.first) + 1) && - return + // row adjacent + ((MvtxDefs::getRow(lhs.first) > MvtxDefs::getRow(rhs.first)) ? MvtxDefs::getRow(lhs.first) <= MvtxDefs::getRow(rhs.first) + 1 : MvtxDefs::getRow(rhs.first) <= MvtxDefs::getRow(lhs.first) + 1); + } + return // column identical - MvtxDefs::getCol(rhs.first)==MvtxDefs::getCol(lhs.first) && + MvtxDefs::getCol(rhs.first) == MvtxDefs::getCol(lhs.first) && // row adjacent - ( (MvtxDefs::getRow(lhs.first) > MvtxDefs::getRow(rhs.first)) ? - MvtxDefs::getRow(lhs.first)<=MvtxDefs::getRow(rhs.first)+1: - MvtxDefs::getRow(rhs.first)<=MvtxDefs::getRow(lhs.first)+1); - - } + ((MvtxDefs::getRow(lhs.first) > MvtxDefs::getRow(rhs.first)) ? MvtxDefs::getRow(lhs.first) <= MvtxDefs::getRow(rhs.first) + 1 : MvtxDefs::getRow(rhs.first) <= MvtxDefs::getRow(lhs.first) + 1); } -bool MvtxClusterizer::are_adjacent(RawHit *lhs, RawHit *rhs) +/** + * Determine whether two MVTX raw hits are adjacent in sensor coordinates. + * + * When Z-clustering is enabled, hits are adjacent if their phi bins differ by at most 1 + * and their time bins differ by at most 1. When Z-clustering is disabled, hits are adjacent + * only if they share the same phi bin and their time bins differ by at most 1. + * + * @param lhs Left-hand-side raw hit (provides phiBin and tBin). + * @param rhs Right-hand-side raw hit (provides phiBin and tBin). + * @return `true` if the two hits are adjacent according to the active Z-clustering mode, `false` otherwise. + */ +bool MvtxClusterizer::are_adjacent(RawHit *lhs, RawHit *rhs) const { if (GetZClustering()) { return - // phi adjacent (== column) - ((lhs->getPhiBin() > rhs->getPhiBin()) ? - lhs->getPhiBin() <= rhs->getPhiBin()+1: - rhs->getPhiBin() <= lhs->getPhiBin()+1) && - - // time adjacent (== row) - ((lhs->getTBin() > rhs->getTBin()) ? - lhs->getTBin() <= rhs->getTBin()+1: - rhs->getTBin() <= lhs->getTBin()+1); + // phi adjacent (== column) + ((lhs->getPhiBin() > rhs->getPhiBin()) ? lhs->getPhiBin() <= rhs->getPhiBin() + 1 : rhs->getPhiBin() <= lhs->getPhiBin() + 1) && - } else { - - return + // time adjacent (== row) + ((lhs->getTBin() > rhs->getTBin()) ? lhs->getTBin() <= rhs->getTBin() + 1 : rhs->getTBin() <= lhs->getTBin() + 1); + } + return // phi identical (== column) lhs->getPhiBin() == rhs->getPhiBin() && // time adjacent (== row) - ((lhs->getTBin() > rhs->getTBin()) ? - lhs->getTBin() <= rhs->getTBin()+1: - rhs->getTBin() <= lhs->getTBin()+1); - - } + ((lhs->getTBin() > rhs->getTBin()) ? lhs->getTBin() <= rhs->getTBin() + 1 : rhs->getTBin() <= lhs->getTBin() + 1); } +/** + * @brief Construct an MvtxClusterizer module with the specified module name. + * + * Initializes the SubsysReco base class with the provided name used to identify + * this reconstruction module instance. + * + * @param name Module name used by the SubsysReco base class. + */ MvtxClusterizer::MvtxClusterizer(const std::string &name) : SubsysReco(name) { } +/** + * @brief Prepare DST node structure and ensure cluster-related nodes exist. + * + * Ensures the DST/TRKR node hierarchy contains a TrkrClusterContainer ("TRKR_CLUSTER") + * and a TrkrClusterHitAssoc ("TRKR_CLUSTERHITASSOC"); when enabled, also ensures a + * ClusHitsVerbose node ("Trkr_SvtxClusHitsVerbose") is present. Creates missing nodes + * under DST/TRKR as needed. + * + * @param topNode Top-level PHCompositeNode of the current run/event node tree. + * @return int Fun4AllReturnCodes::EVENT_OK on success; Fun4AllReturnCodes::ABORTRUN if the DST node is missing. + */ int MvtxClusterizer::InitRun(PHCompositeNode *topNode) { //----------------- @@ -165,7 +192,7 @@ int MvtxClusterizer::InitRun(PHCompositeNode *topNode) } // Create the Cluster node if required - auto trkrclusters = + auto *trkrclusters = findNode::getClass(dstNode, "TRKR_CLUSTER"); if (!trkrclusters) { @@ -183,7 +210,7 @@ int MvtxClusterizer::InitRun(PHCompositeNode *topNode) DetNode->addNode(TrkrClusterContainerNode); } - auto clusterhitassoc = + auto *clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); if (!clusterhitassoc) { @@ -208,14 +235,14 @@ int MvtxClusterizer::InitRun(PHCompositeNode *topNode) if (!mClusHitsVerbose) { PHNodeIterator dstiter(dstNode); - auto DetNode = dynamic_cast(dstiter.findFirst("PHCompositeNode", "TRKR")); + auto *DetNode = dynamic_cast(dstiter.findFirst("PHCompositeNode", "TRKR")); if (!DetNode) { DetNode = new PHCompositeNode("TRKR"); dstNode->addNode(DetNode); } mClusHitsVerbose = new ClusHitsVerbosev1(); - auto newNode = new PHIODataNode(mClusHitsVerbose, "Trkr_SvtxClusHitsVerbose", "PHObject"); + auto *newNode = new PHIODataNode(mClusHitsVerbose, "Trkr_SvtxClusHitsVerbose", "PHObject"); DetNode->addNode(newNode); } } @@ -227,18 +254,29 @@ int MvtxClusterizer::InitRun(PHCompositeNode *topNode) if (Verbosity() > 0) { std::cout << "====================== MvtxClusterizer::InitRun() " - "=====================" - << std::endl; + "=====================" + << std::endl; std::cout << " Z-dimension Clustering = " << std::boolalpha << m_makeZClustering - << std::noboolalpha << std::endl; + << std::noboolalpha << std::endl; std::cout << "==================================================================" - "=========" - << std::endl; + "=========" + << std::endl; } return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Process a single event: read MVTX hits, perform clustering, and store clusters and associations. + * + * Retrieves MVTX hit inputs (digitized or raw), validates required output nodes, clears existing MVTX + * clusters and associations for this event, runs the appropriate clustering routine, and prints a + * cluster summary. + * + * @param topNode Top-level node of the current event node tree. + * @return int `Fun4AllReturnCodes::EVENT_OK` on success; `Fun4AllReturnCodes::ABORTRUN` if required input or + * output nodes are missing. + */ int MvtxClusterizer::process_event(PHCompositeNode *topNode) { // get node containing the digitized hits @@ -283,7 +321,7 @@ int MvtxClusterizer::process_event(PHCompositeNode *topNode) // reset MVTX clusters and cluster associations const auto hitsetkeys = m_clusterlist->getHitSetKeys(TrkrDefs::mvtxId); - for( const auto& hitsetkey:hitsetkeys) + for (const auto &hitsetkey : hitsetkeys) { m_clusterlist->removeClusters(hitsetkey); m_clusterhitassoc->removeAssocs(hitsetkey); @@ -304,6 +342,23 @@ int MvtxClusterizer::process_event(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Cluster MVTX hits into SVTX-style clusters and record associations. + * + * Processes MVTX hit sets from the event node tree, groups adjacent hits into + * clusters, computes per-cluster local positions, sizes, and measurement + * uncertainties, and stores resulting TrkrClusterv5 objects and their hit + * associations. + * + * The function updates m_clusterlist with new clusters and m_clusterhitassoc + * with cluster-to-hit mappings. When enabled, per-cluster verbose hit + * summaries are written to mClusHitsVerbose. Geometry from the + * CYLINDERGEOM_MVTX node is used to compute local coordinates; missing layer + * geometry triggers a fatal exit. + * + * @param topNode Top-level node of the current event/run node tree used to + * retrieve geometry and other event data. + */ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) { if (Verbosity() > 0) @@ -337,8 +392,8 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) unsigned int chip = MvtxDefs::getChipId(hitsetitr->first); unsigned int strobe = MvtxDefs::getStrobeId(hitsetitr->first); std::cout << "MvtxClusterizer found hitsetkey " << hitsetitr->first - << " layer " << layer << " stave " << stave << " chip " << chip - << " strobe " << strobe << std::endl; + << " layer " << layer << " stave " << stave << " chip " << chip + << " strobe " << strobe << std::endl; } if (Verbosity() > 2) @@ -394,7 +449,7 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) std::vector component(num_vertices(G)); // this is the actual clustering, performed by boost - boost::connected_components(G, &component[0]); + boost::connected_components(G, component.data()); // Loop over the components(hits) compiling a list of the // unique connected groups (ie. clusters). @@ -405,7 +460,7 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) cluster_ids.insert(component[i]); clusters.insert(make_pair(component[i], hitvec[i])); } - for (const auto& clusid:cluster_ids) + for (const auto &clusid : cluster_ids) { auto clusrange = clusters.equal_range(clusid); auto ckey = TrkrDefs::genClusKey(hitset->getHitSetKey(), clusid); @@ -413,7 +468,8 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) // determine the size of the cluster in phi and z std::set phibins; std::set zbins; - std::map m_phi, m_z; // Note, there are no "cut" bins for Svtx Clusters + std::map m_phi; + std::map m_z; // Note, there are no "cut" bins for Svtx Clusters // determine the cluster position... double locxsum = 0.; @@ -426,7 +482,7 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) // we need the geometry object for this layer to get the global positions int layer = TrkrDefs::getLayer(ckey); - auto layergeom = dynamic_cast(geom_container->GetLayerGeom(layer)); + auto *layergeom = dynamic_cast(geom_container->GetLayerGeom(layer)); if (!layergeom) { exit(1); @@ -574,11 +630,11 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) if (Verbosity() > 0) { std::cout << " MvtxClusterizer: cluskey " << ckey << " layer " << layer - << " rad " << layergeom->get_radius() << " phibins " - << phibins.size() << " pitch " << pitch << " phisize " << phisize - << " zbins " << zbins.size() << " length " << length << " zsize " - << zsize << " local x " << locclusx << " local y " << locclusz - << std::endl; + << " rad " << layergeom->get_radius() << " phibins " + << phibins.size() << " pitch " << pitch << " phisize " << phisize + << " zbins " << zbins.size() << " length " << length << " zsize " + << zsize << " local x " << locclusx << " local y " << locclusz + << std::endl; } auto clus = std::make_unique(); @@ -605,7 +661,7 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) } } // clusitr loop - } // loop over hitsets + } // loop over hitsets if (Verbosity() > 1) { @@ -616,6 +672,17 @@ void MvtxClusterizer::ClusterMvtx(PHCompositeNode *topNode) return; } +/** + * @brief Cluster MVTX raw hits into TrkrClusterv5 objects and store them. + * + * Iterates MVTX raw hit sets, groups adjacent raw hits into clusters, computes + * each cluster's local position, size, and position uncertainties using the + * MVTX geometry, and adds resulting TrkrClusterv5 objects to the configured + * cluster container with generated cluster keys. + * + * @param topNode Top-level node of the framework node tree used to locate the + * MVTX geometry and other required nodes. + */ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) { if (Verbosity() > 0) @@ -650,8 +717,8 @@ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) unsigned int chip = MvtxDefs::getChipId(hitsetitr->first); unsigned int strobe = MvtxDefs::getStrobeId(hitsetitr->first); std::cout << "MvtxClusterizer found hitsetkey " << hitsetitr->first - << " layer " << layer << " stave " << stave << " chip " << chip - << " strobe " << strobe << std::endl; + << " layer " << layer << " stave " << stave << " chip " << chip + << " strobe " << strobe << std::endl; } if (Verbosity() > 2) @@ -695,7 +762,7 @@ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) std::vector component(num_vertices(G)); // this is the actual clustering, performed by boost - boost::connected_components(G, &component[0]); + boost::connected_components(G, component.data()); // Loop over the components(hits) compiling a list of the // unique connected groups (ie. clusters). @@ -709,7 +776,7 @@ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) } // std::cout << "found cluster #: "<< clusters.size()<< std::endl; // loop over the componenets and make clusters - for( const auto& clusid:cluster_ids) + for (const auto &clusid : cluster_ids) { auto clusrange = clusters.equal_range(clusid); @@ -731,7 +798,7 @@ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) // we need the geometry object for this layer to get the global positions int layer = TrkrDefs::getLayer(ckey); - auto layergeom = dynamic_cast( + auto *layergeom = dynamic_cast( geom_container->GetLayerGeom(layer)); if (!layergeom) { @@ -845,11 +912,11 @@ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) if (Verbosity() > 0) { std::cout << " MvtxClusterizer: cluskey " << ckey << " layer " << layer - << " rad " << layergeom->get_radius() << " phibins " - << phibins.size() << " pitch " << pitch << " phisize " << phisize - << " zbins " << zbins.size() << " length " << length << " zsize " - << zsize << " local x " << locclusx << " local y " << locclusz - << std::endl; + << " rad " << layergeom->get_radius() << " phibins " + << phibins.size() << " pitch " << pitch << " phisize " << phisize + << " zbins " << zbins.size() << " length " << length << " zsize " + << zsize << " local x " << locclusx << " local y " << locclusz + << std::endl; } auto clus = std::make_unique(); @@ -875,7 +942,7 @@ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) m_clusterlist->addClusterSpecifyKey(ckey, clus.release()); } } // clusitr loop - } // loop over hitsets + } // loop over hitsets if (Verbosity() > 1) { @@ -886,6 +953,15 @@ void MvtxClusterizer::ClusterMvtxRaw(PHCompositeNode *topNode) return; } +/** + * @brief Print a brief summary of clusters found after processing the event. + * + * Locates the TRKR_CLUSTER container under the provided node and, if present + * and Verbosity() > 0, prints the total number of clusters and a header/footer. + * If Verbosity() > 3 the full cluster container identify() dump is printed. + * + * @param topNode Top-level node of the current processing tree (DST root). + */ void MvtxClusterizer::PrintClusters(PHCompositeNode *topNode) { if (Verbosity() > 0) @@ -898,11 +974,11 @@ void MvtxClusterizer::PrintClusters(PHCompositeNode *topNode) } std::cout << "================= After MvtxClusterizer::process_event() " - "====================" - << std::endl; + "====================" + << std::endl; std::cout << " There are " << clusterlist->size() - << " clusters recorded: " << std::endl; + << " clusters recorded: " << std::endl; if (Verbosity() > 3) { @@ -910,9 +986,9 @@ void MvtxClusterizer::PrintClusters(PHCompositeNode *topNode) } std::cout << "==================================================================" - "=========" - << std::endl; + "=========" + << std::endl; } return; -} +} \ No newline at end of file diff --git a/offline/packages/mvtx/MvtxHitPruner.cc b/offline/packages/mvtx/MvtxHitPruner.cc index 4f141a4c3f..e74cfd85bb 100644 --- a/offline/packages/mvtx/MvtxHitPruner.cc +++ b/offline/packages/mvtx/MvtxHitPruner.cc @@ -51,26 +51,68 @@ namespace { //! range adaptor to be able to use range-based for loop - template class range_adaptor + template + class range_adaptor { - public: - range_adaptor( const T& range ):m_range(range){} - const typename T::first_type& begin() {return m_range.first;} - const typename T::second_type& end() {return m_range.second;} - private: + public: + /** + * @brief Constructs a range adaptor wrapping the provided range. + * + * @param range Range to be adapted and iterated over by this adaptor. + */ + explicit range_adaptor(const T& range) + : m_range(range) + { + } + /** + * @brief Accesses the first element of the underlying range pair. + * + * @return const typename T::first_type& Reference to the first element (begin) of the stored range pair. + */ +const typename T::first_type& begin() { return m_range.first; } + /** + * @brief Access the end element of the underlying range. + * + * @return const typename T::second_type& Reference to the range's second (end) element; read-only access. + */ +const typename T::second_type& end() { return m_range.second; } + + private: T m_range; }; -} +} /** + * @brief Construct an MvtxHitPruner with the specified module name. + * + * @param name Identifier used to register and reference this SubsysReco module. + */ -MvtxHitPruner::MvtxHitPruner(const std::string &name) +MvtxHitPruner::MvtxHitPruner(const std::string& name) : SubsysReco(name) { } -int MvtxHitPruner::InitRun(PHCompositeNode * /*topNode*/) -{ return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Perform per-run initialization for the MvtxHitPruner. + * + * No per-run setup is performed by this implementation. + * + * @return int Fun4AllReturnCodes::EVENT_OK to indicate successful initialization. + */ +int MvtxHitPruner::InitRun(PHCompositeNode* /*topNode*/) +{ + return Fun4AllReturnCodes::EVENT_OK; +} -int MvtxHitPruner::process_event(PHCompositeNode *topNode) +/** + * @brief Consolidates MVTX hitsets across strobe values into the strobe-0 hitset and removes the non-zero strobe hitsets. + * + * For each MVTX hitset with a non-zero strobe, copies any hits not already present into the corresponding + * strobe-0 (bare) hitset and then deletes the non-zero-strobe hitset. Duplicate hits (same hit key) are not copied. + * + * @param topNode Root of the node tree from which the "TRKR_HITSET" container is retrieved. + * @return int `EVENT_OK` on success, `ABORTRUN` if the "TRKR_HITSET" node cannot be found. + */ +int MvtxHitPruner::process_event(PHCompositeNode* topNode) { // get node containing the digitized hits m_hits = findNode::getClass(topNode, "TRKR_HITSET"); @@ -93,12 +135,14 @@ int MvtxHitPruner::process_event(PHCompositeNode *topNode) std::set bare_hitset_set; const auto hitsetrange = m_hits->getHitSets(TrkrDefs::TrkrId::mvtxId); - for( const auto& [hitsetkey,hitset]:range_adaptor(hitsetrange) ) + for (const auto& [hitsetkey, hitset] : range_adaptor(hitsetrange)) { - // get strobe, skip if already zero const int strobe = MvtxDefs::getStrobeId(hitsetkey); - if( strobe == 0 ) continue; + if (strobe == 0) + { + continue; + } // get the hitsetkey value for strobe 0 const auto bare_hitsetkey = MvtxDefs::resetStrobe(hitsetkey); @@ -117,43 +161,46 @@ int MvtxHitPruner::process_event(PHCompositeNode *topNode) for (const auto& bare_hitsetkey : bare_hitset_set) { // find matching hitset of creater - auto bare_hitset = (m_hits->findOrAddHitSet(bare_hitsetkey))->second; + auto* bare_hitset = (m_hits->findOrAddHitSet(bare_hitsetkey))->second; if (Verbosity()) { std::cout - << "MvtxHitPruner::process_event - bare_hitset " << bare_hitsetkey - << " initially has " << bare_hitset->size() << " hits " - << std::endl; + << "MvtxHitPruner::process_event - bare_hitset " << bare_hitsetkey + << " initially has " << bare_hitset->size() << " hits " + << std::endl; } // get all hitsets with non-zero strobe that match the bare hitset key auto bare_hitsetrange = hitset_multimap.equal_range(bare_hitsetkey); - for( const auto& [unused,hitsetkey]:range_adaptor(bare_hitsetrange) ) + for (const auto& [unused, hitsetkey] : range_adaptor(bare_hitsetrange)) { const int strobe = MvtxDefs::getStrobeId(hitsetkey); - if( strobe == 0 ) continue; + if (strobe == 0) + { + continue; + } if (Verbosity()) { std::cout << "MvtxHitPruner::process_event -" - << " process hitsetkey " << hitsetkey - << " from strobe " << strobe - << " for bare_hitsetkey " << bare_hitsetkey - << std::endl; + << " process hitsetkey " << hitsetkey + << " from strobe " << strobe + << " for bare_hitsetkey " << bare_hitsetkey + << std::endl; } // copy all hits to the hitset with strobe 0 - auto hitset = m_hits->findHitSet(hitsetkey); + auto* hitset = m_hits->findHitSet(hitsetkey); if (Verbosity()) { std::cout << "MvtxHitPruner::process_event - hitsetkey " << hitsetkey - << " has strobe " << strobe << " and has " << hitset->size() - << " hits, so copy it" << std::endl; + << " has strobe " << strobe << " and has " << hitset->size() + << " hits, so copy it" << std::endl; } TrkrHitSet::ConstRange hitrangei = hitset->getHits(); - for( const auto& [hitkey,old_hit]:range_adaptor(hitrangei) ) + for (const auto& [hitkey, old_hit] : range_adaptor(hitrangei)) { if (Verbosity()) { @@ -166,9 +213,9 @@ int MvtxHitPruner::process_event(PHCompositeNode *topNode) if (Verbosity()) { std::cout - << "MvtxHitPruner::process_event - hitkey " << hitkey - << " is already in bare hitsest, do not copy" - << std::endl; + << "MvtxHitPruner::process_event - hitkey " << hitkey + << " is already in bare hitsest, do not copy" + << std::endl; } continue; } @@ -177,11 +224,11 @@ int MvtxHitPruner::process_event(PHCompositeNode *topNode) if (Verbosity()) { std::cout - << "MvtxHitPruner::process_event - copying over hitkey " - << hitkey << std::endl; + << "MvtxHitPruner::process_event - copying over hitkey " + << hitkey << std::endl; } - auto new_hit = new TrkrHitv2; + auto* new_hit = new TrkrHitv2; new_hit->CopyFrom(old_hit); bare_hitset->addHitSpecificKey(hitkey, new_hit); } @@ -193,4 +240,4 @@ int MvtxHitPruner::process_event(PHCompositeNode *topNode) } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/mvtx/SegmentationAlpide.cc b/offline/packages/mvtx/SegmentationAlpide.cc index 6b9aecddd0..b34e849f2b 100644 --- a/offline/packages/mvtx/SegmentationAlpide.cc +++ b/offline/packages/mvtx/SegmentationAlpide.cc @@ -5,16 +5,23 @@ */ #include "SegmentationAlpide.h" -#include - #include +#include +/** + * @brief Print descriptive geometry and size information for the ALPIDE segmentation to standard output. + * + * Prints three lines detailing: + * - Pixel size in microns along rows and columns (with the number of rows/columns). + * - Passive edge dimensions (bottom, top, left/right) in microns. + * - Active and total sizes for rows and columns in centimeters. + */ void SegmentationAlpide::print() { - std::cout << (boost::format("Pixel size: %.2f (along %d rows) %.2f (along %d columns) microns") % (PitchRow * 1e4) % NRows % (PitchCol * 1e4) % NCols).str() + std::cout << std::format("Pixel size: {:.2f} (along {} rows) {:.2f} (along {} columns) microns", (PitchRow * 1e4), NRows, (PitchCol * 1e4), NCols) << std::endl; - std::cout << (boost::format("Passive edges: bottom: %.2f, top: %.2f, left/right: %.2f microns") % (PassiveEdgeReadOut * 1e4) % (PassiveEdgeTop * 1e4) % (PassiveEdgeSide * 1e4)).str() + std::cout << std::format("Passive edges: bottom: {:.2f}, top: {:.2f}, left/right: {:.2f} microns", (PassiveEdgeReadOut * 1e4), (PassiveEdgeTop * 1e4), (PassiveEdgeSide * 1e4)) << std::endl; - std::cout << (boost::format("Active/Total size: %.6f/%.6f (rows) %.6f/%.6f (cols) cm") % ActiveMatrixSizeRows % SensorSizeRows % ActiveMatrixSizeCols % SensorSizeCols).str() + std::cout << std::format("Active/Total size: {:.6f}/{:.6f} (rows) {:.6f}/{:.6f} (cols) cm", ActiveMatrixSizeRows, SensorSizeRows, ActiveMatrixSizeCols, SensorSizeCols) << std::endl; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/LaserClusterizer.cc b/offline/packages/tpc/LaserClusterizer.cc index 170f95944d..7dcab1e144 100644 --- a/offline/packages/tpc/LaserClusterizer.cc +++ b/offline/packages/tpc/LaserClusterizer.cc @@ -31,13 +31,10 @@ #include #include +#include #include #include #include -//#include -//#include -#include -//#include #include #include @@ -46,15 +43,16 @@ #include #include // for sqrt, cos, sin +#include #include #include #include // for _Rb_tree_cons... +#include +#include #include +#include #include // for pair #include -#include -#include -#include #include @@ -68,12 +66,9 @@ using adcKey = std::pair; using pointKeyLaser = std::pair; using hitData = std::pair; - -int layerMins[3] = {7,23,39}; +int layerMins[3] = {7, 23, 39}; int layerMaxes[3] = {22, 38, 54}; - - namespace { struct thread_data @@ -101,28 +96,35 @@ namespace pthread_mutex_t mythreadlock; const std::vector neighborOffsets = { - point(1, 0, 0), point(-1, 0, 0), - point(0, 1, 0), point(0, -1, 0), - point(0, 0, 1), point(0, 0, -1), - point(0, 0, 2), point(0, 0, -2) - }; - - - double layerFunction(double *x, double *par) + point(1, 0, 0), point(-1, 0, 0), + point(0, 1, 0), point(0, -1, 0), + point(0, 0, 1), point(0, 0, -1), + point(0, 0, 2), point(0, 0, -2)}; + + /** + * @brief Computes the contribution of a layer model for a discrete bin. + * + * Calculates the overlap between a unit-width bin centered at round(x[0]) and the interval + * [mu - 0.5, mu + 0.5], then returns A multiplied by that overlap. Returns 0 if there is no overlap. + * + * @param x Input array where x[0] is the (possibly non-integer) layer index to be binned. + * @param par Parameter array where par[0] = A (amplitude) and par[1] = mu (layer center). + * @return double A times the overlap length between the binned layer and the layer interval, or 0.0 if none. + */ + double layerFunction(double *x, const double *par) { double A = par[0]; double mu = par[1]; - double binCenter = round(x[0]); double overlapLow = std::max(binCenter - 0.5, mu - 0.5); double overlapHigh = std::min(binCenter + 0.5, mu + 0.5); double overlap = overlapHigh - overlapLow; - if(overlap <= 0.0) + if (overlap <= 0.0) { return 0.0; } - return A*overlap; + return A * overlap; /* if(fabs(x[0] - mu) < 1) { @@ -131,50 +133,84 @@ namespace } return 0.0; */ - - } + /** + * @brief Evaluates a scaled Gaussian for the phi coordinate. + * + * Computes par[0] multiplied by a Gaussian evaluated at x[0] with mean par[1] and width par[2]. + * + * @param x Pointer to the independent variable array; x[0] is the value at which to evaluate the Gaussian. + * @param par Parameter array where par[0] is the amplitude scale, par[1] is the Gaussian mean, and par[2] is the Gaussian sigma. + * @return double The value par[0] * Gaussian(x[0]; mean=par[1], sigma=par[2]); returns 0.0 if par[2] < 0.0. + */ double phiFunction(double *x, double *par) { - if(par[2] < 0.0) + if (par[2] < 0.0) { return 0.0; } - return par[0] * TMath::Gaus(x[0],par[1],par[2],false); + return par[0] * TMath::Gaus(x[0], par[1], par[2], false); } + /** + * @brief Models the time-dependent amplitude as a Gaussian modulated by a CDF-like term. + * + * Evaluates a Gaussian centered at par[1] with width par[2], multiplies it by + * (1 + erfc(par[3]*(x[0]-par[1])/(sqrt(2)*par[2]))) to introduce an asymmetric tail, + * and scales the result by par[0]. + * + * @param x Input array where x[0] is the time coordinate to evaluate. + * @param par Parameter array interpreted as: + * - par[0]: overall amplitude scale, + * - par[1]: Gaussian mean, + * - par[2]: Gaussian standard deviation (if < 0 the function returns 0), + * - par[3]: coefficient controlling the CDF-like asymmetry. + * @return double Computed value: par[0] * Gaussian(x[0]; par[1], par[2]) * (1 + erfc(par[3]*(x[0]-par[1])/(sqrt(2)*par[2]))); returns 0 if par[2] < 0. + */ double timeFunction(double *x, double *par) { - if(par[2] < 0.0) + if (par[2] < 0.0) { return 0.0; } - double g = TMath::Gaus(x[0],par[1],par[2],true); - double cdf = 1 + TMath::Erfc(par[3]*(x[0]-par[1])/(sqrt(2.0)*par[2])); - return par[0]*g*cdf; + double g = TMath::Gaus(x[0], par[1], par[2], true); + double cdf = 1 + TMath::Erfc(par[3] * (x[0] - par[1]) / (sqrt(2.0) * par[2])); + return par[0] * g * cdf; } + /** + * @brief Reduce a set of candidate hits to the connected region seeded by a specified hit. + * + * Performs a 3D adjacency flood-fill (using the module's neighborOffsets) to find contiguous hits + * connected to the seed hit identified by maxKey, then replaces clusHits with the hits from that + * connected region. If the seed key is not found in the input, the first hit is used as the seed. + * + * @param clusHits Input vector of hitData candidates; on return it contains only the connected region + * (one contiguous cluster) grown from the seed. + * @param maxKey Pair of (hitkey, hitsetkey) used to identify the seed hit within clusHits. + */ void findConnectedRegions3(std::vector &clusHits, std::pair &maxKey) { std::vector> regions; std::vector unvisited; - for(auto &clusHit : clusHits) + unvisited.reserve(clusHits.size()); + for (auto &clusHit : clusHits) { unvisited.push_back(clusHit); } - while(!unvisited.empty()) + while (!unvisited.empty()) { std::vector region; std::queue q; unsigned int mIndex = 0; - int i=0; - for(auto hit : unvisited) + int i = 0; + for (auto hit : unvisited) { - if(hit.second.second.first == maxKey.first && hit.second.second.second == maxKey.second) + if (hit.second.second.first == maxKey.first && hit.second.second.second == maxKey.second) { mIndex = i; break; @@ -183,11 +219,11 @@ namespace } auto seed = unvisited[mIndex]; - unvisited.erase(unvisited.begin()+mIndex); + unvisited.erase(unvisited.begin() + mIndex); q.push(seed); region.push_back(seed); - while(!q.empty()) + while (!q.empty()) { float ix = q.front().first.get<0>(); float iy = q.front().first.get<1>(); @@ -200,13 +236,12 @@ namespace float ny = iy + neigh.get<1>(); float nz = iz + neigh.get<2>(); - for(unsigned int v=0; v() - nx) < 0.01 && fabs(unvisited[v].first.get<1>() - ny) < 0.01 && fabs(unvisited[v].first.get<2>() - nz) < 0.01) + if (fabs(unvisited[v].first.get<0>() - nx) < 0.01 && fabs(unvisited[v].first.get<1>() - ny) < 0.01 && fabs(unvisited[v].first.get<2>() - nz) < 0.01) { auto newSeed = unvisited[v]; - unvisited.erase(unvisited.begin()+v); + unvisited.erase(unvisited.begin() + v); q.push(newSeed); region.push_back(newSeed); break; @@ -215,18 +250,25 @@ namespace } } regions.push_back(region); - } clusHits.clear(); - for(auto hit : regions[0]) + for (auto hit : regions[0]) { clusHits.push_back(hit); } - } - + /** + * @brief Removes a set of hits from the spatial index and corresponding ADC map entries. + * + * For each entry in clusHits, removes that hit from the R-tree and erases the first + * matching entry in adcMap whose stored spechitkey equals the hit's spechitkey. + * + * @param clusHits Vector of hit entries to remove; each element's spechitkey is used to find ADC map matches. + * @param rtree R-tree spatial index from which the hits in clusHits will be removed. + * @param adcMap Multimap of ADC entries; the first entry with a matching spechitkey is erased for each removed hit. + */ void remove_hits(std::vector &clusHits, bgi::rtree> &rtree, std::multimap &adcMap) { for (auto &clusHit : clusHits) @@ -237,51 +279,69 @@ namespace for (auto iterAdc = adcMap.begin(); iterAdc != adcMap.end();) { - if(iterAdc->second.second == spechitkey) - { - iterAdc = adcMap.erase(iterAdc); - break; - } - else - { - ++iterAdc; - } + if (iterAdc->second.second == spechitkey) + { + iterAdc = adcMap.erase(iterAdc); + break; + } + + ++iterAdc; } } - } + /** + * @brief Build and record a laser cluster from a set of candidate hits. + * + * Processes the provided hit list (first reducing it to the connected region + * around the supplied seed), computes centroid and weighted moments, optionally + * performs a 3D likelihood fit to refine layer/phi/time parameters, and + * constructs a LaserClusterv2 which is appended to the per-thread cluster + * storage in my_data. + * + * The function allocates a LaserClusterv2, may allocate a temporary TH3D and + * a ROOT::Fit::Fitter for fitting, and always pushes the created cluster and + * its generated cluster key into my_data.cluster_vector and + * my_data.cluster_key_vector respectively. + * + * @param clusHits Vector of candidate hits; will be reduced to the connected + * region around maxADCKey before cluster calculation. + * Each entry is expected to provide (layer, iphi, it) coords, + * an ADC value, and the original hit keys. + * @param my_data Per-thread processing context and outputs. Used for geometry, + * thresholds, fitting flag, and to receive the created cluster + * and cluster key via my_data.cluster_vector and + * my_data.cluster_key_vector. + * @param maxADCKey Pair of (hitkey, hitsetkey) identifying the seed hit with + * maximum ADC used to define the connected region. + */ void calc_cluster_parameter(std::vector &clusHits, thread_data &my_data, std::pair maxADCKey) { - - - findConnectedRegions3(clusHits, maxADCKey); - double rSum = 0.0; double phiSum = 0.0; double tSum = 0.0; - + double layerSum = 0.0; double iphiSum = 0.0; double itSum = 0.0; - + double adcSum = 0.0; - + double maxAdc = 0.0; TrkrDefs::hitsetkey maxKey = 0; - + unsigned int nHits = clusHits.size(); - + auto *clus = new LaserClusterv2; - + int meanSide = 0; - + std::vector usedLayer; std::vector usedIPhi; std::vector usedIT; - + double meanLayer = 0.0; double meanIPhi = 0.0; double meanIT = 0.0; @@ -293,113 +353,110 @@ namespace unsigned int adc = clusHit.second.first; int side = TpcDefs::getSide(spechitkey.second); - + if (side) { - meanSide++; + meanSide++; } else { - meanSide--; + meanSide--; } - + PHG4TpcGeom *layergeom = my_data.geom_container->GetLayerCellGeom((int) coords[0]); - + double r = layergeom->get_radius(); double phi = layergeom->get_phi(coords[1], side); double t = layergeom->get_zcenter(fabs(coords[2])); - + double hitzdriftlength = t * my_data.tGeometry->get_drift_velocity(); double hitZ = my_data.tdriftmax * my_data.tGeometry->get_drift_velocity() - hitzdriftlength; - - - bool foundLayer = false; - for (float i : usedLayer) - { - if (coords[0] == i) - { - foundLayer = true; - break; - } - } - - if (!foundLayer) - { - usedLayer.push_back(coords[0]); - } - - bool foundIPhi = false; - for (float i : usedIPhi) - { - if (coords[1] == i) - { - foundIPhi = true; - break; - } - } - - if (!foundIPhi) - { - usedIPhi.push_back(coords[1]); - } - - bool foundIT = false; - for (float i : usedIT) - { - if (coords[2] == i) - { - foundIT = true; - break; - } - } - - if (!foundIT) - { - usedIT.push_back(coords[2]); - } - - clus->addHit(); - clus->setHitLayer(clus->getNhits() - 1, coords[0]); - clus->setHitIPhi(clus->getNhits() - 1, coords[1]); - clus->setHitIT(clus->getNhits() - 1, coords[2]); - clus->setHitX(clus->getNhits() - 1, r * cos(phi)); - clus->setHitY(clus->getNhits() - 1, r * sin(phi)); - clus->setHitZ(clus->getNhits() - 1, hitZ); - clus->setHitAdc(clus->getNhits() - 1, (float) adc); - - rSum += r * adc; - phiSum += phi * adc; - tSum += t * adc; - - layerSum += coords[0] * adc; - iphiSum += coords[1] * adc; - itSum += coords[2] * adc; - - meanLayer += coords[0]; - meanIPhi += coords[1]; - meanIT += coords[2]; - - adcSum += adc; - - if (adc > maxAdc) - { - maxAdc = adc; - maxKey = spechitkey.second; - } - - } - + + bool foundLayer = false; + for (float i : usedLayer) + { + if (coords[0] == i) + { + foundLayer = true; + break; + } + } + + if (!foundLayer) + { + usedLayer.push_back(coords[0]); + } + + bool foundIPhi = false; + for (float i : usedIPhi) + { + if (coords[1] == i) + { + foundIPhi = true; + break; + } + } + + if (!foundIPhi) + { + usedIPhi.push_back(coords[1]); + } + + bool foundIT = false; + for (float i : usedIT) + { + if (coords[2] == i) + { + foundIT = true; + break; + } + } + + if (!foundIT) + { + usedIT.push_back(coords[2]); + } + + clus->addHit(); + clus->setHitLayer(clus->getNhits() - 1, coords[0]); + clus->setHitIPhi(clus->getNhits() - 1, coords[1]); + clus->setHitIT(clus->getNhits() - 1, coords[2]); + clus->setHitX(clus->getNhits() - 1, r * cos(phi)); + clus->setHitY(clus->getNhits() - 1, r * sin(phi)); + clus->setHitZ(clus->getNhits() - 1, hitZ); + clus->setHitAdc(clus->getNhits() - 1, (float) adc); + + rSum += r * adc; + phiSum += phi * adc; + tSum += t * adc; + + layerSum += coords[0] * adc; + iphiSum += coords[1] * adc; + itSum += coords[2] * adc; + + meanLayer += coords[0]; + meanIPhi += coords[1]; + meanIT += coords[2]; + + adcSum += adc; + + if (adc > maxAdc) + { + maxAdc = adc; + maxKey = spechitkey.second; + } + } + if (nHits == 0) { return; } - double clusR = rSum / adcSum; double clusPhi = phiSum / adcSum; double clusT = tSum / adcSum; double zdriftlength = clusT * my_data.tGeometry->get_drift_velocity(); - + double clusX = clusR * cos(clusPhi); double clusY = clusR * sin(clusPhi); double clusZ = my_data.tdriftmax * my_data.tGeometry->get_drift_velocity() - zdriftlength; @@ -408,10 +465,10 @@ namespace clusZ = -clusZ; for (int i = 0; i < (int) clus->getNhits(); i++) { - clus->setHitZ(i, -1 * clus->getHitZ(i)); + clus->setHitZ(i, -1 * clus->getHitZ(i)); } } - + std::sort(usedLayer.begin(), usedLayer.end()); std::sort(usedIPhi.begin(), usedIPhi.end()); std::sort(usedIT.begin(), usedIT.end()); @@ -419,30 +476,28 @@ namespace meanLayer = meanLayer / nHits; meanIPhi = meanIPhi / nHits; meanIT = meanIT / nHits; - + double sigmaLayer = 0.0; double sigmaIPhi = 0.0; double sigmaIT = 0.0; - + double sigmaWeightedLayer = 0.0; double sigmaWeightedIPhi = 0.0; double sigmaWeightedIT = 0.0; - - pthread_mutex_lock(&mythreadlock); - my_data.hitHist = new TH3D(Form("hitHist_event%d_side%d_sector%d_module%d_cluster%d",my_data.eventNum,(int)my_data.side,(int)my_data.sector,(int)my_data.module,(int)my_data.cluster_vector.size()),";layer;iphi;it",usedLayer.size()+2,usedLayer[0]-1.5,*usedLayer.rbegin()+1.5,usedIPhi.size()+2,usedIPhi[0]-1.5,*usedIPhi.rbegin()+1.5,usedIT.size()+2,usedIT[0]-1.5,*usedIT.rbegin()+1.5); + pthread_mutex_lock(&mythreadlock); + my_data.hitHist = new TH3D(std::format("hitHist_event{}_side{}_sector{}_module{}_cluster{}", my_data.eventNum, (int) my_data.side, (int) my_data.sector, (int) my_data.module, (int) my_data.cluster_vector.size()).c_str(), ";layer;iphi;it", usedLayer.size() + 2, usedLayer[0] - 1.5, *usedLayer.rbegin() + 1.5, usedIPhi.size() + 2, usedIPhi[0] - 1.5, *usedIPhi.rbegin() + 1.5, usedIT.size() + 2, usedIT[0] - 1.5, *usedIT.rbegin() + 1.5); - //TH3D *hitHist = new TH3D(Form("hitHist_event%d_side%d_sector%d_module%d_cluster%d",my_data.eventNum,(int)my_data.side,(int)my_data.sector,(int)my_data.module,(int)my_data.cluster_vector.size()),";layer;iphi;it",usedLayer.size()+2,usedLayer[0]-1.5,*usedLayer.rbegin()+1.5,usedIPhi.size()+2,usedIPhi[0]-1.5,*usedIPhi.rbegin()+1.5,usedIT.size()+2,usedIT[0]-1.5,*usedIT.rbegin()+1.5); + // TH3D *hitHist = new TH3D(Form("hitHist_event%d_side%d_sector%d_module%d_cluster%d",my_data.eventNum,(int)my_data.side,(int)my_data.sector,(int)my_data.module,(int)my_data.cluster_vector.size()),";layer;iphi;it",usedLayer.size()+2,usedLayer[0]-1.5,*usedLayer.rbegin()+1.5,usedIPhi.size()+2,usedIPhi[0]-1.5,*usedIPhi.rbegin()+1.5,usedIT.size()+2,usedIT[0]-1.5,*usedIT.rbegin()+1.5); for (int i = 0; i < (int) clus->getNhits(); i++) { - my_data.hitHist->Fill(clus->getHitLayer(i), clus->getHitIPhi(i), clus->getHitIT(i), clus->getHitAdc(i)); sigmaLayer += pow(clus->getHitLayer(i) - meanLayer, 2); sigmaIPhi += pow(clus->getHitIPhi(i) - meanIPhi, 2); sigmaIT += pow(clus->getHitIT(i) - meanIT, 2); - + sigmaWeightedLayer += clus->getHitAdc(i) * pow(clus->getHitLayer(i) - (layerSum / adcSum), 2); sigmaWeightedIPhi += clus->getHitAdc(i) * pow(clus->getHitIPhi(i) - (iphiSum / adcSum), 2); sigmaWeightedIT += clus->getHitAdc(i) * pow(clus->getHitIT(i) - (itSum / adcSum), 2); @@ -451,20 +506,18 @@ namespace bool fitSuccess = false; ROOT::Fit::Fitter *fit3D = new ROOT::Fit::Fitter; - if(my_data.doFitting) + if (my_data.doFitting) { - double par_init[7] = { - maxAdc, - meanLayer, - meanIPhi, 0.75, - meanIT, 0.5, 1 - }; + maxAdc, + meanLayer, + meanIPhi, 0.75, + meanIT, 0.5, 1}; double satThreshold = 900.0; double sigma_ADC = 20.0; - auto nll = [&](const double* par) + auto nll = [&](const double *par) { double nll_val = 0.0; @@ -472,37 +525,37 @@ namespace int ny = my_data.hitHist->GetNbinsY(); int nz = my_data.hitHist->GetNbinsZ(); - double parLayer[2] = {1.0,par[1]}; - double parPhi[4] = {1.0,par[2],par[3]}; - double parTime[4] = {1.0,par[4],par[5],par[6]}; + double parLayer[2] = {1.0, par[1]}; + double parPhi[4] = {1.0, par[2], par[3]}; + double parTime[4] = {1.0, par[4], par[5], par[6]}; double xyz[3]; for (int i = 1; i <= nx; ++i) { - xyz[0] = my_data.hitHist->GetXaxis()->GetBinCenter(i); + xyz[0] = my_data.hitHist->GetXaxis()->GetBinCenter(i); for (int j = 1; j <= ny; ++j) { - xyz[1] = my_data.hitHist->GetYaxis()->GetBinCenter(j); + xyz[1] = my_data.hitHist->GetYaxis()->GetBinCenter(j); for (int k = 1; k <= nz; ++k) { xyz[2] = my_data.hitHist->GetZaxis()->GetBinCenter(k); double observed = my_data.hitHist->GetBinContent(i, j, k); - double expected = par[0]*layerFunction(&xyz[0], parLayer)*phiFunction(&xyz[1], parPhi)*timeFunction(&xyz[2], parTime); + double expected = par[0] * layerFunction(&xyz[0], parLayer) * phiFunction(&xyz[1], parPhi) * timeFunction(&xyz[2], parTime); - if(observed <= my_data.adc_threshold) + if (observed <= my_data.adc_threshold) { double arg = (expected - my_data.adc_threshold) / (sqrt(2.0) * sigma_ADC); double tail_prob = 0.5 * TMath::Erfc(arg); nll_val -= log(tail_prob + 1e-12); } - else if(observed < satThreshold) + else if (observed < satThreshold) { double resid = (observed - expected) / sigma_ADC; nll_val += 0.5 * (resid * resid + log(2 * TMath::Pi() * sigma_ADC * sigma_ADC)); } - else if(observed >= satThreshold) + else if (observed >= satThreshold) { double arg = (satThreshold - expected) / (sqrt(2.0) * sigma_ADC); double tail_prob = 0.5 * TMath::Erfc(arg); @@ -518,35 +571,33 @@ namespace fit3D->Config().ParSettings(0).SetName("amp"); fit3D->Config().ParSettings(0).SetStepSize(10); - fit3D->Config().ParSettings(0).SetLimits(0,5000); + fit3D->Config().ParSettings(0).SetLimits(0, 5000); fit3D->Config().ParSettings(1).SetName("mu_layer"); fit3D->Config().ParSettings(1).SetStepSize(0.1); - fit3D->Config().ParSettings(1).SetLimits(usedLayer[0],*usedLayer.rbegin()); + fit3D->Config().ParSettings(1).SetLimits(usedLayer[0], *usedLayer.rbegin()); fit3D->Config().ParSettings(2).SetName("mu_phi"); fit3D->Config().ParSettings(2).SetStepSize(0.1); - fit3D->Config().ParSettings(2).SetLimits(usedIPhi[0],*usedIPhi.rbegin()); + fit3D->Config().ParSettings(2).SetLimits(usedIPhi[0], *usedIPhi.rbegin()); fit3D->Config().ParSettings(3).SetName("sig_phi"); fit3D->Config().ParSettings(3).SetStepSize(0.1); - fit3D->Config().ParSettings(3).SetLimits(0.01,2); + fit3D->Config().ParSettings(3).SetLimits(0.01, 2); fit3D->Config().ParSettings(4).SetName("mu_t"); fit3D->Config().ParSettings(4).SetStepSize(0.1); - fit3D->Config().ParSettings(4).SetLimits(usedIT[0],*usedIT.rbegin()); + fit3D->Config().ParSettings(4).SetLimits(usedIT[0], *usedIT.rbegin()); fit3D->Config().ParSettings(5).SetName("sig_t"); fit3D->Config().ParSettings(5).SetStepSize(0.1); - fit3D->Config().ParSettings(5).SetLimits(0.01,10); + fit3D->Config().ParSettings(5).SetLimits(0.01, 10); fit3D->Config().ParSettings(6).SetName("lambda_t"); fit3D->Config().ParSettings(6).SetStepSize(0.01); - fit3D->Config().ParSettings(6).SetLimits(0,5); + fit3D->Config().ParSettings(6).SetLimits(0, 5); - - if(usedLayer.size() == 1) + if (usedLayer.size() == 1) { fit3D->Config().ParSettings(1).Fix(); } fitSuccess = fit3D->FitFCN(); - if (my_data.Verbosity > 2) { std::cout << "fit success: " << fitSuccess << std::endl; @@ -554,13 +605,9 @@ namespace } pthread_mutex_unlock(&mythreadlock); - - - if(my_data.doFitting && fitSuccess) + if (my_data.doFitting && fitSuccess) { - - const ROOT::Fit::FitResult& result = fit3D->Result(); - + const ROOT::Fit::FitResult &result = fit3D->Result(); PHG4TpcGeom *layergeomLow = my_data.geom_container->GetLayerCellGeom((int) floor(result.Parameter(1))); PHG4TpcGeom *layergeomHigh = my_data.geom_container->GetLayerCellGeom((int) ceil(result.Parameter(1))); @@ -569,12 +616,12 @@ namespace double RHigh = layergeomHigh->get_radius(); double phiHigh_RLow = -999.0; - if(ceil(result.Parameter(2)) < layergeomLow->get_phibins()) + if (ceil(result.Parameter(2)) < layergeomLow->get_phibins()) { phiHigh_RLow = layergeomLow->get_phi(ceil(result.Parameter(2)), (meanSide < 0 ? 0 : 1)); } double phiHigh_RHigh = -999.0; - if(ceil(result.Parameter(2)) < layergeomHigh->get_phibins()) + if (ceil(result.Parameter(2)) < layergeomHigh->get_phibins()) { phiHigh_RHigh = layergeomHigh->get_phi(ceil(result.Parameter(2)), (meanSide < 0 ? 0 : 1)); } @@ -587,18 +634,17 @@ namespace double meanPhi_RLow = ((result.Parameter(2) - floor(result.Parameter(2)))) * (phiHigh_RLow - phiLow_RLow) + phiLow_RLow; double meanPhi_RHigh = ((result.Parameter(2) - floor(result.Parameter(2)))) * (phiHigh_RHigh - phiLow_RHigh) + phiLow_RHigh; - double meanPhi = 0.5*(meanPhi_RLow + meanPhi_RHigh); - if(phiHigh_RLow == -999.0 && phiHigh_RHigh != -999.0) + double meanPhi = 0.5 * (meanPhi_RLow + meanPhi_RHigh); + if (phiHigh_RLow == -999.0 && phiHigh_RHigh != -999.0) { meanPhi = meanPhi_RHigh; } - else if(phiHigh_RLow != -999.0 && phiHigh_RHigh == -999.0) + else if (phiHigh_RLow != -999.0 && phiHigh_RHigh == -999.0) { meanPhi = meanPhi_RLow; } - - if(phiHigh_RLow == -999.0 && phiHigh_RHigh == -999.0) + if (phiHigh_RLow == -999.0 && phiHigh_RHigh == -999.0) { clus->setAdc(adcSum); clus->setX(clusX); @@ -619,10 +665,10 @@ namespace clus->setSDWeightedIT(sqrt(sigmaWeightedIT / adcSum)); } else - { + { clus->setAdc(adcSum); - clus->setX(meanR*cos(meanPhi)); - clus->setY(meanR*sin(meanPhi)); + clus->setX(meanR * cos(meanPhi)); + clus->setY(meanR * sin(meanPhi)); clus->setZ(clusZ); clus->setFitMode(true); clus->setLayer(result.Parameter(1)); @@ -663,25 +709,30 @@ namespace const auto ckey = TrkrDefs::genClusKey(maxKey, my_data.cluster_vector.size()); my_data.cluster_vector.push_back(clus); my_data.cluster_key_vector.push_back(ckey); - - if(fit3D) - { - delete fit3D; - } - if(my_data.hitHist) + delete fit3D; + + if (my_data.hitHist) { delete my_data.hitHist; my_data.hitHist = nullptr; } - - } - + /** + * @brief Processes TPC hits for a single thread partition and produces laser clusters. + * + * Collects hits from the thread's assigned hitsets that pass the configured ADC threshold + * and peak-time-bin filter, deduplicates spatially, and iteratively builds clusters starting + * from the highest-ADC remaining hit. For each cluster, computes cluster parameters (via + * calc_cluster_parameter) and removes clustered hits so they are not reused. Appends resulting + * LaserClusterv2 objects and their keys into the provided thread_data (cluster_vector and + * cluster_keys). + * + * @param my_data Per-thread state and configuration (input hitsets, geometry, thresholds) and + * output locations for generated clusters and their keys. void ProcessModuleData(thread_data *my_data) { - if (my_data->Verbosity > 2) { pthread_mutex_lock(&mythreadlock); @@ -693,85 +744,84 @@ namespace std::multimap adcMap; - if (my_data->hitsets.size() == 0) + if (my_data->hitsets.empty()) { return; } - for(int i=0; i<(int)my_data->hitsets.size(); i++) + for (int i = 0; i < (int) my_data->hitsets.size(); i++) { auto *hitset = my_data->hitsets[i]; unsigned int layer = my_data->layers[i]; bool side = my_data->side; unsigned int sector = my_data->sector; - TrkrDefs::hitsetkey hitsetKey = TpcDefs::genHitSetKey(layer, sector, (int)side); + TrkrDefs::hitsetkey hitsetKey = TpcDefs::genHitSetKey(layer, sector, (int) side); TrkrHitSet::ConstRange hitrangei = hitset->getHits(); for (TrkrHitSet::ConstIterator hitr = hitrangei.first; hitr != hitrangei.second; ++hitr) { - float_t fadc = hitr->second->getAdc(); - unsigned short adc = 0; - if (fadc > my_data->adc_threshold) - { - adc = (unsigned short) fadc; - } - else - { - continue; - } - - int iphi = TpcDefs::getPad(hitr->first); - int it = TpcDefs::getTBin(hitr->first); - - if(fabs(it - my_data->peakTimeBin) > 5) - { - continue; - } - - point coords = point((int) layer, iphi, it); - - std::vector testduplicate; - rtree.query(bgi::intersects(box(point(layer - 0.001, iphi - 0.001, it - 0.001), - point(layer + 0.001, iphi + 0.001, it + 0.001))), - std::back_inserter(testduplicate)); - if (!testduplicate.empty()) - { - testduplicate.clear(); - continue; - } - - TrkrDefs::hitkey hitKey = TpcDefs::genHitKey(iphi, it); - - auto spechitkey = std::make_pair(hitKey, hitsetKey); - pointKeyLaser coordsKey = std::make_pair(coords, spechitkey); - adcMap.insert(std::make_pair(adc, coordsKey)); + float_t fadc = hitr->second->getAdc(); + unsigned short adc = 0; + if (fadc > my_data->adc_threshold) + { + adc = (unsigned short) fadc; + } + else + { + continue; + } + + int iphi = TpcDefs::getPad(hitr->first); + int it = TpcDefs::getTBin(hitr->first); + + if (fabs(it - my_data->peakTimeBin) > 5) + { + continue; + } + + point coords = point((int) layer, iphi, it); + + std::vector testduplicate; + rtree.query(bgi::intersects(box(point(layer - 0.001, iphi - 0.001, it - 0.001), + point(layer + 0.001, iphi + 0.001, it + 0.001))), + std::back_inserter(testduplicate)); + if (!testduplicate.empty()) + { + testduplicate.clear(); + continue; + } + + TrkrDefs::hitkey hitKey = TpcDefs::genHitKey(iphi, it); + + auto spechitkey = std::make_pair(hitKey, hitsetKey); + pointKeyLaser coordsKey = std::make_pair(coords, spechitkey); + adcMap.insert(std::make_pair(adc, coordsKey)); auto adckey = std::make_pair(adc, spechitkey); - rtree.insert(std::make_pair(point(1.0*layer, 1.0*iphi, 1.0*it), adckey)); + rtree.insert(std::make_pair(point(1.0 * layer, 1.0 * iphi, 1.0 * it), adckey)); } } - //finished filling rtree + // finished filling rtree - while (adcMap.size() > 0) + while (!adcMap.empty()) { auto iterKey = adcMap.rbegin(); - if(iterKey == adcMap.rend()) + if (iterKey == adcMap.rend()) { - break; + break; } - auto coords = iterKey->second.first; int layer = coords.get<0>(); int iphi = coords.get<1>(); int it = coords.get<2>(); - + if (my_data->Verbosity > 2) { pthread_mutex_lock(&mythreadlock); - std::cout << "working on cluster " << my_data->cluster_vector.size() << " side: " << my_data->side << " sector: " << my_data->sector << " module: " << (layer<23 ? 1 : (layer<39 ? 2 : 3) ) << std::endl; + // NOLINTNEXTLINE (readability-avoid-nested-conditional-operator) + std::cout << "working on cluster " << my_data->cluster_vector.size() << " side: " << my_data->side << " sector: " << my_data->sector << " module: " << (layer < 23 ? 1 : (layer < 39 ? 2 : 3)) << std::endl; pthread_mutex_unlock(&mythreadlock); - } std::vector clusHits; @@ -781,23 +831,44 @@ namespace calc_cluster_parameter(clusHits, *my_data, iterKey->second.second); remove_hits(clusHits, rtree, adcMap); - } } + /** + * @brief Thread entry point that processes a module's data and then terminates the thread. + * + * @param threadarg Pointer to a thread_data structure containing per-thread state; must be a pointer to thread_data. + * @return void* Thread exit status: always `nullptr`. + */ void *ProcessModule(void *threadarg) { - auto my_data = static_cast(threadarg); + auto *my_data = static_cast(threadarg); ProcessModuleData(my_data); pthread_exit(nullptr); } -} //namespace +} /** + * @brief Construct a LaserClusterizer subsystem with the given name. + * + * Initializes the LaserClusterizer as a SubsysReco module and associates it with the provided identifier. + * + * @param name Identifier used to register and reference this subsystem instance. + */ LaserClusterizer::LaserClusterizer(const std::string &name) : SubsysReco(name) { } +/** + * @brief Initialize runtime resources and ensure required nodes are present. + * + * Ensures the DST tree contains a Laser cluster container (or lamination variant) and that the TPC geometry container + * is available; initializes the ADC clock period and maximum drift time used by the clusterizer. + * + * @param topNode Top-level node of the PHENIX/Fun4All node tree from which required subnodes are located or created. + * @return int Fun4AllReturnCodes::EVENT_OK on success, Fun4AllReturnCodes::ABORTRUN if required nodes (DST or TPCGEOMCONTAINER) + * cannot be found. + */ int LaserClusterizer::InitRun(PHCompositeNode *topNode) { TH1::AddDirectory(kFALSE); @@ -818,7 +889,7 @@ int LaserClusterizer::InitRun(PHCompositeNode *topNode) { laserClusterNodeName = "LAMINATION_CLUSTER"; } - auto laserclusters = findNode::getClass(dstNode, laserClusterNodeName); + auto *laserclusters = findNode::getClass(dstNode, laserClusterNodeName); if (!laserclusters) { PHNodeIterator dstiter(dstNode); @@ -835,7 +906,7 @@ int LaserClusterizer::InitRun(PHCompositeNode *topNode) new PHIODataNode(laserclusters, laserClusterNodeName, "PHObject"); DetNode->addNode(LaserClusterContainerNode); } - + m_geom_container = findNode::getClass(topNode, "TPCGEOMCONTAINER"); if (!m_geom_container) @@ -850,6 +921,20 @@ int LaserClusterizer::InitRun(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Process a single event: locate required nodes, build laser clusters, and store them in the cluster container. + * + * Traverses the node tree starting at topNode to read EventHeader, LaserEventInfo, hitsets, geometry, and the target + * LaserCluster container. If the event is a laser event, spawns worker threads for each sector/side/module to + * collect hits, form clusters, optionally fit cluster parameters, and add resulting LaserCluster objects to the + * LaserClusterContainer. + * + * @param topNode Top-level node of the framework's node tree used to find input/output nodes. + * @return int Fun4AllReturnCodes::EVENT_OK on successful processing; + * Fun4AllReturnCodes::ABORTRUN if required nodes (EventHeader, LaserEventInfo, TRKR_HITSET, cluster node, + * or ActsGeometry) are missing; + * non-zero on lower-level initialization or thread creation/join failures. + */ int LaserClusterizer::process_event(PHCompositeNode *topNode) { eventHeader = findNode::getClass(topNode, "EventHeader"); @@ -873,7 +958,7 @@ int LaserClusterizer::process_event(PHCompositeNode *topNode) return Fun4AllReturnCodes::ABORTRUN; } - if((eventHeader->get_RunNumber() > 66153 && !m_laserEventInfo->isGl1LaserEvent()) || (eventHeader->get_RunNumber() <= 66153 && !m_laserEventInfo->isLaserEvent())) + if ((eventHeader->get_RunNumber() > 66153 && !m_laserEventInfo->isGl1LaserEvent()) || (eventHeader->get_RunNumber() <= 66153 && !m_laserEventInfo->isLaserEvent())) { return Fun4AllReturnCodes::EVENT_OK; } @@ -897,7 +982,7 @@ int LaserClusterizer::process_event(PHCompositeNode *topNode) std::cout << PHWHERE << "ERROR: Can't find node TRKR_HITSET" << std::endl; return Fun4AllReturnCodes::ABORTRUN; } - + // get node for clusters std::string laserClusterNodeName = "LASER_CLUSTER"; if (m_lamination) @@ -921,8 +1006,8 @@ int LaserClusterizer::process_event(PHCompositeNode *topNode) return Fun4AllReturnCodes::ABORTRUN; } - TrkrHitSetContainer::ConstRange hitsetrange = m_hits->getHitSets(TrkrDefs::TrkrId::tpcId);; - + TrkrHitSetContainer::ConstRange hitsetrange = m_hits->getHitSets(TrkrDefs::TrkrId::tpcId); + struct thread_pair_t { pthread_t thread{}; @@ -938,123 +1023,122 @@ int LaserClusterizer::process_event(PHCompositeNode *topNode) if (pthread_mutex_init(&mythreadlock, nullptr) != 0) { - std::cout << std::endl << " mutex init failed" << std::endl; + std::cout << std::endl + << " mutex init failed" << std::endl; return 1; } - - for (unsigned int sec=0; sec<12; sec++) + + for (unsigned int sec = 0; sec < 12; sec++) { - for (int s=0; s<2; s++) + for (int s = 0; s < 2; s++) { - for (unsigned int mod=0; mod<3; mod++) + for (unsigned int mod = 0; mod < 3; mod++) { - - if(Verbosity() > 2) + if (Verbosity() > 2) { std::cout << "making thread for side: " << s << " sector: " << sec << " module: " << mod << std::endl; } - thread_pair_t &thread_pair = threads.emplace_back(); - - std::vector hitsets; - std::vector layers; - - std::vector cluster_vector; - std::vector cluster_key_vector; - - for (TrkrHitSetContainer::ConstIterator hitsetitr = hitsetrange.first; - hitsetitr != hitsetrange.second; - ++hitsetitr) - { - unsigned int layer = TrkrDefs::getLayer(hitsetitr->first); - int side = TpcDefs::getSide(hitsetitr->first); - unsigned int sector = TpcDefs::getSectorId(hitsetitr->first); - if (sector != sec || side != s) - { - continue; - } - if ((mod==0 && (layer<7 || layer>22)) || (mod==1 && (layer<=22 || layer>38) ) || (mod==2 && (layer<=38 || layer>54))) - { - continue; - } - - TrkrHitSet *hitset = hitsetitr->second; - - hitsets.push_back(hitset); - layers.push_back(layer); - - } - - thread_pair.data.geom_container = m_geom_container; - thread_pair.data.tGeometry = m_tGeometry; - thread_pair.data.hitsets = hitsets; - thread_pair.data.layers = layers; - thread_pair.data.side = (bool)s; - thread_pair.data.sector = sec; + thread_pair_t &thread_pair = threads.emplace_back(); + + std::vector hitsets; + std::vector layers; + + std::vector cluster_vector; + std::vector cluster_key_vector; + + for (TrkrHitSetContainer::ConstIterator hitsetitr = hitsetrange.first; + hitsetitr != hitsetrange.second; + ++hitsetitr) + { + unsigned int layer = TrkrDefs::getLayer(hitsetitr->first); + int side = TpcDefs::getSide(hitsetitr->first); + unsigned int sector = TpcDefs::getSectorId(hitsetitr->first); + if (sector != sec || side != s) + { + continue; + } + if ((mod == 0 && (layer < 7 || layer > 22)) || (mod == 1 && (layer <= 22 || layer > 38)) || (mod == 2 && (layer <= 38 || layer > 54))) + { + continue; + } + + TrkrHitSet *hitset = hitsetitr->second; + + hitsets.push_back(hitset); + layers.push_back(layer); + } + + thread_pair.data.geom_container = m_geom_container; + thread_pair.data.tGeometry = m_tGeometry; + thread_pair.data.hitsets = hitsets; + thread_pair.data.layers = layers; + thread_pair.data.side = (bool) s; + thread_pair.data.sector = sec; thread_pair.data.module = mod; - thread_pair.data.cluster_vector = cluster_vector; - thread_pair.data.cluster_key_vector = cluster_key_vector; - thread_pair.data.adc_threshold = m_adc_threshold; - thread_pair.data.peakTimeBin = m_laserEventInfo->getPeakSample(s); - thread_pair.data.layerMin = 3; - thread_pair.data.layerMax = 3; - thread_pair.data.tdriftmax = m_tdriftmax; + thread_pair.data.cluster_vector = cluster_vector; + thread_pair.data.cluster_key_vector = cluster_key_vector; + thread_pair.data.adc_threshold = m_adc_threshold; + thread_pair.data.peakTimeBin = m_laserEventInfo->getPeakSample(s); + thread_pair.data.layerMin = 3; + thread_pair.data.layerMax = 3; + thread_pair.data.tdriftmax = m_tdriftmax; thread_pair.data.eventNum = m_event; thread_pair.data.Verbosity = Verbosity(); thread_pair.data.hitHist = nullptr; thread_pair.data.doFitting = m_do_fitting; - int rc; - rc = pthread_create(&thread_pair.thread, &attr, ProcessModule, (void *) &thread_pair.data); - - if (rc) - { - std::cout << "Error:unable to create thread," << rc << std::endl; - } - - if (m_do_sequential) - { - //wait for termination of thread - int rc2 = pthread_join(thread_pair.thread, nullptr); - if (rc2) - { - std::cout << "Error:unable to join," << rc2 << std::endl; - } - - //add clusters from thread to laserClusterContainer - const auto &data(thread_pair.data); - for(int index = 0; index < (int) data.cluster_vector.size(); ++index) - { - auto cluster = data.cluster_vector[index]; - const auto ckey = data.cluster_key_vector[index]; - - m_clusterlist->addClusterSpecifyKey(ckey, cluster); - } - } + int rc; + rc = pthread_create(&thread_pair.thread, &attr, ProcessModule, (void *) &thread_pair.data); + + if (rc) + { + std::cout << "Error:unable to create thread," << rc << std::endl; + } + + if (m_do_sequential) + { + // wait for termination of thread + int rc2 = pthread_join(thread_pair.thread, nullptr); + if (rc2) + { + std::cout << "Error:unable to join," << rc2 << std::endl; + } + + // add clusters from thread to laserClusterContainer + const auto &data(thread_pair.data); + for (int index = 0; index < (int) data.cluster_vector.size(); ++index) + { + auto *cluster = data.cluster_vector[index]; + const auto ckey = data.cluster_key_vector[index]; + + m_clusterlist->addClusterSpecifyKey(ckey, cluster); + } + } } } } - + pthread_attr_destroy(&attr); if (!m_do_sequential) { - for (const auto & thread_pair : threads) + for (const auto &thread_pair : threads) { int rc2 = pthread_join(thread_pair.thread, nullptr); if (rc2) { - std::cout << "Error:unable to join," << rc2 << std::endl; + std::cout << "Error:unable to join," << rc2 << std::endl; } - - //const auto &data(thread_pair.data); - - for(int index = 0; index < (int) thread_pair.data.cluster_vector.size(); ++index) + + // const auto &data(thread_pair.data); + + for (int index = 0; index < (int) thread_pair.data.cluster_vector.size(); ++index) { - auto cluster = thread_pair.data.cluster_vector[index]; - const auto ckey = thread_pair.data.cluster_key_vector[index]; - - m_clusterlist->addClusterSpecifyKey(ckey, cluster); + auto *cluster = thread_pair.data.cluster_vector[index]; + const auto ckey = thread_pair.data.cluster_key_vector[index]; + + m_clusterlist->addClusterSpecifyKey(ckey, cluster); } } } @@ -1067,5 +1151,4 @@ int LaserClusterizer::process_event(PHCompositeNode *topNode) } return Fun4AllReturnCodes::EVENT_OK; - -} +} \ No newline at end of file diff --git a/offline/packages/tpc/LaserEventIdentifier.cc b/offline/packages/tpc/LaserEventIdentifier.cc index 8b6035db05..e62ea8795b 100644 --- a/offline/packages/tpc/LaserEventIdentifier.cc +++ b/offline/packages/tpc/LaserEventIdentifier.cc @@ -111,6 +111,17 @@ int LaserEventIdentifier::InitRun(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Identify laser events from TPC hits and GL1 packet information and record results. + * + * Processes the event by locating the LaserEventInfo node and optional GL1RAWHIT packet, updating + * GL1-related flags, accumulating TPC time-bin histograms per side, detecting Gaussian-like + * time-bin peaks, and storing laser-event and peak information into the LaserEventInfo object. + * If debugging is enabled, per-event debug data are written to the debug TTree. + * + * @param topNode Top-level node of the framework's node tree used to find input/output nodes. + * @return Fun4AllReturnCodes::ABORTRUN if the LaserEventInfo node is missing; Fun4AllReturnCodes::EVENT_OK otherwise. + */ int LaserEventIdentifier::process_event(PHCompositeNode *topNode) { m_laserEventInfo = findNode::getClass(topNode, "LaserEventInfo"); @@ -130,7 +141,7 @@ int LaserEventIdentifier::process_event(PHCompositeNode *topNode) } else if(m_runnumber > 66153) { - if ((gl1pkt->getGTMAllBusyVector() & (1<<14)) == 0) + if ((gl1pkt->getGTMAllBusyVector() & (1U<<14U)) == 0) { m_laserEventInfo->setIsGl1LaserEvent(true); m_laserEventInfo->setIsGl1LaserPileupEvent(false); @@ -274,4 +285,4 @@ int LaserEventIdentifier::End(PHCompositeNode * /*topNode*/) } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/Tpc3DClusterizer.cc b/offline/packages/tpc/Tpc3DClusterizer.cc index e19cbf507f..c9b8b8d6de 100644 --- a/offline/packages/tpc/Tpc3DClusterizer.cc +++ b/offline/packages/tpc/Tpc3DClusterizer.cc @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -59,6 +60,18 @@ Tpc3DClusterizer::Tpc3DClusterizer(const std::string &name) { } +/** + * @brief Initialize runtime nodes, I/O, geometry, and timers required by the clusterizer. + * + * Ensures a LASER_CLUSTER container exists under DST/TRKR (creates it if absent), + * opens debug and/or output ROOT files and creates associated histograms/trees/ntuple + * when enabled, obtains the TPC geometry container and derives ADC timing parameters, + * and initializes per-module timers used to profile processing stages. + * + * @param topNode The top-level PHCompositeNode of the current event/run node tree. + * @return Fun4AllReturnCodes::EVENT_OK on successful initialization; + * Fun4AllReturnCodes::ABORTRUN if required nodes (DST or TPCGEOMCONTAINER) are missing. + */ int Tpc3DClusterizer::InitRun(PHCompositeNode *topNode) { PHNodeIterator iter(topNode); @@ -72,7 +85,7 @@ int Tpc3DClusterizer::InitRun(PHCompositeNode *topNode) } // Create the Cluster node if required - auto laserclusters = findNode::getClass(dstNode, "LASER_CLUSTER"); + auto *laserclusters = findNode::getClass(dstNode, "LASER_CLUSTER"); if (!laserclusters) { PHNodeIterator dstiter(dstNode); @@ -111,13 +124,14 @@ int Tpc3DClusterizer::InitRun(PHCompositeNode *topNode) m_clusterTree->Branch("time_erase", &time_erase); m_clusterTree->Branch("time_all", &time_all); } - - if (m_output){ + + if (m_output) + { m_outputFile = new TFile(m_outputFileName.c_str(), "RECREATE"); - m_clusterNT = new TNtuple("clus3D", "clus3D","event:seed:x:y:z:r:phi:phibin:tbin:adc:maxadc:layer:phielem:zelem:size:phisize:tsize:lsize"); + m_clusterNT = new TNtuple("clus3D", "clus3D", "event:seed:x:y:z:r:phi:phibin:tbin:adc:maxadc:layer:phielem:zelem:size:phisize:tsize:lsize"); } - + m_geom_container = findNode::getClass(topNode, "TPCGEOMCONTAINER"); if (!m_geom_container) @@ -142,13 +156,26 @@ int Tpc3DClusterizer::InitRun(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Process a single event: build 3D TPC clusters from TRKR_HITSET and store them in the LASER_CLUSTER node. + * + * Traverses TPC hitsets to build spatial indexes, selects seed hits by ADC, forms clusters within a local 3D window, + * computes cluster parameters, removes clustered hits from the working sets, and appends created LaserCluster objects + * to the LASER_CLUSTER container. Timing and optional debug outputs are updated when enabled. + * + * @param topNode Root of the node tree for the current event; must contain TRKR_HITSET, LASER_CLUSTER, and ActsGeometry nodes. + * @return Fun4AllReturnCodes::EVENT_OK on successful processing of the event; Fun4AllReturnCodes::ABORTRUN if required nodes (DST, TRKR_HITSET, LASER_CLUSTER, or ActsGeometry) are missing. + */ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) { ++m_event; - recoConsts* rc = recoConsts::instance(); - if (rc->FlagExist("RANDOMSEED")){ - m_seed = (int)rc->get_IntFlag("RANDOMSEED"); - } else { + recoConsts *rc = recoConsts::instance(); + if (rc->FlagExist("RANDOMSEED")) + { + m_seed = rc->get_IntFlag("RANDOMSEED"); + } + else + { m_seed = std::numeric_limits::quiet_NaN(); } @@ -163,11 +190,12 @@ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) } // get node containing the digitized hits m_hits = findNode::getClass(topNode, "TRKR_HITSET"); - if (!m_hits){ + if (!m_hits) + { std::cout << PHWHERE << "ERROR: Can't find node TRKR_HITSET" << std::endl; return Fun4AllReturnCodes::ABORTRUN; } - + // get node for clusters m_clusterlist = findNode::getClass(topNode, "LASER_CLUSTER"); if (!m_clusterlist) @@ -193,7 +221,8 @@ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) bgi::rtree> rtree_reject; for (TrkrHitSetContainer::ConstIterator hitsetitr = hitsetrange.first; hitsetitr != hitsetrange.second; - ++hitsetitr){ + ++hitsetitr) + { TrkrHitSet *hitset = hitsetitr->second; unsigned int layer = TrkrDefs::getLayer(hitsetitr->first); int side = TpcDefs::getSide(hitsetitr->first); @@ -202,49 +231,54 @@ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) TrkrHitSet::ConstRange hitrangei = hitset->getHits(); for (TrkrHitSet::ConstIterator hitr = hitrangei.first; - hitr != hitrangei.second; - ++hitr){ + hitr != hitrangei.second; + ++hitr) + { int iphi = TpcDefs::getPad(hitr->first); int it = TpcDefs::getTBin(hitr->first); // std::cout << " iphi: " << iphi << " it: " << it << std::endl; - float_t fadc = (hitr->second->getAdc());// - m_pedestal; // proper int rounding +0.5 + float_t fadc = (hitr->second->getAdc()); // - m_pedestal; // proper int rounding +0.5 unsigned short adc = 0; - if (fadc > 0){ - adc = (unsigned short) fadc; + if (fadc > 0) + { + adc = (unsigned short) fadc; } - if (adc <= 0){ - continue; + if (adc <= 0) + { + continue; } - + std::vector testduplicate; rtree.query(bgi::intersects(box(point(layer - 0.001, iphi - 0.001, it - 0.001), - point(layer + 0.001, iphi + 0.001, it + 0.001))), - std::back_inserter(testduplicate)); - if (!testduplicate.empty()){ - testduplicate.clear(); - continue; + point(layer + 0.001, iphi + 0.001, it + 0.001))), + std::back_inserter(testduplicate)); + if (!testduplicate.empty()) + { + testduplicate.clear(); + continue; } TrkrDefs::hitkey hitKey = TpcDefs::genHitKey(iphi, it); - + auto spechitkey = std::make_pair(hitKey, hitsetKey); rtree_reject.insert(std::make_pair(point(1.0 * layer, 1.0 * iphi, 1.0 * it), spechitkey)); } } - + std::multimap, std::array>> adcMap; // std::cout << "n hitsets: " << std::distance(hitsetrange.first,hitsetrange.second) // << std::endl; for (TrkrHitSetContainer::ConstIterator hitsetitr = hitsetrange.first; hitsetitr != hitsetrange.second; - ++hitsetitr){ + ++hitsetitr) + { TrkrHitSet *hitset = hitsetitr->second; unsigned int layer = TrkrDefs::getLayer(hitsetitr->first); int side = TpcDefs::getSide(hitsetitr->first); unsigned int sector = TpcDefs::getSectorId(hitsetitr->first); - //PHG4TpcGeom *layergeom = m_geom_container->GetLayerCellGeom(layer); - // double r = layergeom->get_radius(); - + // PHG4TpcGeom *layergeom = m_geom_container->GetLayerCellGeom(layer); + // double r = layergeom->get_radius(); + TrkrDefs::hitsetkey hitsetKey = TpcDefs::genHitSetKey(layer, sector, side); TrkrHitSet::ConstRange hitrangei = hitset->getHits(); @@ -252,74 +286,122 @@ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) // << std::endl; // int nhits = 0; for (TrkrHitSet::ConstIterator hitr = hitrangei.first; - hitr != hitrangei.second; - ++hitr){ + hitr != hitrangei.second; + ++hitr) + { int iphi = TpcDefs::getPad(hitr->first); int it = TpcDefs::getTBin(hitr->first); // std::cout << " iphi: " << iphi << " it: " << it << std::endl; - float_t fadc = (hitr->second->getAdc());// - m_pedestal; // proper int rounding +0.5 + float_t fadc = (hitr->second->getAdc()); // - m_pedestal; // proper int rounding +0.5 unsigned short adc = 0; // std::cout << " nhit: " << nhits++ << "adc: " << fadc << " phi: " << iphi << " it: " << it << std::endl; - if (fadc > 0){ - adc = (unsigned short) fadc; + if (fadc > 0) + { + adc = (unsigned short) fadc; } - if (adc <= 0){ - continue; + if (adc <= 0) + { + continue; } - if(layer>=7+32){ - //if(side==1)continue; - if(abs(iphi-0)<=2) continue; - if(abs(iphi-191)<=2) continue; - if(abs(iphi-206)<=1) continue; - if(abs(iphi-383)<=2) continue; - if(abs(iphi-576)<=2) continue; - if(abs(iphi-767)<=2) continue; - if(abs(iphi-960)<=2) continue; - if(abs(iphi-1522)<=2) continue; - if(abs(iphi-1344)<=2) continue; - if(abs(iphi-1536)<=2) continue; - if(abs(iphi-1728)<=2) continue; - if(abs(iphi-1920)<=2) continue; - if(abs(iphi-2111)<=2) continue; - if(abs(iphi-2303)<=2) continue; + if (layer >= 7 + 32) + { + // if(side==1)continue; + if (abs(iphi - 0) <= 2) + { + continue; + } + if (abs(iphi - 191) <= 2) + { + continue; + } + if (abs(iphi - 206) <= 1) + { + continue; + } + if (abs(iphi - 383) <= 2) + { + continue; + } + if (abs(iphi - 576) <= 2) + { + continue; + } + if (abs(iphi - 767) <= 2) + { + continue; + } + if (abs(iphi - 960) <= 2) + { + continue; + } + if (abs(iphi - 1522) <= 2) + { + continue; + } + if (abs(iphi - 1344) <= 2) + { + continue; + } + if (abs(iphi - 1536) <= 2) + { + continue; + } + if (abs(iphi - 1728) <= 2) + { + continue; + } + if (abs(iphi - 1920) <= 2) + { + continue; + } + if (abs(iphi - 2111) <= 2) + { + continue; + } + if (abs(iphi - 2303) <= 2) + { + continue; + } } /* double phi = layergeom->get_phi(iphi); double m_sampa_tbias = 39.6; double zdriftlength = (layergeom->get_zcenter(it)+ m_sampa_tbias) * m_tGeometry->get_drift_velocity(); - + float x = r * cos(phi); float y = r * sin(phi); float z = m_tdriftmax * m_tGeometry->get_drift_velocity() - zdriftlength; if (side == 0){ - z = -z; - it = -it; + z = -z; + it = -it; } */ std::array coords = {(int) layer, iphi, it}; - + std::vector testduplicate; rtree.query(bgi::intersects(box(point(layer - 0.001, iphi - 0.001, it - 0.001), - point(layer + 0.001, iphi + 0.001, it + 0.001))), - std::back_inserter(testduplicate)); - if (!testduplicate.empty()){ - testduplicate.clear(); - continue; + point(layer + 0.001, iphi + 0.001, it + 0.001))), + std::back_inserter(testduplicate)); + if (!testduplicate.empty()) + { + testduplicate.clear(); + continue; } - - //test for isolated hit + + // test for isolated hit std::vector testisolated; rtree_reject.query(bgi::intersects(box(point(layer - 1.001, iphi - 1.001, it - 1.001), - point(layer + 1.001, iphi + 1.001, it + 1.001))), - std::back_inserter(testisolated)); - if(testisolated.size()==1){ - //testisolated.clear(); - continue; + point(layer + 1.001, iphi + 1.001, it + 1.001))), + std::back_inserter(testisolated)); + if (testisolated.size() == 1) + { + // testisolated.clear(); + continue; } - + TrkrDefs::hitkey hitKey = TpcDefs::genHitKey(iphi, it); - + auto spechitkey = std::make_pair(hitKey, hitsetKey); auto keyCoords = std::make_pair(spechitkey, coords); adcMap.insert(std::make_pair(adc, keyCoords)); @@ -327,38 +409,43 @@ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) rtree.insert(std::make_pair(point(1.0 * layer, 1.0 * iphi, 1.0 * it), spechitkey)); } } - - if (Verbosity() > 1){ + + if (Verbosity() > 1) + { std::cout << "finished looping over hits" << std::endl; std::cout << "map size: " << adcMap.size() << std::endl; std::cout << "rtree size: " << rtree.size() << std::endl; } - + // done filling rTree - + t_all->restart(); - - while (adcMap.size() > 0){ + + while (!adcMap.empty()) + { auto iterKey = adcMap.rbegin(); - if (iterKey == adcMap.rend()){ + if (iterKey == adcMap.rend()) + { break; } - + auto coords = iterKey->second.second; int layer = coords[0]; int iphi = coords[1]; int it = coords[2]; - + int layerMax = layer + 1; - if (layer == 22 || layer == 38 || layer == 54){ + if (layer == 22 || layer == 38 || layer == 54) + { layerMax = layer; } int layerMin = layer - 1; - if (layer == 7 || layer == 23 || layer == 39){ + if (layer == 7 || layer == 23 || layer == 39) + { layerMin = layer; } - + std::vector clusHits; t_search->restart(); @@ -376,21 +463,24 @@ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) clusHits.clear(); } - if (m_debug){ + if (m_debug) + { m_nClus = (int) m_eventClusters.size(); } t_all->stop(); - if (m_debug){ + if (m_debug) + { time_search = t_search->get_accumulated_time() / 1000.; time_clus = t_clus->get_accumulated_time() / 1000.; time_erase = t_erase->get_accumulated_time() / 1000.; time_all = t_all->get_accumulated_time() / 1000.; - + m_clusterTree->Fill(); } - - if (Verbosity()){ + + if (Verbosity()) + { std::cout << "rtree search time: " << t_search->get_accumulated_time() / 1000. << " sec" << std::endl; std::cout << "clustering time: " << t_clus->get_accumulated_time() / 1000. << " sec" << std::endl; std::cout << "erasing time: " << t_erase->get_accumulated_time() / 1000. << " sec" << std::endl; @@ -401,30 +491,48 @@ int Tpc3DClusterizer::process_event(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } -int Tpc3DClusterizer::ResetEvent(PHCompositeNode * /*topNode*/){ +/** + * @brief Reset per-event diagnostics and debug state. + * + * Clears per-event iteration and, when debug mode is enabled, timing histograms and the collected event clusters. + * + * @return int Fun4AllReturnCodes::EVENT_OK on success. + */ +int Tpc3DClusterizer::ResetEvent(PHCompositeNode * /*topNode*/) +{ m_itHist_0->Reset(); m_itHist_1->Reset(); - + if (m_debug) - { - m_tHist_0->Reset(); - m_tHist_1->Reset(); - - m_eventClusters.clear(); - } - + { + m_tHist_0->Reset(); + m_tHist_1->Reset(); + + m_eventClusters.clear(); + } + return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Finalize the module by writing and closing enabled debug and output ROOT files. + * + * If debug mode is enabled, writes the in-memory cluster tree to the debug file and closes it. + * If output mode is enabled, writes the cluster NTuple to the output file and closes it. + * + * @return Fun4AllReturnCodes::EVENT_OK indicating successful completion. + */ int Tpc3DClusterizer::End(PHCompositeNode * /*topNode*/) { - if (m_debug){ + if (m_debug) + { m_debugFile->cd(); m_clusterTree->Write(); m_debugFile->Close(); } - if (m_output){ + if (m_output) + { m_outputFile->cd(); m_clusterNT->Write(); m_outputFile->Close(); @@ -432,9 +540,31 @@ int Tpc3DClusterizer::End(PHCompositeNode * /*topNode*/) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Compute and finalize a 3D laser cluster from candidate hits. + * + * Calculates ADC-weighted cluster centroid and extent in radius, phi, and time, + * converts hit coordinates to Cartesian (X,Y,Z) using TPC geometry and drift + * velocity, aggregates hit-level ADC and maxima, constructs a LaserCluster + * object, and records the cluster in the module outputs. + * + * @param clusHits Vector of candidate hits for the cluster; each entry pairs + * a 3D hit coordinate tuple (layer, iphi, it) with the + * corresponding (hitkey, hitsetkey). + * @param adcMap Multimap keyed by ADC value that maps to pairs of + * ((hitkey, hitsetkey), coordinate-array). Used to look up + * per-hit ADC and to remove hits from the active dataset. + * + * Side effects: + * - Adds a LaserCluster to m_clusterlist when at least one hit is processed. + * - When debugging is enabled, clones the cluster into m_currentCluster and + * appends to m_eventClusters. + * - Fills the m_clusterNT ntuple with per-cluster summary values. + * - Returns early without creating a cluster if clusHits is empty. + */ void Tpc3DClusterizer::calc_cluster_parameter(std::vector &clusHits, std::multimap, std::array>> &adcMap) { - //std::cout << "nu clus" << std::endl; + // std::cout << "nu clus" << std::endl; double rSum = 0.0; double phiSum = 0.0; double tSum = 0.0; @@ -449,9 +579,12 @@ void Tpc3DClusterizer::calc_cluster_parameter(std::vector &clusHi TrkrDefs::hitsetkey maxKey = 0; unsigned int nHits = clusHits.size(); - int iphimin = 6666, iphimax = -1; - int ilaymin = 6666, ilaymax = -1; - float itmin = 66666666.6, itmax = -6666666666.6; + int iphimin = 6666; + int iphimax = -1; + int ilaymin = 6666; + int ilaymax = -1; + float itmin = 66666666.6; + float itmax = -6666666666.6; auto *clus = new LaserClusterv1; for (auto &clusHit : clusHits) @@ -468,21 +601,21 @@ void Tpc3DClusterizer::calc_cluster_parameter(std::vector &clusHi double phi = layergeom->get_phi(coords[1], side); double t = layergeom->get_zcenter(fabs(coords[2])); int tbin = coords[2]; - int lay = coords[0];//TrkrDefs::getLayer(spechitkey.second); + int lay = coords[0]; // TrkrDefs::getLayer(spechitkey.second); double hitzdriftlength = t * m_tGeometry->get_drift_velocity(); double hitZ = m_tdriftmax * m_tGeometry->get_drift_velocity() - hitzdriftlength; /*std::cout << " lay: " << lay - << " phi: " << phi - << " t: " << t - << " side: " << side - << std::endl; + << " phi: " << phi + << " t: " << t + << " side: " << side + << std::endl; */ - if(phiiphimax){iphimax = phi;} - if(layilaymax){ilaymax = lay;} - if(tbinitmax){itmax = tbin;} + iphimin = std::min(phi, iphimin); + iphimax = std::max(phi, iphimax); + ilaymin = std::min(lay, ilaymin); + ilaymax = std::max(lay, ilaymax); + itmin = std::min(tbin, itmin); + itmax = std::max(tbin, itmax); for (auto &iterKey : adcMap) { @@ -522,7 +655,7 @@ void Tpc3DClusterizer::calc_cluster_parameter(std::vector &clusHi if (nHits == 0) { - std::cout << "no hits"<< std::endl; + std::cout << "no hits" << std::endl; return; } @@ -554,67 +687,82 @@ void Tpc3DClusterizer::calc_cluster_parameter(std::vector &clusHi clus->setLayer(layerSum / adcSum); clus->setIPhi(iphiSum / adcSum); clus->setIT(itSum / adcSum); - int phisize = iphimax - iphimin + 1; - int lsize = ilaymax - ilaymin + 1; - int tsize = itmax - itmin +1; + int phisize = iphimax - iphimin + 1; + int lsize = ilaymax - ilaymin + 1; + int tsize = itmax - itmin + 1; if (m_debug) { m_currentCluster = (LaserCluster *) clus->CloneMe(); m_eventClusters.push_back((LaserCluster *) m_currentCluster->CloneMe()); } // if(nHits>1&&tsize>5){ - if(nHits>=1){ + if (nHits >= 1) + { const auto ckey = TrkrDefs::genClusKey(maxKey, m_clusterlist->size()); m_clusterlist->addClusterSpecifyKey(ckey, clus); - } else { + } + else + { delete clus; } - - //event:seed:x:y:z:r:phi:phibin:tbin::adc:maxadc:layer:phielem:zelem:size:phisize:tsize:lsize + // event:seed:x:y:z:r:phi:phibin:tbin::adc:maxadc:layer:phielem:zelem:size:phisize:tsize:lsize //"event:seed:x:y:z:r:phi:phibin:tbin::adc:maxadc:layer:phielem:zelem:size:phisize:tsize:lsize" /* std::cout << " l size: " << lsize - << " phisize : " << phisize - << " tsize: " << tsize - << " maxside: " << maxside - << std::endl; + << " phisize : " << phisize + << " tsize: " << tsize + << " maxside: " << maxside + << std::endl; */ // if (m_output){ - float fX[20] = {0}; - int n = 0; - fX[n++] = m_event; - fX[n++] = m_seed; - fX[n++] = clusX; - fX[n++] = clusY; - fX[n++] = clusZ; - fX[n++] = clusR; - fX[n++] = clusPhi; - fX[n++] = clusiPhi; - fX[n++] = clusT; - fX[n++] = adcSum; - fX[n++] = maxAdc; - fX[n++] = (layerSum/adcSum); - fX[n++] = maxsector; - fX[n++] = maxside; - fX[n++] = nHits; - fX[n++] = phisize; - fX[n++] = tsize; - fX[n++] = lsize; - m_clusterNT->Fill(fX); - // } + float fX[20] = {0}; + int n = 0; + fX[n++] = m_event; + fX[n++] = m_seed; + fX[n++] = clusX; + fX[n++] = clusY; + fX[n++] = clusZ; + fX[n++] = clusR; + fX[n++] = clusPhi; + fX[n++] = clusiPhi; + fX[n++] = clusT; + fX[n++] = adcSum; + fX[n++] = maxAdc; + fX[n++] = (layerSum / adcSum); + fX[n++] = maxsector; + fX[n++] = maxside; + fX[n++] = nHits; + fX[n++] = phisize; + fX[n++] = tsize; + fX[n++] = lsize; + m_clusterNT->Fill(fX); + // } } +/** + * @brief Remove entries corresponding to cluster hits from the ADC map. + * + * For each hit in clusHits, erases the first matching entry in adcMap whose + * spechitkey equals the hit's stored key. If the provided rtree is empty, + * writes "not good" to standard output. This function does not remove + * entries from the rtree (rtree removals are intentionally commented out). + * + * @param clusHits Vector of spatial keys and associated hit keys representing cluster hits. + * @param rtree Spatial index of candidate hits; inspected for emptiness and not modified. + * @param adcMap Multimap of ADC values to (hit key, hitset key, coordinates) from which matching entries are erased. + */ void Tpc3DClusterizer::remove_hits(std::vector &clusHits, bgi::rtree> &rtree, std::multimap, std::array>> &adcMap) { for (auto &clusHit : clusHits) { auto spechitkey = clusHit.second; - if(rtree.size()==0){ + if (rtree.empty()) + { std::cout << "not good" << std::endl; } - //rtree.remove(clusHit); + // rtree.remove(clusHit); for (auto iterAdc = adcMap.begin(); iterAdc != adcMap.end();) { @@ -623,10 +771,8 @@ void Tpc3DClusterizer::remove_hits(std::vector &clusHits, bgi::rt iterAdc = adcMap.erase(iterAdc); break; } - else - { - ++iterAdc; - } + + ++iterAdc; } } -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcClusterMover.cc b/offline/packages/tpc/TpcClusterMover.cc index 643fe5587f..d0c1d2b1e2 100644 --- a/offline/packages/tpc/TpcClusterMover.cc +++ b/offline/packages/tpc/TpcClusterMover.cc @@ -17,19 +17,41 @@ namespace { - [[maybe_unused]] std::ostream& operator << (std::ostream& out, const Acts::Vector3& v ) + /** + * @brief Writes an Acts::Vector3 to an output stream as a parenthesized, comma-separated tuple. + * + * Formats the vector as "(x, y, z)" where x, y, and z are the vector components. + * + * @param out Output stream to write to. + * @param v Vector whose components will be formatted. + * @return std::ostream& Reference to the same output stream to allow chaining. + */ + [[maybe_unused]] std::ostream& operator<<(std::ostream& out, const Acts::Vector3& v) { out << "(" << v.x() << ", " << v.y() << ", " << v.z() << ")"; return out; } -} +} /** + * @brief Initialize spacings and nominal radii for TPC readout layers. + * + * Computes the radial spacing for the inner, mid, and outer TPC regions and + * populates the layer_radius array with the nominal radius (layer center) + * for 48 TPC layers (16 layers per region). + * + * The spacing for each region is derived from its configured min/max radii, + * and each region's 16 layer entries are set to the layer center: + * - layer_radius[0..15] : inner region (from inner_tpc_min_radius) + * - layer_radius[16..31] : mid region (from mid_tpc_min_radius) + * - layer_radius[32..47] : outer region (from outer_tpc_min_radius) + */ TpcClusterMover::TpcClusterMover() + : inner_tpc_spacing((mid_tpc_min_radius - inner_tpc_min_radius) / 16.0) + , mid_tpc_spacing((outer_tpc_min_radius - mid_tpc_min_radius) / 16.0) + , outer_tpc_spacing((outer_tpc_max_radius - outer_tpc_min_radius) / 16.0) { // initialize layer radii - inner_tpc_spacing = (mid_tpc_min_radius - inner_tpc_min_radius) / 16.0; - mid_tpc_spacing = (outer_tpc_min_radius - mid_tpc_min_radius) / 16.0; - outer_tpc_spacing = (outer_tpc_max_radius - outer_tpc_min_radius) / 16.0; + for (int i = 0; i < 16; ++i) { layer_radius[i] = inner_tpc_min_radius + (double) i * inner_tpc_spacing + 0.5 * inner_tpc_spacing; @@ -44,7 +66,15 @@ TpcClusterMover::TpcClusterMover() } } -void TpcClusterMover::initialize_geometry(PHG4TpcGeomContainer *cellgeo) +/** + * @brief Populate the internal TPC layer radii from a geometry container. + * + * Updates this object's layer_radius[] entries with the radius values taken + * from the provided PHG4TpcGeomContainer in the container's iteration order. + * + * @param cellgeo Geometry container providing per-layer radius information. + */ +void TpcClusterMover::initialize_geometry(PHG4TpcGeomContainer* cellgeo) { if (_verbosity > 0) { @@ -62,10 +92,20 @@ void TpcClusterMover::initialize_geometry(PHG4TpcGeomContainer *cellgeo) } } -//____________________________________________________________________________.. +/** + * @brief Move TPC clusters on a track radially to their readout-layer surface positions. + * + * Fits a circle to the track's TPC cluster XY positions and a line to z versus radius, then projects each TPC + * cluster along the fitted circle from its current radius to the layer's configured readout radius and adjusts z + * according to the fitted line. Non-TPC clusters are preserved unchanged. + * + * @return std::vector> A vector of cluster keys and their updated global + * positions. Non-TPC clusters appear exactly as in the input. TPC clusters are replaced by their projected positions; + * TPC clusters for which geometric intersection fails are omitted. If the track contains fewer than three TPC clusters, + * the original input vector is returned unchanged. + */ std::vector> TpcClusterMover::processTrack(const std::vector>& global_in) { - // Get the global positions of the TPC clusters for this track, already corrected for distortions, and move them to the surfaces // The input object contains all clusters for the track @@ -74,7 +114,7 @@ std::vector> TpcClusterMover::proces std::vector tpc_global_vec; std::vector tpc_cluskey_vec; - for (const auto& [ckey,global]:global_in) + for (const auto& [ckey, global] : global_in) { const auto trkrid = TrkrDefs::getTrkrId(ckey); if (trkrid == TrkrDefs::tpcId) @@ -85,7 +125,7 @@ std::vector> TpcClusterMover::proces else { // si clusters stay where they are - global_moved.emplace_back(ckey,global); + global_moved.emplace_back(ckey, global); } } @@ -158,7 +198,25 @@ std::vector> TpcClusterMover::proces return global_moved; } -int TpcClusterMover::get_circle_circle_intersection(double target_radius, double R, double X0, double Y0, double xclus, double yclus, double &x, double &y) +/** + * @brief Compute the intersection point between a fitted circle and a cylinder at a given radius. + * + * Selects the intersection point (x,y) on the circle defined by (R, X0, Y0) that is closest to the cluster's + * current (xclus, yclus). If the geometric intersection computation fails, the call indicates the cluster should + * be skipped. + * + * @param target_radius Radius of the cylinder (distance from z-axis) to intersect with. + * @param R Radius of the fitted circle. + * @param X0 X-coordinate of the fitted circle center. + * @param Y0 Y-coordinate of the fitted circle center. + * @param xclus X-coordinate of the cluster's current position (used to choose the correct intersection). + * @param yclus Y-coordinate of the cluster's current position (used to choose the correct intersection). + * @param[out] x Chosen intersection X-coordinate (set on success). + * @param[out] y Chosen intersection Y-coordinate (set on success). + * @return int Fun4AllReturnCodes::EVENT_OK on success; Fun4AllReturnCodes::ABORTEVENT if the intersection + * calculation failed and the cluster should be skipped. + */ +int TpcClusterMover::get_circle_circle_intersection(double target_radius, double R, double X0, double Y0, double xclus, double yclus, double& x, double& y) const { // finds the intersection of the fitted circle with the cylinder having radius = target_radius const auto [xplus, yplus, xminus, yminus] = TrackFitUtils::circle_circle_intersection(target_radius, R, X0, Y0); @@ -188,4 +246,4 @@ int TpcClusterMover::get_circle_circle_intersection(double target_radius, double y = yminus; } return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcClusterMover.h b/offline/packages/tpc/TpcClusterMover.h index dc67312f7f..0236f768d5 100644 --- a/offline/packages/tpc/TpcClusterMover.h +++ b/offline/packages/tpc/TpcClusterMover.h @@ -12,6 +12,40 @@ class PHG4TpcGeomContainer; +/** + * Construct a TpcClusterMover. + */ + +/** + * Set the internal verbosity level for diagnostic output. + * @param verb Verbosity level (higher values increase detail). + */ + +/** + * Project track cluster positions onto the TPC readout surface. + * @param global_in Vector of pairs (cluster key, global position/orientation) representing input cluster coordinates. + * @returns Vector of pairs with the same cluster keys and transformed positions on the readout surface. + */ + +/** + * Update the internal geometry parameters to match the provided TPC cell geometry container. + * @param cellgeo Pointer to the PHG4TpcGeomContainer containing the TPC cell geometry to use. + */ + +/** + * Compute an intersection point between two circles: one centered at (X0,Y0) with radius R, + * and the circle centered at (xclus,yclus) with radius inferred from target_radius. + * The intersection coordinates are written to x and y. + * @param target_radius Target radius used for determining the intersection circle. + * @param R Radius of the first circle centered at (X0, Y0). + * @param X0 X coordinate of the first circle center. + * @param Y0 Y coordinate of the first circle center. + * @param xclus X coordinate of the second circle center (cluster position). + * @param yclus Y coordinate of the second circle center (cluster position). + * @param x Output X coordinate of the intersection point. + * @param y Output Y coordinate of the intersection point. + * @returns `0` on success (intersection found), non-zero on failure (no intersection or error). + */ class TpcClusterMover { public: @@ -27,7 +61,7 @@ class TpcClusterMover void initialize_geometry(PHG4TpcGeomContainer *cellgeo); private: - int get_circle_circle_intersection(double target_radius, double R, double X0, double Y0, double xclus, double yclus, double &x, double &y); + int get_circle_circle_intersection(double target_radius, double R, double X0, double Y0, double xclus, double yclus, double &x, double &y) const; double _z_start = 0.0; double _y_start = 0.0; @@ -50,4 +84,4 @@ class TpcClusterMover int _verbosity = 0; }; -#endif +#endif \ No newline at end of file diff --git a/offline/packages/tpc/TpcClusterizer.cc b/offline/packages/tpc/TpcClusterizer.cc index bd5fc3c340..28ed6f2580 100644 --- a/offline/packages/tpc/TpcClusterizer.cc +++ b/offline/packages/tpc/TpcClusterizer.cc @@ -25,7 +25,6 @@ #include #include #include -#include #include #include // for SubsysReco @@ -50,6 +49,7 @@ #include +#include #include #include // for sqrt, cos, sin #include @@ -64,7 +64,14 @@ namespace { template - inline constexpr T square(const T &x) + /** + * @brief Compute the square of a value. + * + * @tparam T Type that supports multiplication (operator*) and copy/const reference. + * @param x Value to square. + * @return T The product of `x` and `x`. + */ + constexpr T square(const T &x) { return x * x; } @@ -357,6 +364,20 @@ namespace return; } + /** + * @brief Determines whether a hit at the given phi and time bins is isolated. + * + * Checks the immediate 3x3 neighborhood around (iphi, it) (clamped at array edges) and + * evaluates whether the sum of ADC values in the neighboring bins (excluding the center) + * is zero. + * + * @param iphi Phi bin index of the hit. + * @param it Time bin index of the hit. + * @param NPhiBinsMax Total number of phi bins (used for edge clamping). + * @param NTBinsMax Total number of time bins (used for edge clamping). + * @param adcval 2D array of ADC values indexed as adcval[iphi][it]. + * @return int `1` if all neighboring bins (excluding the center) have a summed ADC of zero, `0` otherwise. + */ int is_hit_isolated(int iphi, int it, int NPhiBinsMax, int NTBinsMax, const std::vector> &adcval) { // check isolated hits @@ -365,20 +386,14 @@ namespace int isosum = 0; int isophimin = iphi - 1; - if (isophimin < 0) - { - isophimin = 0; - } + isophimin = std::max(isophimin, 0); int isophimax = iphi + 1; if (!(isophimax < NPhiBinsMax)) { isophimax = NPhiBinsMax - 1; } int isotmin = it - 1; - if (isotmin < 0) - { - isotmin = 0; - } + isotmin = std::max(isotmin, 0); int isotmax = it + 1; if (!(isotmax < NTBinsMax)) { @@ -465,6 +480,22 @@ namespace return; } + /** + * @brief Compute cluster properties from a list of hits and store the resulting cluster and metadata. + * + * Calculates weighted cluster position, errors, and geometry-dependent local coordinates from the provided hit list. + * If the cluster passes size and charge thresholds and a valid TPC surface is found, a TrkrCluster is created and + * appended to my_data.cluster_vector; hit associations are appended to my_data.association_vector when enabled. + * Optionally produces per-cluster verbose hit summaries and training hit data when those features are enabled. + * + * @param iphi_center Phi-bin index of the cluster seed (in local bin coordinates before phioffset). + * @param it_center Time-bin index of the cluster seed (in local bin coordinates before toffset). + * @param ihit_list Vector of hits (iphi, it, adc, edge) that belong to the cluster (local bin coordinates). + * @param my_data Per-thread clustering context and output containers; updated with any created cluster, associations, + * verbose hit vectors, and training hits. + * @param ntouch Number of overlapping pads (overlap count) for the cluster; stored on the created cluster. + * @param nedge Number of edge pads in the cluster; stored on the created cluster. + */ void calc_cluster_parameter(const int iphi_center, const int it_center, const std::vector &ihit_list, thread_data &my_data, int ntouch, int nedge) { @@ -537,30 +568,11 @@ namespace continue; } - if (adc > max_adc) - { - max_adc = adc; - } - - if (iphi > phibinhi) - { - phibinhi = iphi; - } - - if (iphi < phibinlo) - { - phibinlo = iphi; - } - - if (it > tbinhi) - { - tbinhi = it; - } - - if (it < tbinlo) - { - tbinlo = it; - } + max_adc = std::max(max_adc, static_cast(std::round(adc))); // preserves rounding (0.5 -> 1) + phibinhi = std::max(iphi, phibinhi); + phibinlo = std::min(iphi, phibinlo); + tbinhi = std::max(it, tbinhi); + tbinlo = std::min(it, tbinlo); // if(it==it_center){ yg_sum += adc; } // update phi sums @@ -686,7 +698,7 @@ namespace // std::cout << "clus num" << my_data.cluster_vector.size() << " X " << local(0) << " Y " << clust << std::endl; if (sqrt(phi_err_square) > my_data.min_err_squared) { - auto clus = new TrkrClusterv5; + auto *clus = new TrkrClusterv5; // auto clus = std::make_unique(); clus_base = clus; clus->setAdc(adc_sum); @@ -738,19 +750,19 @@ namespace if (my_data.fillClusHitsVerbose && b_made_cluster) { // push the data back to - my_data.phivec_ClusHitsVerbose.push_back(std::vector>{}); - my_data.zvec_ClusHitsVerbose.push_back(std::vector>{}); + my_data.phivec_ClusHitsVerbose.emplace_back(); + my_data.zvec_ClusHitsVerbose.emplace_back(); auto &vphi = my_data.phivec_ClusHitsVerbose.back(); auto &vz = my_data.zvec_ClusHitsVerbose.back(); for (auto &entry : m_phi) { - vphi.push_back({entry.first, entry.second}); + vphi.emplace_back(entry.first, entry.second); } for (auto &entry : m_z) { - vz.push_back({entry.first, entry.second}); + vz.emplace_back(entry.first, entry.second); } } @@ -777,6 +789,19 @@ namespace // std::cout << "done calc" << std::endl; } + /** + * @brief Find and build TPC clusters for a single sector or raw hitset. + * + * Processes hits from the provided thread_data (either digital TrkrHitSet or RawHitSet), + * applies pedestal subtraction, thresholds, optional wedge emulation and fixed-window logic, + * groups neighboring hits into clusters, computes cluster parameters, and records results. + * + * @param my_data Per-thread context containing input hitset/rawhitset, geometry and + * clustering configuration. On return the function appends constructed + * clusters to my_data->cluster_vector and hit-to-cluster associations to + * my_data->assoc_vector; it may also populate training hits and verbose + * outputs if those options are enabled in my_data. + */ void ProcessSectorData(thread_data *my_data) { const auto &pedestal = my_data->pedestal; @@ -875,7 +900,7 @@ namespace } if (adc > my_data->edge_threshold) { - adcval[phibin][tbin] = (unsigned short) adc; + adcval[phibin][tbin] = adc; } } } @@ -967,7 +992,7 @@ namespace } */ // std::cout << "done filling " << std::endl; - while (all_hit_map.size() > 0) + while (!all_hit_map.empty()) { // std::cout << "all hit map size: " << all_hit_map.size() << std::endl; auto iter = all_hit_map.rbegin(); @@ -1013,22 +1038,10 @@ namespace { continue; } - if (wiphi > wphibinhi) - { - wphibinhi = wiphi; - } - if (wiphi < wphibinlo) - { - wphibinlo = wiphi; - } - if (wit > wtbinhi) - { - wtbinhi = wit; - } - if (wit < wtbinlo) - { - wtbinlo = wit; - } + wphibinhi = std::max(wiphi, wphibinhi); + wphibinlo = std::min(wiphi, wphibinlo); + wtbinhi = std::max(wit, wtbinhi); + wtbinlo = std::min(wit, wtbinlo); } char wtsize = wtbinhi - wtbinlo + 1; char wphisize = wphibinhi - wphibinlo + 1; @@ -1075,9 +1088,18 @@ namespace */ // pthread_exit(nullptr); } + /** + * @brief Thread entry point that runs clustering for a single TPC sector. + * + * Interprets the provided argument as a pointer to thread_data, executes ProcessSectorData + * for that thread's context, and then terminates the thread. + * + * @param threadarg Pointer to a thread_data instance describing the sector and runtime context. + * @return nullptr (thread exits via pthread_exit). + */ void *ProcessSector(void *threadarg) { - auto my_data = static_cast(threadarg); + auto *my_data = static_cast(threadarg); ProcessSectorData(my_data); pthread_exit(nullptr); } @@ -1120,6 +1142,18 @@ bool TpcClusterizer::is_in_sector_boundary(int phibin, int sector, PHG4TpcGeom * return reject_it; } +/** + * @brief Initialize run resources for the TPC clusterizer and ensure required DST nodes exist. + * + * Ensures the DST node contains TRKR data nodes required by the clusterizer (TRKR_CLUSTER, + * TRKR_CLUSTERHITASSOC, and TRAINING_HITSET) and creates them if missing. Optionally loads + * a TorchScript neural-network model when neural-net mode is enabled, prepares verbose-hit + * output storage if requested, and retrieves TPC geometry (used to set timing/ADC parameters). + * + * @param topNode Top-level PHENIX node from which DST and other nodes are located. + * @return int `Fun4AllReturnCodes::EVENT_OK` on successful initialization; `Fun4AllReturnCodes::ABORTRUN` + * if required nodes (DST or TPC geometry) are missing or cannot be accessed. + */ int TpcClusterizer::InitRun(PHCompositeNode *topNode) { PHNodeIterator iter(topNode); @@ -1133,7 +1167,7 @@ int TpcClusterizer::InitRun(PHCompositeNode *topNode) } // Create the Cluster node if required - auto trkrclusters = findNode::getClass(dstNode, "TRKR_CLUSTER"); + auto *trkrclusters = findNode::getClass(dstNode, "TRKR_CLUSTER"); if (!trkrclusters) { PHNodeIterator dstiter(dstNode); @@ -1151,7 +1185,7 @@ int TpcClusterizer::InitRun(PHCompositeNode *topNode) DetNode->addNode(TrkrClusterContainerNode); } - auto clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); + auto *clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); if (!clusterhitassoc) { PHNodeIterator dstiter(dstNode); @@ -1168,7 +1202,7 @@ int TpcClusterizer::InitRun(PHCompositeNode *topNode) DetNode->addNode(newNode); } - auto training_container = findNode::getClass(dstNode, "TRAINING_HITSET"); + auto *training_container = findNode::getClass(dstNode, "TRAINING_HITSET"); if (!training_container) { PHNodeIterator dstiter(dstNode); @@ -1217,18 +1251,18 @@ int TpcClusterizer::InitRun(PHCompositeNode *topNode) if (!mClusHitsVerbose) { PHNodeIterator dstiter(dstNode); - auto DetNode = dynamic_cast(dstiter.findFirst("PHCompositeNode", "TRKR")); + auto *DetNode = dynamic_cast(dstiter.findFirst("PHCompositeNode", "TRKR")); if (!DetNode) { DetNode = new PHCompositeNode("TRKR"); dstNode->addNode(DetNode); } mClusHitsVerbose = new ClusHitsVerbosev1(); - auto newNode = new PHIODataNode(mClusHitsVerbose, "Trkr_SvtxClusHitsVerbose", "PHObject"); + auto *newNode = new PHIODataNode(mClusHitsVerbose, "Trkr_SvtxClusHitsVerbose", "PHObject"); DetNode->addNode(newNode); } } - auto geom = + auto *geom = findNode::getClass(topNode, "TPCGEOMCONTAINER"); if (!geom) { @@ -1239,18 +1273,31 @@ int TpcClusterizer::InitRun(PHCompositeNode *topNode) AdcClockPeriod = geom->GetFirstLayerCellGeom()->get_zstep(); std::cout << "FirstLayerCellGeomv1 streamer: " << std::endl; - auto *g1 = (PHG4TpcGeomv1*) geom->GetFirstLayerCellGeom(); // cast because << not in the base class + auto *g1 = static_cast (geom->GetFirstLayerCellGeom()); // cast because << not in the base class std::cout << *g1 << std::endl; std::cout << "LayerCellGeomv1 streamer for layer 24: " << std::endl; - auto *g2 = (PHG4TpcGeomv1*) geom->GetLayerCellGeom(24); // cast because << not in the base class + auto *g2 = static_cast (geom->GetLayerCellGeom(24)); // cast because << not in the base class std::cout << *g2 << std::endl; std::cout << "LayerCellGeomv1 streamer for layer 40: " << std::endl; - auto *g3 = (PHG4TpcGeomv1*) geom->GetLayerCellGeom(40); // cast because << not in the base class + auto *g3 = static_cast (geom->GetLayerCellGeom(40)); // cast because << not in the base class std::cout << *g3 << std::endl; return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Process a single event: perform TPC clustering and attach clusters and associations to DST nodes. + * + * Temporarily disables alignment transforms, partitions TPC hits (digitized or raw) by readout hitset, + * runs per-hitset clustering (multi-threaded unless configured sequential), copies generated clusters and + * cluster-hit associations into the TRKR_CLUSTER and TRKR_CLUSTERHITASSOC nodes, optionally records + * training hits and verbose per-cluster info, and re-enables alignment transforms before returning. + * + * @param topNode Top-level node of the event node tree used to find input hit containers, geometry, + * and output containers. + * @return int Fun4All return code: `EVENT_OK` on success; `ABORTRUN` if required nodes (DST, hit containers, + * geometry, cluster or association containers, or training container) are missing or invalid. + */ int TpcClusterizer::process_event(PHCompositeNode *topNode) { // The TPC is the only subsystem that clusters in global coordinates. For consistency, @@ -1489,18 +1536,18 @@ int TpcClusterizer::process_event(PHCompositeNode *topNode) const auto ckey = TrkrDefs::genClusKey(hitsetkey, index); // get cluster - auto cluster = data.cluster_vector[index]; + auto *cluster = data.cluster_vector[index]; // insert in map m_clusterlist->addClusterSpecifyKey(ckey, cluster); if (mClusHitsVerbose) { - for (auto &hit : data.phivec_ClusHitsVerbose[index]) + for (const auto &hit : data.phivec_ClusHitsVerbose[index]) { mClusHitsVerbose->addPhiHit(hit.first, hit.second); } - for (auto &hit : data.zvec_ClusHitsVerbose[index]) + for (const auto &hit : data.zvec_ClusHitsVerbose[index]) { mClusHitsVerbose->addZHit(hit.first, hit.second); } @@ -1624,7 +1671,7 @@ int TpcClusterizer::process_event(PHCompositeNode *topNode) const auto ckey = TrkrDefs::genClusKey(hitsetkey, index); // get cluster - auto cluster = data.cluster_vector[index]; + auto *cluster = data.cluster_vector[index]; // insert in map m_clusterlist->addClusterSpecifyKey(ckey, cluster); @@ -1668,7 +1715,7 @@ int TpcClusterizer::process_event(PHCompositeNode *topNode) const auto ckey = TrkrDefs::genClusKey(hitsetkey, index); // get cluster - auto cluster = data.cluster_vector[index]; + auto *cluster = data.cluster_vector[index]; // insert in map // std::cout << "X: " << cluster->getLocalX() << "Y: " << cluster->getLocalY() << std::endl; @@ -1676,11 +1723,11 @@ int TpcClusterizer::process_event(PHCompositeNode *topNode) if (mClusHitsVerbose) { - for (auto &hit : data.phivec_ClusHitsVerbose[index]) + for (const auto &hit : data.phivec_ClusHitsVerbose[index]) { mClusHitsVerbose->addPhiHit(hit.first, (float) hit.second); } - for (auto &hit : data.zvec_ClusHitsVerbose[index]) + for (const auto &hit : data.zvec_ClusHitsVerbose[index]) { mClusHitsVerbose->addZHit(hit.first, (float) hit.second); } @@ -1698,7 +1745,7 @@ int TpcClusterizer::process_event(PHCompositeNode *topNode) m_clusterhitassoc->addAssoc(ckey, hkey); } - for (auto v_hit : thread_pair.data.v_hits) + for (auto *v_hit : thread_pair.data.v_hits) { if (_store_hits) { @@ -1738,4 +1785,4 @@ int TpcClusterizer::process_event(PHCompositeNode *topNode) int TpcClusterizer::End(PHCompositeNode * /*topNode*/) { return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcCombinedRawDataUnpacker.cc b/offline/packages/tpc/TpcCombinedRawDataUnpacker.cc index c194567d58..8c8bd1ee96 100644 --- a/offline/packages/tpc/TpcCombinedRawDataUnpacker.cc +++ b/offline/packages/tpc/TpcCombinedRawDataUnpacker.cc @@ -53,11 +53,19 @@ TpcCombinedRawDataUnpacker::~TpcCombinedRawDataUnpacker() { delete m_cdbttree; } +/** + * @brief Enable zero-suppressed data handling and load ADU thresholds. + * + * Enables zero-suppression emulation, disables baseline correction, and loads + * per-region ADU thresholds into m_zs_threshold[0..2] from the CDB entry + * identified by "TPC_ZS_THRESHOLDS". If the CDB URL is empty, the function + * leaves existing thresholds unchanged (the codebase default is 20 ADU). + */ void TpcCombinedRawDataUnpacker::ReadZeroSuppressedData() { m_do_zs_emulation = true; m_do_baseline_corr = false; - auto cdb = CDBInterface::instance(); + auto *cdb = CDBInterface::instance(); std::string dir = cdb->getUrl("TPC_ZS_THRESHOLDS"); auto cdbtree = std::make_unique(dir); @@ -75,7 +83,7 @@ void TpcCombinedRawDataUnpacker::ReadZeroSuppressedData() { name.str(""); name << "R"<GetSingleFloatValue(name.str().c_str()); + m_zs_threshold[i] = cdbtree->GetSingleFloatValue(name.str()); if(Verbosity() > 1) { std::cout << "Loading ADU threshold of " << m_zs_threshold[i] << " for region " << i << std::endl; @@ -700,4 +708,4 @@ int TpcCombinedRawDataUnpacker::End(PHCompositeNode* /*topNode*/) // if(m_Debug==1) hm->dumpHistos(m_filename, "RECREATE"); return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcCombinedRawDataUnpackerDebug.cc b/offline/packages/tpc/TpcCombinedRawDataUnpackerDebug.cc index 0bb412197f..fce1638de2 100644 --- a/offline/packages/tpc/TpcCombinedRawDataUnpackerDebug.cc +++ b/offline/packages/tpc/TpcCombinedRawDataUnpackerDebug.cc @@ -34,6 +34,7 @@ #include #include +#include #include #include // for exit #include // for exit @@ -169,6 +170,16 @@ int TpcCombinedRawDataUnpackerDebug::InitRun(PHCompositeNode* topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Unpacks TPC raw hits for the current event, applies pedestal and baseline corrections (optional zero-suppression and noise rejection), populates TRKR hit sets, and fills diagnostic TNtuples/histograms when enabled. + * + * The method iterates all TPC raw hits from the TpcRawHitContainer, computes geometry keys (layer/phi/tbin), determines per-channel pedestals and widths (or uses emulated ZS values), applies optional baseline correction and noise rejection, creates or updates TrkrHit entries in the TRKR_HITSET container, and accumulates per-FEE ADC histograms used for local baseline estimation. When baseline correction is enabled a second pass applies per-FEE time-dependent corrections to hits. Diagnostic TNtuples and ROOT histograms are filled if configured. + * + * @param topNode Pointer to the PHCompositeNode root of the current event node tree (used to find/attach TRKR_HITSET, TpcRawHitContainer, and geometry/calibration nodes). + * @return Fun4AllReturnCodes::EVENT_OK on successful processing of the event; + * Fun4AllReturnCodes::DISCARDEVENT when the event is intentionally skipped (out of configured event range) or on certain node-missing error exits; + * Fun4AllReturnCodes::ABORTRUN if required geometry node (TPCGEOMCONTAINER) is missing. + */ int TpcCombinedRawDataUnpackerDebug::process_event(PHCompositeNode* topNode) { if (_ievent < startevt || _ievent > endevt) @@ -234,14 +245,8 @@ int TpcCombinedRawDataUnpackerDebug::process_event(PHCompositeNode* topNode) TpcRawHit* tpchit = tpccont->get_hit(i); uint64_t gtm_bco = tpchit->get_gtm_bco(); - if (gtm_bco < bco_min) - { - bco_min = gtm_bco; - } - if (gtm_bco > bco_max) - { - bco_max = gtm_bco; - } + bco_min = std::min(gtm_bco, bco_min); + bco_max = std::max(gtm_bco, bco_max); int fee = tpchit->get_fee(); int channel = tpchit->get_channel(); @@ -539,7 +544,7 @@ int TpcCombinedRawDataUnpackerDebug::process_event(PHCompositeNode* topNode) for (int binx = 1; binx < hist2d->GetNbinsX(); binx++) { - double timebin = ((TAxis*) hist2d->GetXaxis())->GetBinCenter(binx); + double timebin = ( hist2d->GetXaxis())->GetBinCenter(binx); std::string histname1d = "h" + std::to_string(hiter.first) + "_" + std::to_string((int) timebin); TH1D* hist1d = hist2d->ProjectionY(histname1d.c_str(), binx, binx); float local_ped = 0; @@ -697,11 +702,8 @@ int TpcCombinedRawDataUnpackerDebug::process_event(PHCompositeNode* topNode) if ((float(adc) - pedestal_offset - corr) > (hpedwidth2 * m_ped_sig_cut)) { float nuadc = (float(adc) - corr - pedestal_offset); - if (nuadc < 0) - { - nuadc = 0; - } - hitr->second->setAdc(float(nuadc)); + nuadc = std::max(nuadc, 0); + hitr->second->setAdc(nuadc); #ifdef DEBUG // hitr->second->setAdc(10); if (tbin == 383 && layer >= 7 + 32 && fee == 21) @@ -788,4 +790,4 @@ int TpcCombinedRawDataUnpackerDebug::End(PHCompositeNode* /*topNode*/) // if(m_Debug==1) hm->dumpHistos(m_filename, "RECREATE"); return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcDistortionCorrection.cc b/offline/packages/tpc/TpcDistortionCorrection.cc index b759331e77..1b7d96617a 100644 --- a/offline/packages/tpc/TpcDistortionCorrection.cc +++ b/offline/packages/tpc/TpcDistortionCorrection.cc @@ -15,7 +15,14 @@ namespace { template - inline constexpr T square(const T& x) + /** + * @brief Computes the square of a value. + * + * @tparam T Numeric type of the input. + * @param x Value to be squared. + * @return T The result of x multiplied by itself. + */ + constexpr T square(const T x) { return x * x; } @@ -137,4 +144,4 @@ if(dcc->m_use_scalefactor) const auto y_new = r_new * std::sin(phi_new); return {x_new, y_new, z_new}; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcLoadDistortionCorrection.cc b/offline/packages/tpc/TpcLoadDistortionCorrection.cc index 0ce2ffa4e1..3a6a367861 100644 --- a/offline/packages/tpc/TpcLoadDistortionCorrection.cc +++ b/offline/packages/tpc/TpcLoadDistortionCorrection.cc @@ -45,7 +45,20 @@ TpcLoadDistortionCorrection::TpcLoadDistortionCorrection(const std::string& name { } -//_____________________________________________________________________ +/** + * @brief Loads TPC distortion correction histograms from files and attaches containers to the node tree. + * + * Initializes TpcDistortionCorrectionContainer nodes under the RUN node for each enabled correction, + * reads the corresponding ROOT histograms (negative/positive z) from the configured files, and configures + * each container's properties (dimensions, phi units, z-interpolation, and scale factor). + * + * @param topNode Top-level node of the framework's node tree used to find or create RUN and correction nodes. + * @return int Fun4All return code: `Fun4AllReturnCodes::ABORTRUN` if the RUN node is missing, `Fun4AllReturnCodes::EVENT_OK` on success. + * + * Side effects: + * - Creates PHDataNode entries under RUN for enabled corrections when missing. + * - Terminates the process via `exit(1)` if a configured distortion file cannot be opened. + */ int TpcLoadDistortionCorrection::InitRun(PHCompositeNode* topNode) { // look for distortion calibration object @@ -58,7 +71,7 @@ int TpcLoadDistortionCorrection::InitRun(PHCompositeNode* topNode) } /// Get the RUN node and check - auto runNode = dynamic_cast(iter.findFirst("PHCompositeNode", "RUN")); + auto *runNode = dynamic_cast(iter.findFirst("PHCompositeNode", "RUN")); if (!runNode) { std::cout << "TpcLoadDistortionCorrection::InitRun - RUN Node missing, quitting" << std::endl; @@ -74,17 +87,17 @@ int TpcLoadDistortionCorrection::InitRun(PHCompositeNode* topNode) } // get distortion correction object and create if not found - auto distortion_correction_object = findNode::getClass(topNode, m_node_name[i]); + auto *distortion_correction_object = findNode::getClass(topNode, m_node_name[i]); if (!distortion_correction_object) { std::cout << "TpcLoadDistortionCorrection::InitRun - creating TpcDistortionCorrectionContainer in node " << m_node_name[i] << std::endl; distortion_correction_object = new TpcDistortionCorrectionContainer; - auto node = new PHDataNode(distortion_correction_object, m_node_name[i]); + auto *node = new PHDataNode(distortion_correction_object, m_node_name[i]); runNode->addNode(node); } std::cout << "TpcLoadDistortionCorrection::InitRun - reading corrections from " << m_correction_filename[i] << std::endl; - auto distortion_tfile = TFile::Open(m_correction_filename[i].c_str()); + auto *distortion_tfile = TFile::Open(m_correction_filename[i].c_str()); if (!distortion_tfile) { std::cout << "TpcLoadDistortionCorrection::InitRun - cannot open " << m_correction_filename[i] << std::endl; @@ -137,4 +150,4 @@ int TpcLoadDistortionCorrection::InitRun(PHCompositeNode* topNode) int TpcLoadDistortionCorrection::process_event(PHCompositeNode* /*unused*/) { return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcRawDataTree.cc b/offline/packages/tpc/TpcRawDataTree.cc index 6199fb7425..00372e3379 100644 --- a/offline/packages/tpc/TpcRawDataTree.cc +++ b/offline/packages/tpc/TpcRawDataTree.cc @@ -1,4 +1,3 @@ - #include "TpcRawDataTree.h" #include @@ -27,7 +26,18 @@ TpcRawDataTree::TpcRawDataTree(const std::string &name) M.setMapNames("AutoPad-R1-RevA.sch.ChannelMapping.csv", "AutoPad-R2-RevA-Pads.sch.ChannelMapping.csv", "AutoPad-R3-RevA.sch.ChannelMapping.csv"); } -//____________________________________________________________________________.. +/** + * @brief Prepare run-dependent output structures and open the output ROOT file. + * + * Initializes run-specific state derived from the configured filename, opens and + * asserts the output ROOT file, and creates the TTrees, branches, and QA + * histograms used to record packets, waveforms, and tagger entries for the run. + * + * Side effects: opens and writes to m_file, allocates TTrees and TH1/TH2 objects, + * and configures branch buffers referenced by member variables. + * + * @return Fun4AllReturnCodes::EVENT_OK on successful initialization. + */ int TpcRawDataTree::InitRun(PHCompositeNode * /*unused*/) { sectorNum = m_fname; @@ -61,7 +71,7 @@ int TpcRawDataTree::InitRun(PHCompositeNode * /*unused*/) m_SampleTree->Branch("nWaveormInFrame", &m_nWaveormInFrame, "nWaveormInFrame/I"); m_SampleTree->Branch("maxFEECount", &m_maxFEECount, "maxFEECount/I"); m_SampleTree->Branch("nSamples", &m_nSamples, "nSamples/I"); - m_SampleTree->Branch("adcSamples", &m_adcSamples[0], "adcSamples[nSamples]/s"); + m_SampleTree->Branch("adcSamples", m_adcSamples.data(), "adcSamples[nSamples]/s"); m_SampleTree->Branch("fee", &m_fee, "fee/I"); m_SampleTree->Branch("sampaAddress", &m_sampaAddress, "sampaAddress/I"); m_SampleTree->Branch("sampaChannel", &m_sampaChannel, "sampaChannel/I"); @@ -279,4 +289,4 @@ int TpcRawDataTree::End(PHCompositeNode * /*topNode*/) m_file->Close(); return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcRawWriter.cc b/offline/packages/tpc/TpcRawWriter.cc index 5d7711e2bb..53c4556fbd 100644 --- a/offline/packages/tpc/TpcRawWriter.cc +++ b/offline/packages/tpc/TpcRawWriter.cc @@ -59,6 +59,14 @@ TpcRawWriter::TpcRawWriter(const std::string &name) std::cout << PHWHERE << "Construct TpcRawWriter" << std::endl; } +/** + * @brief Prepare DST node tree and ensure tracker cluster, cluster-hit association, and raw hitset containers exist. + * + * Ensures the DST/TRKR subtree contains TRKR_CLUSTER, TRKR_CLUSTERHITASSOC, and TRKR_RAWHITSET containers, creating the TRKR composite node and the missing containers if necessary so subsequent event processing can store clusters, cluster–hit associations, and raw hit sets. + * + * @param topNode Root of the PHOOL node tree to inspect and modify. + * @return int Fun4AllReturnCodes::EVENT_OK on success, Fun4AllReturnCodes::ABORTRUN if the DST node is missing. + */ int TpcRawWriter::InitRun(PHCompositeNode *topNode) { if (topNode) @@ -76,7 +84,7 @@ int TpcRawWriter::InitRun(PHCompositeNode *topNode) } // Create the Cluster node if required - auto trkrclusters = findNode::getClass(dstNode, "TRKR_CLUSTER"); + auto *trkrclusters = findNode::getClass(dstNode, "TRKR_CLUSTER"); if (!trkrclusters) { PHNodeIterator dstiter(dstNode); @@ -94,7 +102,7 @@ int TpcRawWriter::InitRun(PHCompositeNode *topNode) DetNode->addNode(TrkrClusterContainerNode); } - auto clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); + auto *clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); if (!clusterhitassoc) { PHNodeIterator dstiter(dstNode); @@ -116,7 +124,7 @@ int TpcRawWriter::InitRun(PHCompositeNode *topNode) if (!m_rawhits) { PHNodeIterator dstiter(dstNode); - auto DetNode = dynamic_cast(dstiter.findFirst("PHCompositeNode", "TRKR")); + auto *DetNode = dynamic_cast(dstiter.findFirst("PHCompositeNode", "TRKR")); if (!DetNode) { DetNode = new PHCompositeNode("TRKR"); @@ -124,7 +132,7 @@ int TpcRawWriter::InitRun(PHCompositeNode *topNode) } m_rawhits = new RawHitSetContainerv1; - auto newNode = new PHIODataNode(m_rawhits, "TRKR_RAWHITSET", "PHObject"); + auto *newNode = new PHIODataNode(m_rawhits, "TRKR_RAWHITSET", "PHObject"); DetNode->addNode(newNode); } @@ -676,4 +684,4 @@ int TpcRawWriter::process_event(PHCompositeNode *topNode) int TpcRawWriter::End(PHCompositeNode * /*topNode*/) { return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TpcSimpleClusterizer.cc b/offline/packages/tpc/TpcSimpleClusterizer.cc index f439d1d682..570919921e 100644 --- a/offline/packages/tpc/TpcSimpleClusterizer.cc +++ b/offline/packages/tpc/TpcSimpleClusterizer.cc @@ -33,6 +33,7 @@ #include +#include #include #include // for sqrt, cos, sin #include @@ -46,7 +47,14 @@ namespace { template - inline constexpr T square(const T &x) + /** + * @brief Returns the square of a value. + * + * @tparam T Type that supports multiplication. + * @param x Value to be squared. + * @return T The result of `x * x`. + */ + constexpr T square(const T &x) { return x * x; } @@ -114,6 +122,20 @@ namespace ihit_list.push_back(thisHit); } + /** + * @brief Compute cluster properties from a list of TPC hits and record the resulting cluster. + * + * Processes the provided hit list to compute charge-weighted cluster position, error estimates, + * and local coordinates on the corresponding TPC surface, then constructs a TrkrClusterv3 + * and appends it to my_data.cluster_vector. If my_data.do_assoc is true, creates (cluster index, hitkey) + * association entries in my_data.association_vector. If the corresponding Acts surface cannot be found, + * the function returns without creating a cluster. + * + * @param ihit_list Vector of hits belonging to the cluster; each entry is (adc, (phiBin, zBin)). + * @param my_data Thread-specific context containing layer geometry, bin offsets, pedestal/parameters, + * Acts geometry pointer, identifiers (layer, side, sector), and output vectors + * (cluster_vector, association_vector) where the created cluster and associations are stored. + */ void calc_cluster_parameter(std::vector &ihit_list, thread_data &my_data) { // loop over the hits in this cluster @@ -142,22 +164,10 @@ namespace int iphi = iter.second.first + my_data.phioffset; int iz = iter.second.second + my_data.zoffset; - if (iphi > phibinhi) - { - phibinhi = iphi; - } - if (iphi < phibinlo) - { - phibinlo = iphi; - } - if (iz > zbinhi) - { - zbinhi = iz; - } - if (iz < zbinlo) - { - zbinlo = iz; - } + phibinhi = std::max(iphi, phibinhi); + phibinlo = std::min(iphi, phibinlo); + zbinhi = std::max(iz, zbinhi); + zbinlo = std::min(iz, zbinlo); // update phi sums double phi_center = my_data.layergeom->get_phicenter(iphi, my_data.side); @@ -205,7 +215,7 @@ namespace clusz -= (clusz < 0) ? my_data.par0_neg : my_data.par0_pos; // create cluster and fill - auto clus = new TrkrClusterv3; + auto *clus = new TrkrClusterv3; clus->setAdc(adc_sum); /// Get the surface key to find the surface from the map @@ -278,9 +288,22 @@ namespace } } + /** + * @brief Process hits for a single TPC sector and produce clusters. + * + * Processes the TrkrHitSet referenced by the provided thread_data, performs local + * hit-based clustering within the sector, and appends created TrkrClusterv3 + * objects and cluster-to-hit associations into the thread_data vectors. + * + * @param threadarg Pointer to a populated thread_data struct (must remain valid + * for the lifetime of this call). The function does not take + * ownership of the pointer; results are stored in + * thread_data::cluster_vector and thread_data::association_vector. + * @return nullptr + */ void *ProcessSector(void *threadarg) { - auto my_data = (struct thread_data *) threadarg; + auto *my_data = (struct thread_data *) threadarg; const auto &pedestal = my_data->pedestal; const auto &phibins = my_data->phibins; @@ -332,11 +355,11 @@ namespace all_hit_map.insert(std::make_pair(adc, thisHit)); } // adcval[phibin][zbin] = (unsigned short) adc; - adcval[phibin][zbin] = (unsigned short) adc; + adcval[phibin][zbin] = adc; } } - while (all_hit_map.size() > 0) + while (!all_hit_map.empty()) { auto iter = all_hit_map.rbegin(); if (iter == all_hit_map.rend()) @@ -400,6 +423,16 @@ bool TpcSimpleClusterizer::is_in_sector_boundary(int phibin, int sector, PHG4Tpc return reject_it; } +/** + * @brief Ensure required TRKR nodes exist under the DST node, creating them if absent. + * + * Searches the provided node tree for the DST node and ensures a TRKR/ TRKR_CLUSTER + * container and a TRKR/ TRKR_CLUSTERHITASSOC container are present; creates and + * attaches them when missing. + * + * @param topNode Top-level PHCompositeNode containing the DST node. + * @return int Fun4AllReturnCodes::EVENT_OK on success, Fun4AllReturnCodes::ABORTRUN if the DST node is missing. + */ int TpcSimpleClusterizer::InitRun(PHCompositeNode *topNode) { PHNodeIterator iter(topNode); @@ -413,7 +446,7 @@ int TpcSimpleClusterizer::InitRun(PHCompositeNode *topNode) } // Create the Cluster node if required - auto trkrclusters = findNode::getClass(dstNode, "TRKR_CLUSTER"); + auto *trkrclusters = findNode::getClass(dstNode, "TRKR_CLUSTER"); if (!trkrclusters) { PHNodeIterator dstiter(dstNode); @@ -431,7 +464,7 @@ int TpcSimpleClusterizer::InitRun(PHCompositeNode *topNode) DetNode->addNode(TrkrClusterContainerNode); } - auto clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); + auto *clusterhitassoc = findNode::getClass(topNode, "TRKR_CLUSTERHITASSOC"); if (!clusterhitassoc) { PHNodeIterator dstiter(dstNode); @@ -451,6 +484,21 @@ int TpcSimpleClusterizer::InitRun(PHCompositeNode *topNode) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Process one event: cluster TPC hits into TRKR_CLUSTER entries and create hit associations. + * + * Processes TPC hitsets from the node tree under topNode, spawns per-hitset worker threads to + * cluster hits sector-by-sector, and stores resulting clusters in the TRKR_CLUSTER container and + * cluster-to-hit associations in TRKR_CLUSTERHITASSOC. + * + * @param topNode Root of the node tree containing required input and output nodes (e.g., TRKR_HITSET, + * TPCGEOMCONTAINER, ActsGeometry) and where TRKR_CLUSTER and TRKR_CLUSTERHITASSOC are stored. + * @return int Fun4All return code: + * - Fun4AllReturnCodes::EVENT_OK on successful clustering, + * - Fun4AllReturnCodes::ABORTRUN if required nodes (DST, TRKR_HITSET, TRKR_CLUSTER, + * TRKR_CLUSTERHITASSOC, TPCGEOMCONTAINER, or ActsGeometry) are missing, + * - 1 if thread mutex initialization fails. + */ int TpcSimpleClusterizer::process_event(PHCompositeNode *topNode) { // int print_layer = 18; @@ -614,7 +662,7 @@ int TpcSimpleClusterizer::process_event(PHCompositeNode *topNode) const auto ckey = TrkrDefs::genClusKey(hitsetkey, index); // get cluster - auto cluster = data.cluster_vector[index]; + auto *cluster = data.cluster_vector[index]; // insert in map m_clusterlist->addClusterSpecifyKey(ckey, cluster); @@ -641,4 +689,4 @@ int TpcSimpleClusterizer::process_event(PHCompositeNode *topNode) int TpcSimpleClusterizer::End(PHCompositeNode * /*topNode*/) { return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/tpc/TrainingHits.cc b/offline/packages/tpc/TrainingHits.cc index 3a70db0854..9402b33049 100644 --- a/offline/packages/tpc/TrainingHits.cc +++ b/offline/packages/tpc/TrainingHits.cc @@ -1,19 +1,32 @@ #include "TrainingHits.h" +/** + * @brief Default-initializes a TrainingHits instance. + * + * Initializes numeric members (radius, phi, z, phistep, zstep) and integer + * members (layer, ntouch, nedge, cluskey) to 0 and fills the ADC buffer + * (v_adc) with zeros. + */ TrainingHits::TrainingHits() + : radius(0.) + , phi(0.) + , z(0.) + , phistep(0.) + , zstep(0.) + , layer(0) + , ntouch(0) + , nedge(0) + , cluskey(0) { v_adc.fill(0); - radius = 0.; - phi = 0.; - z = 0.; - phistep = 0.; - zstep = 0.; - layer = 0; - ntouch = 0; - nedge = 0; - cluskey = 0; } +/** + * @brief Resets the hit data to an initial empty state. + * + * Clears the ADC sample buffer and sets all positional and state members to zero. + * Specifically resets: v_adc, radius, phi, z, phistep, zstep, layer, ntouch, nedge, and cluskey. + */ void TrainingHits::Reset() { v_adc.fill(0); @@ -26,4 +39,4 @@ void TrainingHits::Reset() ntouch = 0; nedge = 0; cluskey = 0; -} +} \ No newline at end of file diff --git a/offline/packages/trackbase/AlignmentTransformation.cc b/offline/packages/trackbase/AlignmentTransformation.cc index b3eb79fcb0..2fa56b2132 100644 --- a/offline/packages/trackbase/AlignmentTransformation.cc +++ b/offline/packages/trackbase/AlignmentTransformation.cc @@ -29,6 +29,19 @@ #include #include +/** + * @brief Builds and installs detector alignment transforms from configuration. + * + * Reads alignment parameter lines (per-hitsetkey rotations and translations) from the configured + * file or database, parses each entry, optionally applies random perturbations, constructs an + * Acts::Transform3 for the corresponding surface (MVTX, INTT, TPC, Micromegas), and stores the + * resulting transforms in the persistent and transient transform containers. For TPC module + * entries the method may compute and apply a local-frame translation to account for module tilt. + * After processing all lines, the collected transform map is assigned to the geometry context and + * alignment usage is re-enabled. + * + * @param topNode Root of the PHENIX node tree used to locate required geometry and container nodes. + */ void AlignmentTransformation::createMap(PHCompositeNode* topNode) { localVerbosity = 0; @@ -277,6 +290,7 @@ void AlignmentTransformation::createMap(PHCompositeNode* topNode) surf = surfMaps.getTpcSurface(this_hitsetkey, (unsigned int) sskey); Eigen::Vector3d localFrameTranslation(0, 0, 0); + use_module_tilt = false; if (test_layer < 4 || use_module_tilt_always) { // get the local frame translation that puts the local surface center at the tilted position after the local rotations are applied @@ -285,6 +299,9 @@ void AlignmentTransformation::createMap(PHCompositeNode* topNode) double this_radius = std::sqrt(this_center[0] * this_center[0] + this_center[1] * this_center[1]); float moduleRadius = TpcModuleRadii[side][sector][this_region]; // radius of the center of the module in cm localFrameTranslation = getTpcLocalFrameTranslation(moduleRadius, this_radius, sensorAngles) * 10; // cm to mm + + // set this flag for later use + use_module_tilt = true; } Acts::Transform3 transform; @@ -355,7 +372,25 @@ void AlignmentTransformation::createMap(PHCompositeNode* topNode) alignmentTransformationContainer::use_alignment = true; } -// currently used as the transform maker +/** + * @brief Compose an Acts Transform for a detector surface using alignment parameters. + * + * Builds the final surface transform by combining the surface's nominal Acts transform + * with provided local (sensor) rotations, global (survey) rotations, local-frame + * translations, and millepede translations. The exact multiplication order depends + * on the `survey` flag, the tracker id, and runtime flags controlling module-tilt + * and silicon rotation order. + * + * @param surf The Acts surface for which the transform is produced. + * @param millepedeTranslation Translation vector read from alignment parameters (global/survey translation). + * @param sensorAngles Local sensor rotation angles (alpha, beta, gamma) applied in local sensor coordinates. + * @param localFrameTranslation Translation in the local sensor frame (e.g., module-tilt-induced offset). + * @param sensorAnglesGlobal Global/survey rotation angles applied in global coordinates. + * @param trkrid Tracker identifier (e.g., TPC vs silicon) that influences transform composition. + * @param survey If true, treat the provided parameters as survey/global parameters and compose using the survey order. + * + * @return Acts::Transform3 The composed transform to apply to the surface (combination of global translation, global rotation, nominal Acts transform, local translation and local rotation according to flags and tracker type). + */ Acts::Transform3 AlignmentTransformation::newMakeTransform(const Surface& surf, Eigen::Vector3d& millepedeTranslation, Eigen::Vector3d& sensorAngles, Eigen::Vector3d& localFrameTranslation, Eigen::Vector3d& sensorAnglesGlobal, unsigned int trkrid, bool survey) { // define null matrices @@ -417,70 +452,83 @@ Acts::Transform3 AlignmentTransformation::newMakeTransform(const Surface& surf, Acts::Transform3 transform; //! If we read the survey parameters directly, that is the full transform if (survey) - { - //! The millepede affines will just be what was read in, which was the - //! survey information. This should (in principle) be equivalent to - //! the ideal position + any misalignment - transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * mpLocalRotationAffine; - } - else - { - if (trkrid == TrkrDefs::tpcId) { - transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * actsTranslationAffine * actsRotationAffine * mpLocalTranslationAffine * mpLocalRotationAffine; + //! The millepede affines will just be what was read in, which was the + //! survey information. This should (in principle) be equivalent to + //! the ideal position + any misalignment + transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * mpLocalRotationAffine; } - else + else { - if(use_new_silicon_rotation_order) + // not survey. this is the normal usage + + if (trkrid == TrkrDefs::tpcId) { - transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * actsTranslationAffine * actsRotationAffine * mpLocalTranslationAffine * mpLocalRotationAffine; + if(use_module_tilt) + { + // use module tilt transforms with local rotation followed by local translation + transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * actsTranslationAffine * actsRotationAffine * mpLocalTranslationAffine * mpLocalRotationAffine; + } + else + { + // backward compatibility for old alignment params sets + transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * actsTranslationAffine * mpLocalRotationAffine * actsRotationAffine; + } } else { - // needed for backward compatibility to existing local rotations in MVTX - transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * actsTranslationAffine * mpLocalRotationAffine * actsRotationAffine; + // silicon and TPOT + if(use_new_silicon_rotation_order) + { + // use new transform order for silicon as well as TPC + transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * actsTranslationAffine * actsRotationAffine * mpLocalTranslationAffine * mpLocalRotationAffine; + } + else + { + // needed for backward compatibility to existing local rotation parmeter sets in silicon + transform = mpGlobalTranslationAffine * mpGlobalRotationAffine * actsTranslationAffine * mpLocalRotationAffine * actsRotationAffine; + } } } - } - + if (localVerbosity) - { - Acts::Transform3 actstransform = actsTranslationAffine * actsRotationAffine; - - std::cout << "newMakeTransform" << std::endl; - std::cout << "Input sensorAngles: " << std::endl - << sensorAngles << std::endl; - std::cout << "Input sensorAnglesGlobal: " << std::endl - << sensorAnglesGlobal << std::endl; - std::cout << "Input translation: " << std::endl - << millepedeTranslation << std::endl; - std::cout << "mpLocalRotationAffine: " << std::endl - << mpLocalRotationAffine.matrix() << std::endl; - std::cout << "mpLocalTranslationAffine: " << std::endl - << mpLocalTranslationAffine.matrix() << std::endl; - std::cout << "actsRotationAffine: " << std::endl - << actsRotationAffine.matrix() << std::endl; - std::cout << "actsTranslationAffine: " << std::endl - << actsTranslationAffine.matrix() << std::endl; - std::cout << "mpRotationGlobalAffine: " << std::endl - << mpGlobalRotationAffine.matrix() << std::endl; - std::cout << "mpTranslationGlobalAffine: " << std::endl - << mpGlobalTranslationAffine.matrix() << std::endl; - std::cout << "Overall transform: " << std::endl - << transform.matrix() << std::endl; - std::cout << "overall * idealinv " << std::endl - << (transform * actstransform.inverse()).matrix() << std::endl; - std::cout << "overall - ideal " << std::endl; - for (int test = 0; test < transform.matrix().rows(); test++) { - for (int test2 = 0; test2 < transform.matrix().cols(); test2++) - { - std::cout << transform(test, test2) - actstransform(test, test2) << ", "; - } - std::cout << std::endl; + Acts::Transform3 actstransform = actsTranslationAffine * actsRotationAffine; + + std::cout << "newMakeTransform" << std::endl; + std::cout << "Input sensorAngles: " << std::endl + << sensorAngles << std::endl; + std::cout << "Input sensorAnglesGlobal: " << std::endl + << sensorAnglesGlobal << std::endl; + std::cout << "Input translation: " << std::endl + << millepedeTranslation << std::endl; + std::cout << "mpLocalRotationAffine: " << std::endl + << mpLocalRotationAffine.matrix() << std::endl; + std::cout << "mpLocalTranslationAffine: " << std::endl + << mpLocalTranslationAffine.matrix() << std::endl; + std::cout << "actsRotationAffine: " << std::endl + << actsRotationAffine.matrix() << std::endl; + std::cout << "actsTranslationAffine: " << std::endl + << actsTranslationAffine.matrix() << std::endl; + std::cout << "mpRotationGlobalAffine: " << std::endl + << mpGlobalRotationAffine.matrix() << std::endl; + std::cout << "mpTranslationGlobalAffine: " << std::endl + << mpGlobalTranslationAffine.matrix() << std::endl; + std::cout << "Overall transform: " << std::endl + << transform.matrix() << std::endl; + std::cout << "overall * idealinv " << std::endl + << (transform * actstransform.inverse()).matrix() << std::endl; + std::cout << "overall - ideal " << std::endl; + for (int test = 0; test < transform.matrix().rows(); test++) + { + for (int test2 = 0; test2 < transform.matrix().cols(); test2++) + { + std::cout << transform(test, test2) - actstransform(test, test2) << ", "; + } + std::cout << std::endl; + } } - } - + return transform; } @@ -674,4 +722,4 @@ double AlignmentTransformation::extractModuleCenter(TrkrDefs::hitsetkey hitsetke double surf_radius = std::sqrt(surf_center[0] * surf_center[0] + surf_center[1] * surf_center[1]); return surf_radius; -} +} \ No newline at end of file diff --git a/offline/packages/trackbase/AlignmentTransformation.h b/offline/packages/trackbase/AlignmentTransformation.h index 7055d6c65a..df77605afe 100644 --- a/offline/packages/trackbase/AlignmentTransformation.h +++ b/offline/packages/trackbase/AlignmentTransformation.h @@ -14,6 +14,72 @@ class PHCompositeNode; class ActsGeometry; +/** + * Populate the alignment transformation map using nodes from the provided top node. + * @param topNode Root node from which required subnodes and geometry are retrieved. + */ + +/** + * Create or initialize the alignment transformation container attached to the provided top node. + * @param topNode Root node under which the alignment transformation container will be created or located. + */ + +/** + * Generate random rotation and translation perturbations using the provided standard deviations. + * @param angleDev Standard deviations for rotation about local X, Y, Z axes (radians). + * @param transformDev Standard deviations for translation along local X, Y, Z axes (same units as detector geometry). + */ + +/** + * Configure MVTX perturbation standard deviations and enable MVTX perturbations. + * @param mvtxDevs Array of six values: first three are rotation std devs (X, Y, Z in radians), last three are translation std devs (X, Y, Z). + */ + +/** + * Configure INTT perturbation standard deviations and enable INTT perturbations. + * @param inttDevs Array of six values: first three are rotation std devs (X, Y, Z in radians), last three are translation std devs (X, Y, Z). + */ + +/** + * Configure TPC perturbation standard deviations and enable TPC perturbations. + * @param tpcDevs Array of six values: first three are rotation std devs (X, Y, Z in radians), last three are translation std devs (X, Y, Z). + */ + +/** + * Configure MM perturbation standard deviations and enable MM perturbations. + * @param mmDevs Array of six values: first three are rotation std devs (X, Y, Z in radians), last three are translation std devs (X, Y, Z). + */ + +/** + * Enable verbose local logging for this AlignmentTransformation instance. + */ + +/** + * Set the misalignment factor for a given detector layer. + * @param layer Layer identifier for which the misalignment factor will be set. + * @param factor Misalignment scaling factor to apply to the specified layer. + */ + +/** + * Retrieve the misalignment factor for a given detector layer. + * @param layer Layer identifier to query. + * @returns The misalignment scaling factor for the specified layer. + */ + +/** + * Enable or disable use of INTT survey geometry when building transforms. + * @param sur If true, use INTT survey geometry; if false, do not use it. + */ + +/** + * Select whether to use the new silicon rotation order when constructing rotations. + * @param flag If true, use the new rotation order; if false, use the legacy order. + */ + +/** + * Configure whether module tilt should always be considered when computing transforms. + * @param flag If true, always apply module tilt; if false, apply tilt according to existing rules. + */ class AlignmentTransformation { public: @@ -128,7 +194,8 @@ class AlignmentTransformation bool use_new_silicon_rotation_order = false; bool use_module_tilt_always = false; - + bool use_module_tilt = false; // starts at false in all cases + bool use_intt_survey_geometry = false; Acts::Transform3 newMakeTransform(const Surface& surf, Eigen::Vector3d& millepedeTranslation, Eigen::Vector3d& sensorAngles, Eigen::Vector3d& localFrameTranslation, Eigen::Vector3d& sensorAnglesGlobal, unsigned int trkrid, bool survey); @@ -149,4 +216,4 @@ class AlignmentTransformation double sectorPhi[2][12] = {}; }; -#endif +#endif \ No newline at end of file diff --git a/offline/packages/trackreco/DSTClusterPruning.cc b/offline/packages/trackreco/DSTClusterPruning.cc index 15bfe547ca..4983e33d9b 100644 --- a/offline/packages/trackreco/DSTClusterPruning.cc +++ b/offline/packages/trackreco/DSTClusterPruning.cc @@ -145,7 +145,17 @@ int DSTClusterPruning::load_nodes(PHCompositeNode* topNode) return Fun4AllReturnCodes::EVENT_OK; } -//_____________________________________________________________________ +/** + * @brief Prunes and propagates clusters into the reduced cluster container based on track seeds. + * + * @details The function ensures required containers are present and returns early if any are missing. + * If m_pruneAllSeeds is true, it iterates all TrackSeed entries in the TPC and silicon seed containers + * and for each referenced cluster key copies the corresponding cluster from the full cluster map into + * the reduced cluster map if it does not already exist. If m_pruneAllSeeds is false, it iterates the + * combined track seed container, resolves the corresponding TPC and silicon seeds by index for each + * entry, and copies clusters referenced by those seeds into the reduced map. Missing cluster keys or + * null seeds are logged. New clusters are created as TrkrClusterv5 and inserted into m_reduced_cluster_map. + */ void DSTClusterPruning::prune_clusters() { // use this to create object that looks through both tracks and clusters and saves into new object @@ -177,6 +187,39 @@ void DSTClusterPruning::prune_clusters() } return; } + if(m_pruneAllSeeds) + { + for(const auto& container : {m_tpc_track_seed_container, m_silicon_track_seed_container}) + { + for (const auto& trackseed : *container) + { + if (!trackseed) + { + std::cout << "No TrackSeed" << std::endl; + continue; + } + + for (auto key_iter = trackseed->begin_cluster_keys(); key_iter != trackseed->end_cluster_keys(); ++key_iter) + { + const auto& cluster_key = *key_iter; + auto *cluster = m_cluster_map->findCluster(cluster_key); + if (!cluster) + { + std::cout << "DSTClusterPruning::evaluate_tracks - unable to find cluster for key " << cluster_key << std::endl; + continue; + } + if (!m_reduced_cluster_map->findCluster(cluster_key)) + { + m_cluster = new TrkrClusterv5(); + m_cluster->CopyFrom(cluster); + m_reduced_cluster_map->addClusterSpecifyKey(cluster_key, m_cluster); + } + } + } + } + return; + } + for (const auto& trackseed : *m_track_seed_container) { if (!trackseed) @@ -402,4 +445,4 @@ void DSTClusterPruning::print_clusters() std::cout << "end of loop" << "\n"; } -} +} \ No newline at end of file diff --git a/offline/packages/trackreco/DSTClusterPruning.h b/offline/packages/trackreco/DSTClusterPruning.h index c9ec35ea53..a5fd5e62df 100644 --- a/offline/packages/trackreco/DSTClusterPruning.h +++ b/offline/packages/trackreco/DSTClusterPruning.h @@ -36,6 +36,12 @@ class TrkrHitSetContainer; class TrkrHitTruthAssoc; class TrackSeedContainer; +/** + * @brief Enable exhaustive dumping of clusters for every seed. + * + * When called, clusters on all silicon and TPC seeds will be individually + * dumped during the pruning step. + */ class DSTClusterPruning : public SubsysReco { public: @@ -52,6 +58,12 @@ class DSTClusterPruning : public SubsysReco //! end of processing //int End(PHCompositeNode*) override; + //! dump all clusters on all seeds out + void pruneAllSeeds() + { + m_pruneAllSeeds = true; + } + private: //! load nodes int load_nodes(PHCompositeNode*); @@ -68,6 +80,9 @@ class DSTClusterPruning : public SubsysReco TrackSeedContainer* m_tpc_track_seed_container = nullptr; TrackSeedContainer* m_silicon_track_seed_container = nullptr; +//! set to true if you want to dump out all clusters on all silicon +//! and all tpc seeds individually + bool m_pruneAllSeeds = false; //@} // debugging helpers diff --git a/offline/packages/trackreco/PHActsTrkFitter.cc b/offline/packages/trackreco/PHActsTrkFitter.cc index a26ee1fa0a..6143c740b6 100644 --- a/offline/packages/trackreco/PHActsTrkFitter.cc +++ b/offline/packages/trackreco/PHActsTrkFitter.cc @@ -5,7 +5,6 @@ * \author Tony Frawley */ - #include "PHActsTrkFitter.h" #include "ActsPropagator.h" @@ -25,7 +24,7 @@ #include #include #include -//#include +// #include #include #include #include @@ -86,6 +85,22 @@ PHActsTrkFitter::PHActsTrkFitter(const std::string& name) { } +/** + * @brief Initialize the PHActsTrkFitter and its runtime resources. + * + * Configures and allocates components required for track refitting: creates and + * retrieves required nodes, loads alignment states and field map, constructs + * Acts Kalman and directed Kalman fitter functions, collects material surfaces + * when needed, configures the outlier finder, and optionally initializes + * timing histograms and the Acts evaluator. Verifies presence of the TPC + * geometry container. + * + * @param topNode Top-level Fun4All node used to find or create required nodes. + * @return int Fun4All return code: + * - Fun4AllReturnCodes::EVENT_OK on successful initialization. + * - Fun4AllReturnCodes::ABORTEVENT if node creation or retrieval fails. + * - Fun4AllReturnCodes::ABORTRUN if the TPC geometry container is missing. + */ int PHActsTrkFitter::InitRun(PHCompositeNode* topNode) { if (Verbosity() > 1) @@ -134,8 +149,8 @@ int PHActsTrkFitter::InitRun(PHCompositeNode* topNode) MaterialSurfaceSelector selector; if (m_fitSiliconMMs || m_directNavigation) { - m_tGeometry->geometry().tGeometry->visitSurfaces(selector,false); - //std::cout<<"selector.surfaces.size() "<geometry().tGeometry->visitSurfaces(selector, false); + // std::cout<<"selector.surfaces.size() "<(m_evalname); m_evaluator->Init(topNode); - if(m_actsEvaluator && !m_simActsEvaluator) + if (m_actsEvaluator && !m_simActsEvaluator) { m_evaluator->isData(); } @@ -182,10 +197,10 @@ int PHActsTrkFitter::InitRun(PHCompositeNode* topNode) _tpccellgeo = findNode::getClass(topNode, "TPCGEOMCONTAINER"); if (!_tpccellgeo) - { - std::cout << PHWHERE << " unable to find DST node TPCGEOMCONTAINER" << std::endl; - return Fun4AllReturnCodes::ABORTRUN; - } + { + std::cout << PHWHERE << " unable to find DST node TPCGEOMCONTAINER" << std::endl; + return Fun4AllReturnCodes::ABORTRUN; + } if (Verbosity() > 1) { @@ -269,6 +284,15 @@ int PHActsTrkFitter::ResetEvent(PHCompositeNode* /*topNode*/) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Finalize the fitter and write optional outputs. + * + * If time analysis is enabled, writes timing histograms to the configured file and closes it. + * If an Acts evaluator is configured, calls its End() method. + * If the outlier finder is enabled, writes its results. + * + * @return int Fun4AllReturnCodes::EVENT_OK on successful finalization. + */ int PHActsTrkFitter::End(PHCompositeNode* /*topNode*/) { if (m_timeAnalysis) @@ -287,7 +311,7 @@ int PHActsTrkFitter::End(PHCompositeNode* /*topNode*/) { m_evaluator->End(); } - if(m_useOutlierFinder) + if (m_useOutlierFinder) { m_outlierFinder.Write(); } @@ -298,6 +322,18 @@ int PHActsTrkFitter::End(PHCompositeNode* /*topNode*/) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Iterate over seed tracks, perform Acts-based refits, and populate output track maps. + * + * This routine walks the seed track map, builds measurements and source links for each seed, + * optionally varies INTT crossing estimates, runs the Acts fitter for each trial, and on successful + * fits converts Acts results into SvtxTrack entries inserted into the appropriate output map + * (m_trackMap or m_directedTrackMap). Failed fits increment m_nBadFits and are skipped. The method + * also prepares transient geometry/context, handles directed-navigation surface selection when + * configured, and selects the best trial when multiple crossing variations are used. + * + * @param logLevel Logging verbosity to use for the internal Acts logger. + */ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) { auto logger = Acts::getDefaultLogger("PHActsTrkFitter", logLevel); @@ -314,47 +350,46 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) // capture the input crossing value, and set crossing parameters //============================== - short silicon_crossing = SHRT_MAX; - auto siseed = m_siliconSeeds->get(siid); - if(siseed) - { - silicon_crossing = siseed->get_crossing(); - } + short silicon_crossing = SHRT_MAX; + auto *siseed = m_siliconSeeds->get(siid); + if (siseed) + { + silicon_crossing = siseed->get_crossing(); + } short crossing = silicon_crossing; short int crossing_estimate = crossing; - if(m_enable_crossing_estimate) - { - crossing_estimate = track->get_crossing_estimate(); // geometric crossing estimate from matcher - } + if (m_enable_crossing_estimate) + { + crossing_estimate = track->get_crossing_estimate(); // geometric crossing estimate from matcher + } //=============================== - // must have silicon seed with valid crossing if we are doing a SC calibration fit if (m_fitSiliconMMs) + { + if ((siid == std::numeric_limits::max()) || (silicon_crossing == SHRT_MAX)) { - if( (siid == std::numeric_limits::max()) || (silicon_crossing == SHRT_MAX)) - { - continue; - } + continue; } + } // do not skip TPC only tracks, just set crossing to the nominal zero - if(!siseed) - { - crossing = 0; - } + if (!siseed) + { + crossing = 0; + } if (Verbosity() > 1) { - if(siseed) - { - std::cout << "tpc and si id " << tpcid << ", " << siid << " silicon_crossing " << silicon_crossing - << " crossing " << crossing << " crossing estimate " << crossing_estimate << std::endl; - } + if (siseed) + { + std::cout << "tpc and si id " << tpcid << ", " << siid << " silicon_crossing " << silicon_crossing + << " crossing " << crossing << " crossing estimate " << crossing_estimate << std::endl; + } } - auto tpcseed = m_tpcSeeds->get(tpcid); + auto *tpcseed = m_tpcSeeds->get(tpcid); /// Need to also check that the tpc seed wasn't removed by the ghost finder if (!tpcseed) @@ -381,7 +416,7 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) if (Verbosity() > 1 && siseed) { std::cout << " m_pp_mode " << m_pp_mode << " m_enable_crossing_estimate " << m_enable_crossing_estimate - << " INTT crossing " << crossing << " crossing_estimate " << crossing_estimate << std::endl; + << " INTT crossing " << crossing << " crossing_estimate " << crossing_estimate << std::endl; } short int this_crossing = crossing; @@ -390,35 +425,35 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) std::vector chisq_ndf; std::vector svtx_vec; - if(m_pp_mode) + if (m_pp_mode) + { + if (m_enable_crossing_estimate && crossing == SHRT_MAX) + { + // this only happens if there is a silicon seed but no assigned INTT crossing, and only in pp_mode + // If there is no INTT crossing, start with the crossing_estimate value, vary up and down, fit, and choose the best chisq/ndf + use_estimate = true; + nvary = max_bunch_search; + if (Verbosity() > 1) + { + std::cout << " No INTT crossing: use crossing_estimate " << crossing_estimate << " with nvary " << nvary << std::endl; + } + } + else { - if (m_enable_crossing_estimate && crossing == SHRT_MAX) - { - // this only happens if there is a silicon seed but no assigned INTT crossing, and only in pp_mode - // If there is no INTT crossing, start with the crossing_estimate value, vary up and down, fit, and choose the best chisq/ndf - use_estimate = true; - nvary = max_bunch_search; - if (Verbosity() > 1) - { - std::cout << " No INTT crossing: use crossing_estimate " << crossing_estimate << " with nvary " << nvary << std::endl; - } - } - else - { - // use INTT crossing - crossing_estimate = crossing; - } + // use INTT crossing + crossing_estimate = crossing; } + } else + { + // non pp mode, we want only crossing zero, veto others + if (siseed && silicon_crossing != 0) { - // non pp mode, we want only crossing zero, veto others - if(siseed && silicon_crossing != 0) - { - crossing = 0; - //continue; - } - crossing_estimate = crossing; + crossing = 0; + // continue; } + crossing_estimate = crossing; + } // Fit this track assuming either: // crossing = INTT value, if it exists (uses nvary = 0) @@ -441,16 +476,16 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) makeSourceLinks.initialize(_tpccellgeo); makeSourceLinks.setVerbosity(Verbosity()); makeSourceLinks.set_pp_mode(m_pp_mode); - for(const auto& layer : m_ignoreLayer) + for (const auto& layer : m_ignoreLayer) { makeSourceLinks.ignoreLayer(layer); } // loop over modifiedTransformSet and replace transient elements modified for the previous track with the default transforms // does nothing if m_transient_id_set is empty makeSourceLinks.resetTransientTransformMap( - m_alignmentTransformationMapTransient, - m_transient_id_set, - m_tGeometry); + m_alignmentTransformationMapTransient, + m_transient_id_set, + m_tGeometry); if (m_use_clustermover) { @@ -459,37 +494,56 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) { // silicon source links sourceLinks = makeSourceLinks.getSourceLinksClusterMover( - siseed, + siseed, + measurements, + m_clusterContainer, + m_tGeometry, + m_globalPositionWrapper, + this_crossing); + } + + // tpc source links + const auto tpcSourceLinks = makeSourceLinks.getSourceLinksClusterMover( + tpcseed, measurements, m_clusterContainer, m_tGeometry, m_globalPositionWrapper, this_crossing); - } - - // tpc source links - const auto tpcSourceLinks = makeSourceLinks.getSourceLinksClusterMover( - tpcseed, - measurements, - m_clusterContainer, - m_tGeometry, - m_globalPositionWrapper, - this_crossing); // add tpc sourcelinks to silicon source links sourceLinks.insert(sourceLinks.end(), tpcSourceLinks.begin(), tpcSourceLinks.end()); - - } else { - + } + else + { // make source links using transient transforms for distortion corrections - if(Verbosity() > 1) - { std::cout << "Calling getSourceLinks for si seed, siid " << siid << " and tpcid " << tpcid << std::endl; } + if (Verbosity() > 1) + { + std::cout << "Calling getSourceLinks for si seed, siid " << siid << " and tpcid " << tpcid << std::endl; + } if (siseed && !m_ignoreSilicon) { // silicon source links sourceLinks = makeSourceLinks.getSourceLinks( - siseed, + siseed, + measurements, + m_clusterContainer, + m_tGeometry, + m_globalPositionWrapper, + m_alignmentTransformationMapTransient, + m_transient_id_set, + this_crossing); + } + + if (Verbosity() > 1) + { + std::cout << "Calling getSourceLinks for tpc seed, siid " << siid << " and tpcid " << tpcid << std::endl; + } + + // tpc source links + const auto tpcSourceLinks = makeSourceLinks.getSourceLinks( + tpcseed, measurements, m_clusterContainer, m_tGeometry, @@ -497,21 +551,6 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) m_alignmentTransformationMapTransient, m_transient_id_set, this_crossing); - } - - if(Verbosity() > 1) - { std::cout << "Calling getSourceLinks for tpc seed, siid " << siid << " and tpcid " << tpcid << std::endl; } - - // tpc source links - const auto tpcSourceLinks = makeSourceLinks.getSourceLinks( - tpcseed, - measurements, - m_clusterContainer, - m_tGeometry, - m_globalPositionWrapper, - m_alignmentTransformationMapTransient, - m_transient_id_set, - this_crossing); // add tpc sourcelinks to silicon source links sourceLinks.insert(sourceLinks.end(), tpcSourceLinks.begin(), tpcSourceLinks.end()); @@ -524,15 +563,15 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) Acts::Vector3 position(0, 0, 0); if (siseed) { - position = TrackSeedHelper::get_xyz(siseed)*Acts::UnitConstants::cm; + position = TrackSeedHelper::get_xyz(siseed) * Acts::UnitConstants::cm; } - if(!siseed || !is_valid(position) || m_ignoreSilicon) + if (!siseed || !is_valid(position) || m_ignoreSilicon) { - position = TrackSeedHelper::get_xyz(tpcseed)*Acts::UnitConstants::cm; + position = TrackSeedHelper::get_xyz(tpcseed) * Acts::UnitConstants::cm; } if (!is_valid(position)) { - if(Verbosity() > 4) + if (Verbosity() > 4) { std::cout << "Invalid position of " << position.transpose() << std::endl; } @@ -559,26 +598,26 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) for (const auto& surface_apr : m_materialSurfaces) { - if(m_forceSiOnlyFit) + if (m_forceSiOnlyFit) { - if(surface_apr->geometryId().volume() >12) + if (surface_apr->geometryId().volume() > 12) { continue; } } bool pop_flag = false; - if(surface_apr->geometryId().approach() == 1) + if (surface_apr->geometryId().approach() == 1) { surfaces.push_back(surface_apr); } else { pop_flag = true; - for (const auto& surface_sns: surfaces_tmp) + for (const auto& surface_sns : surfaces_tmp) { if (surface_apr->geometryId().volume() == surface_sns->geometryId().volume()) { - if ( surface_apr->geometryId().layer()==surface_sns->geometryId().layer()) + if (surface_apr->geometryId().layer() == surface_sns->geometryId().layer()) { pop_flag = false; surfaces.push_back(surface_sns); @@ -594,9 +633,9 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) surfaces.pop_back(); pop_flag = false; } - if (surface_apr->geometryId().volume() == 12&& surface_apr->geometryId().layer()==8) + if (surface_apr->geometryId().volume() == 12 && surface_apr->geometryId().layer() == 8) { - for (const auto& surface_sns: surfaces_tmp) + for (const auto& surface_sns : surfaces_tmp) { if (14 == surface_sns->geometryId().volume()) { @@ -619,13 +658,13 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) { // make sure micromegas are in the tracks, if required if (m_useMicromegas && - std::none_of(surfaces.begin(), surfaces.end(), [this](const auto& surface) - { return m_tGeometry->maps().isMicromegasSurface(surface); })) - { - continue; + std::none_of(surfaces.begin(), surfaces.end(), [this](const auto& surface) + { return m_tGeometry->maps().isMicromegasSurface(surface); })) + { + continue; + } } } - } float px = std::numeric_limits::quiet_NaN(); float py = std::numeric_limits::quiet_NaN(); @@ -635,7 +674,7 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) float seedphi = 0; float seedtheta = 0; float seedeta = 0; - if(siseed) + if (siseed) { seedphi = siseed->get_phi(); seedtheta = siseed->get_theta(); @@ -659,7 +698,9 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) px = pt * std::cos(phi); py = pt * std::sin(phi); pz = pt * std::cosh(eta) * std::cos(theta); - } else { + } + else + { px = seedpt * std::cos(seedphi); py = seedpt * std::sin(seedphi); pz = seedpt * std::cosh(seedeta) * std::cos(seedtheta); @@ -668,14 +709,14 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) Acts::Vector3 momentum(px, py, pz); if (!is_valid(momentum)) { - if(Verbosity() > 4) + if (Verbosity() > 4) { std::cout << "Invalid momentum of " << momentum.transpose() << std::endl; } continue; } - auto pSurface = Acts::Surface::makeShared( position); + auto pSurface = Acts::Surface::makeShared(position); Acts::Vector4 actsFourPos(position(0), position(1), position(2), 10 * Acts::UnitConstants::ns); Acts::BoundSquareMatrix cov = setDefaultCovariance(); @@ -723,8 +764,10 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) auto trackStateContainer = std::make_shared(); ActsTrackFittingAlgorithm::TrackContainer tracks(trackContainer, trackStateContainer); - if(Verbosity() > 1) - { std::cout << "Calling fitTrack for track with siid " << siid << " tpcid " << tpcid << " crossing " << crossing << std::endl; } + if (Verbosity() > 1) + { + std::cout << "Calling fitTrack for track with siid " << siid << " tpcid " << tpcid << " crossing " << crossing << std::endl; + } auto result = fitTrack(sourceLinks, seed, kfOptions, surfaces, calibrator, tracks); fitTimer.stop(); @@ -761,7 +804,7 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) if (ivary != nvary) { - if(Verbosity() > 3) + if (Verbosity() > 3) { std::cout << "Skipping track fit for trial variation" << std::endl; } @@ -806,7 +849,6 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) if (getTrackFitResult(result, track, &newTrack, tracks, measurements)) { - // insert in dedicated map m_directedTrackMap->insertWithKey(&newTrack, trid); } @@ -822,11 +864,7 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) m_trackMap->insertWithKey(&newTrack, trid); } } // end insert track for normal fit - } // end case where INTT crossing is known - - - - + } // end case where INTT crossing is known } else if (!m_fitSiliconMMs) { @@ -840,7 +878,7 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) << std::endl; } } // end fit failed case - } // end ivary loop + } // end ivary loop trackTimer.stop(); auto trackTime = trackTimer.get_accumulated_time(); @@ -854,11 +892,25 @@ void PHActsTrkFitter::loopTracks(Acts::Logging::Level logLevel) return; } +/** + * @brief Populate a SvtxTrack from an Acts fit result when a reference surface is available. + * + * If the fit result contains a reference surface, updates the provided SvtxTrack with + * fitted position, momentum, charge, covariance and track states; optionally fills + * alignment-state map and invokes the Acts evaluator depending on configuration. + * + * @param fitOutput The Acts fit result to extract the fitted trajectory and parameters from. + * @param seed The input TrackSeed associated with this fit (used for evaluator/context). + * @param track The SvtxTrack to update with the fit outcome; modified when the function returns true. + * @param tracks Container of Acts track states produced by the fitter. + * @param measurements Container of measurement objects corresponding to the fit (used for alignment/state mapping). + * @return true if the fit result contained a reference surface and the SvtxTrack was updated, false otherwise. + */ bool PHActsTrkFitter::getTrackFitResult( - const FitResult& fitOutput, - TrackSeed* seed, SvtxTrack* track, - const ActsTrackFittingAlgorithm::TrackContainer& tracks, - const ActsTrackFittingAlgorithm::MeasurementContainer& measurements) + const FitResult& fitOutput, + TrackSeed* seed, SvtxTrack* track, + const ActsTrackFittingAlgorithm::TrackContainer& tracks, + const ActsTrackFittingAlgorithm::MeasurementContainer& measurements) { /// Make a trajectory state for storage, which conforms to Acts track fit /// analysis tool @@ -872,12 +924,12 @@ bool PHActsTrkFitter::getTrackFitResult( // retrieve track parameters from fit result Acts::BoundTrackParameters parameters = ActsExamples::TrackParameters(outtrack.referenceSurface().getSharedPtr(), - outtrack.parameters(), outtrack.covariance(), outtrack.particleHypothesis()); + outtrack.parameters(), outtrack.covariance(), outtrack.particleHypothesis()); indexedParams.emplace( - outtrack.tipIndex(), - ActsExamples::TrackParameters{outtrack.referenceSurface().getSharedPtr(), - outtrack.parameters(), outtrack.covariance(), outtrack.particleHypothesis()}); + outtrack.tipIndex(), + ActsExamples::TrackParameters{outtrack.referenceSurface().getSharedPtr(), + outtrack.parameters(), outtrack.covariance(), outtrack.particleHypothesis()}); if (Verbosity() > 2) { @@ -937,7 +989,21 @@ bool PHActsTrkFitter::getTrackFitResult( return false; } -//__________________________________________________________________________________ +/** + * @brief Run the appropriate Acts track fitter for the provided measurements and seed. + * + * Selects the directed fitter when silicon‑MM fitting or direct navigation is enabled; otherwise + * invokes the full Kalman fitter. The chosen fitter is executed with the provided source links, + * seed, fitter options, calibrator, and result container. + * + * @param sourceLinks Measured source links (ordered measurements) for the track. + * @param seed Initial track parameters used to seed the fitter. + * @param kfOptions Fitter configuration and options. + * @param surfSequence Sequence of navigation surfaces (used by directed navigation fits). + * @param calibrator Calibrator adapter applied during fitting. + * @param tracks Container that will be populated with fit trajectories/results. + * @return ActsTrackFittingAlgorithm::TrackFitterResult Result status and outputs produced by the invoked fitter. + */ ActsTrackFittingAlgorithm::TrackFitterResult PHActsTrkFitter::fitTrack( const std::vector& sourceLinks, const ActsTrackFittingAlgorithm::TrackParameters& seed, @@ -948,13 +1014,25 @@ ActsTrackFittingAlgorithm::TrackFitterResult PHActsTrkFitter::fitTrack( { // use direct fit for silicon MM gits or direct navigation if (m_fitSiliconMMs || m_directNavigation) - { return (*m_fitCfg.dFit)(sourceLinks, seed, kfOptions, surfSequence, calibrator, tracks); } + { + return (*m_fitCfg.dFit)(sourceLinks, seed, kfOptions, surfSequence, calibrator, tracks); + } // use full fit in all other cases return (*m_fitCfg.fit)(sourceLinks, seed, kfOptions, calibrator, tracks); } -//__________________________________________________________________________________ +/** + * @brief Filter source links to the subset located on silicon/micromegas surfaces. + * + * Iterates through the provided source links, selects those that lie on silicon or + * micromegas surfaces according to the fitter configuration, and appends the + * corresponding surface pointers to the supplied surfaces vector. + * + * @param sourceLinks Input collection of source links to filter. + * @param surfaces Output vector that will be appended with the matching surface pointers. + * @return SourceLinkVec The filtered subset of sourceLinks that correspond to the appended surfaces. + */ SourceLinkVec PHActsTrkFitter::getSurfaceVector(const SourceLinkVec& sourceLinks, SurfacePtrVec& surfaces) const { SourceLinkVec siliconMMSls; @@ -986,9 +1064,9 @@ SourceLinkVec PHActsTrkFitter::getSurfaceVector(const SourceLinkVec& sourceLinks } } - if(m_forceSiOnlyFit) + if (m_forceSiOnlyFit) { - if(m_tGeometry->maps().isMicromegasSurface(surf)||m_tGeometry->maps().isTpcSurface(surf)) + if (m_tGeometry->maps().isMicromegasSurface(surf) || m_tGeometry->maps().isTpcSurface(surf)) { continue; } @@ -1010,6 +1088,15 @@ SourceLinkVec PHActsTrkFitter::getSurfaceVector(const SourceLinkVec& sourceLinks return siliconMMSls; } +/** + * @brief Ensure surfaces are ordered by geometry volume then layer and remove any that are out of order. + * + * Traverses the provided surface vector and removes entries whose geometryId().volume() + * is less than a preceding entry's volume, or whose geometryId().layer() is less when + * volumes are equal. + * + * @param surfaces Vector of surface pointers to validate and modify in place. + */ void PHActsTrkFitter::checkSurfaceVec(SurfacePtrVec& surfaces) const { for (unsigned int i = 0; i < surfaces.size() - 1; i++) @@ -1058,11 +1145,27 @@ void PHActsTrkFitter::checkSurfaceVec(SurfacePtrVec& surfaces) const } } +/** + * @brief Update a SvtxTrack object with the results of an Acts fit. + * + * Populates the provided SvtxTrack with position, momentum, charge, fit chi2/ndf, + * rotated covariance (if present), and a set of SvtxTrackState entries derived + * from the Acts trajectory. Optionally fills SvtxTrackStates via the transformer + * and, when configured to fit silicon/micromegas (m_fitSiliconMMs), extrapolates + * the fitted parameters to associated TPC cluster surfaces and appends states + * for those clusters. + * + * @param tips Ordered indices of trajectory tips for the fitted Acts trajectory; the first element is used. + * @param paramsMap Map of trajectory index to fitted track parameters used to extract position, momentum, charge, and covariance. + * Position values are converted from Acts' millimeter units to SvtxTrack's centimeter units. + * @param tracks Container holding Acts trajectory and measurement/state information required to build SvtxTrackStates. + * @param track Mutable SvtxTrack to update with fit results and appended states. + */ void PHActsTrkFitter::updateSvtxTrack( - const std::vector& tips, - const Trajectory::IndexedParameters& paramsMap, - const ActsTrackFittingAlgorithm::TrackContainer& tracks, - SvtxTrack* track) + const std::vector& tips, + const Trajectory::IndexedParameters& paramsMap, + const ActsTrackFittingAlgorithm::TrackContainer& tracks, + SvtxTrack* track) { const auto& mj = tracks.trackStateContainer(); @@ -1133,31 +1236,37 @@ void PHActsTrkFitter::updateSvtxTrack( trackStateTimer.restart(); if (m_fillSvtxTrackStates) - { transformer.fillSvtxTrackStates(mj, trackTip, track, m_transient_geocontext); } + { + transformer.fillSvtxTrackStates(mj, trackTip, track, m_transient_geocontext); + } // in using silicon mm fit also extrapolate track parameters to all TPC surfaces with clusters // get all tpc clusters auto* seed = track->get_tpc_seed(); - if( m_fitSiliconMMs && seed ) + if (m_fitSiliconMMs && seed) { - // acts propagator ActsPropagator propagator(m_tGeometry); // loop over cluster keys associated to TPC seed - for( auto key_iter = seed->begin_cluster_keys(); key_iter != seed->end_cluster_keys(); ++key_iter ) + for (auto key_iter = seed->begin_cluster_keys(); key_iter != seed->end_cluster_keys(); ++key_iter) { const auto& cluskey = *key_iter; // make sure cluster is from TPC const auto detId = TrkrDefs::getTrkrId(cluskey); if (detId != TrkrDefs::tpcId) - { continue; } + { + continue; + } // get layer, propagate const auto layer = TrkrDefs::getLayer(cluskey); auto result = propagator.propagateTrack(params, layer); - if( !result.ok() ) { continue; } + if (!result.ok()) + { + continue; + } // get path length and extrapolated parameters auto& [pathLength, trackStateParams] = result.value(); @@ -1380,4 +1489,4 @@ int PHActsTrkFitter::getNodes(PHCompositeNode* topNode) m_globalPositionWrapper.set_suppressCrossing(true); return Fun4AllReturnCodes::EVENT_OK; -} +} \ No newline at end of file diff --git a/offline/packages/trackreco/PHActsTrkFitter.h b/offline/packages/trackreco/PHActsTrkFitter.h index c6e0afec35..1c15effb8a 100644 --- a/offline/packages/trackreco/PHActsTrkFitter.h +++ b/offline/packages/trackreco/PHActsTrkFitter.h @@ -21,8 +21,8 @@ #include #include #include -#include #include +#include #include @@ -127,22 +127,66 @@ class PHActsTrkFitter : public SubsysReco } void SetIteration(int iter) { _n_iteration = iter; } - void set_track_map_name(const std::string& map_name) { _track_map_name = map_name; } - void set_svtx_seed_map_name(const std::string& map_name) { _svtx_seed_map_name = map_name; } - - void set_svtx_alignment_state_map_name(const std::string& map_name) { - _svtx_alignment_state_map_name = map_name; - m_alignStates.alignmentStateMap(map_name); + /** + * Set the name used for the SvtxTrackMap node. + * @param map_name The node name to use for the track map. + */ +void set_track_map_name(const std::string& map_name) { _track_map_name = map_name; } + /** + * Set the SvtxTrackSeedContainer node name used by the fitter. + * @param map_name Name of the SvtxTrackSeedContainer to read from or write to in the node tree. + */ +void set_svtx_seed_map_name(const std::string& map_name) { _svtx_seed_map_name = map_name; } + + /** + * Set the name of the SvtxAlignmentStateMap to use and propagate it to the alignment state manager. + * + * Updates the internal map name and informs the alignment state manager to use the specified map. + * + * @param map_name Name of the SvtxAlignmentStateMap node. + */ + void set_svtx_alignment_state_map_name(const std::string& map_name) + { + _svtx_alignment_state_map_name = map_name; + m_alignStates.alignmentStateMap(map_name); } - /// Set flag for pp running + /** + * Configure proton–proton (pp) running mode. + * @param ispp `true` to enable pp mode, `false` to disable it. + */ void set_pp_mode(bool ispp) { m_pp_mode = ispp; } - void set_enable_geometric_crossing_estimate(bool flag) { m_enable_crossing_estimate = flag ; } - void set_use_clustermover(bool use) { m_use_clustermover = use; } - void ignoreLayer(int layer) { m_ignoreLayer.insert(layer); } - void setTrkrClusterContainerName(std::string &name){ m_clusterContainerName = name; } - void setDirectNavigation(bool flag) { m_directNavigation = flag; } + /** + * Enable or disable the geometric crossing estimate feature. + * @param flag `true` to enable geometric crossing estimate, `false` to disable it. + */ +void set_enable_geometric_crossing_estimate(bool flag) { m_enable_crossing_estimate = flag; } + /** + * Enable or disable use of the cluster mover. + * @param use `true` to enable the cluster mover, `false` to disable it. + */ +void set_use_clustermover(bool use) { m_use_clustermover = use; } + /** + * Mark a detector layer to be ignored during fitting. + * + * @param layer Detector layer identifier to add to the ignore set. + */ +void ignoreLayer(int layer) { m_ignoreLayer.insert(layer); } + /** + * Set the name of the TRKR_CLUSTER container to use for input clusters. + * @param name Name of the TRKR_CLUSTER container. + */ +void setTrkrClusterContainerName(const std::string& name) { m_clusterContainerName = name; } + /** + * Enable or disable direct navigation mode for track fitting. + * + * When enabled, the fitter will attempt to use direct navigation (surface-sequence based fitting) + * instead of the default navigation strategy. + * + * @param flag True to enable direct navigation, false to disable it. + */ +void setDirectNavigation(bool flag) { m_directNavigation = flag; } private: /// Get all the nodes @@ -155,10 +199,10 @@ class PHActsTrkFitter : public SubsysReco /// Convert the acts track fit result to an svtx track void updateSvtxTrack( - const std::vector& tips, - const Trajectory::IndexedParameters& paramsMap, - const ActsTrackFittingAlgorithm::TrackContainer& tracks, - SvtxTrack* track); + const std::vector& tips, + const Trajectory::IndexedParameters& paramsMap, + const ActsTrackFittingAlgorithm::TrackContainer& tracks, + SvtxTrack* track); /// Helper function to call either the regular navigation or direct /// navigation, depending on m_fitSiliconMMs @@ -233,14 +277,22 @@ class PHActsTrkFitter : public SubsysReco bool m_directNavigation = true; - // do we have a constant field + /** + * Add the given surface to the selector's internal list if it has material and is not already present. + * + * @param surface The surface to test and append; ignored when `surface->surfaceMaterial()` is null or the surface is already in `surfaces`. + */ bool m_ConstField{false}; - double fieldstrength{std::numeric_limits::quiet_NaN()}; + /** + * Collect surfaces that have associated material and append them to the selector's collection if not already present. + * @param surface Surface to test and potentially add to the collection. + */ + double fieldstrength{std::numeric_limits::quiet_NaN()}; // max variation of bunch crossing away from crossing_estimate short int max_bunch_search = 2; - //name of TRKR_CLUSTER container + // name of TRKR_CLUSTER container std::string m_clusterContainerName = "TRKR_CLUSTER"; //!@name evaluator @@ -253,7 +305,7 @@ class PHActsTrkFitter : public SubsysReco //@} //! tracks -// SvtxTrackMap* m_seedTracks = nullptr; + // SvtxTrackMap* m_seedTracks = nullptr; //! tpc global position wrapper TpcGlobalPositionWrapper m_globalPositionWrapper; @@ -268,7 +320,7 @@ class PHActsTrkFitter : public SubsysReco int _n_iteration = 0; std::string _track_map_name = "SvtxTrackMap"; std::string _svtx_seed_map_name = "SvtxTrackSeedContainer"; - std::string _svtx_alignment_state_map_name = "SvtxAlignmentStateMap"; + std::string _svtx_alignment_state_map_name = "SvtxAlignmentStateMap"; /// Default particle assumption to pion unsigned int m_pHypothesis = 211; @@ -292,14 +344,18 @@ class PHActsTrkFitter : public SubsysReco std::vector m_materialSurfaces = {}; - struct MaterialSurfaceSelector { + struct MaterialSurfaceSelector + { std::vector surfaces = {}; /// @param surface is the test surface - void operator()(const Acts::Surface* surface) { - if (surface->surfaceMaterial() != nullptr) { + void operator()(const Acts::Surface* surface) + { + if (surface->surfaceMaterial() != nullptr) + { if (std::find(surfaces.begin(), surfaces.end(), surface) == - surfaces.end()) { + surfaces.end()) + { surfaces.push_back(surface); } } @@ -307,4 +363,4 @@ class PHActsTrkFitter : public SubsysReco }; }; -#endif +#endif \ No newline at end of file diff --git a/offline/packages/trackreco/PHSiliconTpcTrackMatching.cc b/offline/packages/trackreco/PHSiliconTpcTrackMatching.cc index 2e66a6df28..16c332c4bc 100644 --- a/offline/packages/trackreco/PHSiliconTpcTrackMatching.cc +++ b/offline/packages/trackreco/PHSiliconTpcTrackMatching.cc @@ -387,6 +387,17 @@ int PHSiliconTpcTrackMatching::End(PHCompositeNode * /*unused*/) return Fun4AllReturnCodes::EVENT_OK; } +/** + * @brief Retrieve required nodes from the top-level node tree and create missing SVTX seed container if needed. + * + * Looks up and caches pointers to the cluster-crossing map, silicon and TPC track seed containers, + * the SVTX track seed container (creating and inserting one under DST/SVTX if absent), the cluster + * container (using the configurable node name), and the Acts geometry object. + * + * @param topNode Top-level PHCompositeNode from which nodes are retrieved. + * @return int Fun4All return code: EVENT_OK on success; ABORTEVENT if any required node (silicon track map, + * TPC track map, cluster container, or Acts geometry) is missing and prevents initialization. + */ int PHSiliconTpcTrackMatching::GetNodes(PHCompositeNode *topNode) { //--------------------------------- @@ -445,10 +456,10 @@ int PHSiliconTpcTrackMatching::GetNodes(PHCompositeNode *topNode) svtxNode->addNode(node); } - _cluster_map = findNode::getClass(topNode, "TRKR_CLUSTER"); + _cluster_map = findNode::getClass(topNode, _cluster_map_name); if (!_cluster_map) { - std::cout << PHWHERE << " ERROR: Can't find node TRKR_CLUSTER" << std::endl; + std::cout << PHWHERE << " ERROR: Can't find node " <<_cluster_map_name << std::endl; return Fun4AllReturnCodes::ABORTEVENT; } @@ -841,4 +852,4 @@ std::vector PHSiliconTpcTrackMatching::getTrackletClusterList cluskey_vec.push_back(key); } // end loop over clusters for this track return cluskey_vec; -} +} \ No newline at end of file diff --git a/offline/packages/trackreco/PHSiliconTpcTrackMatching.h b/offline/packages/trackreco/PHSiliconTpcTrackMatching.h index acb157bd83..644d488eed 100644 --- a/offline/packages/trackreco/PHSiliconTpcTrackMatching.h +++ b/offline/packages/trackreco/PHSiliconTpcTrackMatching.h @@ -136,10 +136,31 @@ class PHSiliconTpcTrackMatching : public SubsysReco, public PHParameterInterface // void set_use_old_matching(const bool flag) { _use_old_matching = flag; } void set_test_windows_printout(const bool test) { _test_windows = test; } - void set_file_name(const std::string &name) { _file_name = name; } - void set_pp_mode(const bool flag) { _pp_mode = flag; } - void set_use_intt_crossing(const bool flag) { _use_intt_crossing = flag; } - + /** + * Set the output file name used by the track matching component. + * @param name File name to use for output (replaces the current _file_name). + */ +void set_file_name(const std::string &name) { _file_name = name; } + /** + * Set whether proton-proton (pp) mode is enabled. + * + * @param flag If `true`, enable pp mode; if `false`, disable pp mode. + */ +void set_pp_mode(const bool flag) { _pp_mode = flag; } + /** + * Enable or disable use of INTT crossing information during track matching. + * @param flag `true` to use INTT crossing information, `false` to ignore it. + */ +void set_use_intt_crossing(const bool flag) { _use_intt_crossing = flag; } + /** + * Set the name of the cluster map node used by the matching algorithm. + * + * @param name The node name to use for the TrkrClusterContainer (overrides the default "TRKR_CLUSTER"). + */ + void set_cluster_map_name(const std::string &name) + { + _cluster_map_name = name; + } int InitRun(PHCompositeNode *topNode) override; int process_event(PHCompositeNode *) override; @@ -200,7 +221,13 @@ class PHSiliconTpcTrackMatching : public SubsysReco, public PHParameterInterface // double _collision_rate = 50e3; // input rate for phi correction // double _reference_collision_rate = 50e3; // reference rate for phi correction - // double _si_vertex_dzmax = 0.25; // mm + /** + * Retrieve the list of cluster keys associated with a silicon or TPC tracklet. + * + * @param tracklet Pointer to the TrackSeed whose associated cluster keys are requested. + * If `nullptr` or the tracklet has no clusters, an empty vector is returned. + * @returns A vector of `TrkrDefs::cluskey` representing the cluster keys linked to the given tracklet. + */ double fieldstrength{std::numeric_limits::quiet_NaN()}; bool _test_windows = false; @@ -210,8 +237,9 @@ class PHSiliconTpcTrackMatching : public SubsysReco, public PHParameterInterface int _n_iteration = 0; std::string _track_map_name = "TpcTrackSeedContainer"; std::string _silicon_track_map_name = "SiliconTrackSeedContainer"; + std::string _cluster_map_name = "TRKR_CLUSTER"; std::string m_fieldMap = "1.4"; std::vector getTrackletClusterList(TrackSeed* tracklet); }; -#endif // PHSILICONTPCTRACKMATCHING_H +#endif // PHSILICONTPCTRACKMATCHING_H \ No newline at end of file diff --git a/offline/packages/trackreco/PHSimpleVertexFinder.h b/offline/packages/trackreco/PHSimpleVertexFinder.h index da0e36fabf..03d91cf6a5 100644 --- a/offline/packages/trackreco/PHSimpleVertexFinder.h +++ b/offline/packages/trackreco/PHSimpleVertexFinder.h @@ -51,11 +51,32 @@ class PHSimpleVertexFinder : public SubsysReco void setTrackPtCut(const double cut) { _track_pt_cut = cut; } // void setUseTrackCovariance(bool set) {_use_track_covariance = set;} void setOutlierPairCut(const double cut) { _outlier_cut = cut; } - void setTrackMapName(const std::string &name) { _track_map_name = name; } - void setVertexMapName(const std::string &name) { _vertex_map_name = name; } - void zeroField(const bool flag) { _zero_field = flag; } - void setTrkrClusterContainerName(std::string &name){ m_clusterContainerName = name; } - void set_pp_mode(bool mode) { _pp_mode = mode; } + /** + * Set the identifier used to find the input track map. + * @param name Name of the SvtxTrackMap node to read tracks from. + */ +void setTrackMapName(const std::string &name) { _track_map_name = name; } + /** + * Set the name of the SvtxVertexMap node used for storing reconstructed vertices. + * @param name Name of the vertex map node to use. + */ +void setVertexMapName(const std::string &name) { _vertex_map_name = name; } + /** + * Enable or disable zero magnetic field mode. + * @param flag `true` to treat the detector magnetic field as zero, `false` to use the nominal field. + */ +void zeroField(const bool flag) { _zero_field = flag; } + /** + * Set the name of the TrkrClusterContainer node to use. + * + * @param name Name of the cluster container (for example "TRKR_CLUSTER"). + */ +void setTrkrClusterContainerName(const std::string &name){ m_clusterContainerName = name; } + /** + * Set whether the finder operates in proton–proton collision mode. + * @param mode `true` to enable pp mode, `false` to use non-pp mode (e.g., heavy-ion). + */ +void set_pp_mode(bool mode) { _pp_mode = mode; } private: int GetNodes(PHCompositeNode *topNode); @@ -118,4 +139,4 @@ class PHSimpleVertexFinder : public SubsysReco bool _pp_mode = true; // default to pp mode }; -#endif // PHSIMPLEVERTEXFINDER_H +#endif // PHSIMPLEVERTEXFINDER_H \ No newline at end of file diff --git a/offline/packages/trackreco/PHTpcDeltaZCorrection.h b/offline/packages/trackreco/PHTpcDeltaZCorrection.h index 46d099f587..3414d84853 100644 --- a/offline/packages/trackreco/PHTpcDeltaZCorrection.h +++ b/offline/packages/trackreco/PHTpcDeltaZCorrection.h @@ -31,8 +31,19 @@ class PHTpcDeltaZCorrection : public SubsysReco, public PHParameterInterface int InitRun(PHCompositeNode *topNode) override; int process_event(PHCompositeNode *topNode) override; int End(PHCompositeNode *topNode) override; + /** + * Reset module parameters to their default values. + * + * This restores any configurable parameters to their initialized defaults. + */ + + /** + * Set the name of the TrkrCluster container node to use. + * + * @param name Node name of the TrkrCluster container (e.g., "TRKR_CLUSTER"). + */ void SetDefaultParameters() override; - void setTrkrClusterContainerName(std::string &name) { m_clusterContainerName = name; } + void setTrkrClusterContainerName(const std::string &name) { m_clusterContainerName = name; } private: /// load nodes @@ -61,4 +72,4 @@ class PHTpcDeltaZCorrection : public SubsysReco, public PHParameterInterface std::set m_corrected_clusters; }; -#endif // PHTpcDeltaZCorrection_H +#endif // PHTpcDeltaZCorrection_H \ No newline at end of file