Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2026-04-06 08:12:10

0001 #ifndef SEPDEVENTPLANECALIB_QVECCALIB_H
0002 #define SEPDEVENTPLANECALIB_QVECCALIB_H
0003 
0004 #include "QVecDefs.h"
0005 
0006 #include <fun4all/SubsysReco.h>
0007 
0008 #include <array>
0009 #include <map>
0010 #include <stdexcept>
0011 #include <string>
0012 #include <string_view>
0013 #include <unordered_set>
0014 #include <vector>
0015 
0016 class PHCompositeNode;
0017 class EventPlaneData;
0018 class TFile;
0019 class TH1;
0020 class TH2;
0021 class TProfile;
0022 
0023 /**
0024  * @class QVecCalib
0025  * @brief Orchestrates the multi-pass anisotropy calibration for the sEPD Q-vectors.
0026  *
0027  * This class implements a three-pass correction procedure designed to remove
0028  * detector-induced biases from the sEPD event plane reconstruction:
0029  * * 1. **ComputeRecentering**: Calculates the first-order <Q> vector offsets (re-centering)
0030  * per centrality bin.
0031  * 2. **ApplyRecentering**: Applies the first-order offsets and computes the
0032  * second-order whitening/flattening matrix.
0033  * 3. **ApplyFlattening**: Applies the full correction (re-centering + flattening)
0034  * to produce final validated event planes.
0035  * * The class manages event-level selections based on charge-centrality correlations
0036  * and handles the exclusion of "bad" (hot/cold/dead) sEPD channels.
0037  */
0038 class QVecCalib : public SubsysReco
0039 {
0040  public:
0041   explicit QVecCalib(const std::string& name = "QVecCalib");
0042 
0043   /** Called during initialization.
0044       Typically this is where you can book histograms, and e.g.
0045       register them to Fun4AllServer (so they can be output to file
0046       using Fun4AllServer::dumpHistos() method).
0047    */
0048   int Init(PHCompositeNode* topNode) override;
0049 
0050   /** Called for first event when run number is known.
0051       Typically this is where you may want to fetch data from
0052       database, because you know the run number.
0053    */
0054   int InitRun(PHCompositeNode *topNode) override;
0055 
0056   /** Called for each event.
0057       This is where you do the real work.
0058    */
0059   int process_event(PHCompositeNode* topNode) override;
0060 
0061   /// Clean up internals after each event.
0062   int ResetEvent(PHCompositeNode* topNode) override;
0063 
0064   /// Called at the end of all processing.
0065   int End(PHCompositeNode* topNode) override;
0066 
0067   enum class Pass
0068   {
0069     ComputeRecentering,
0070     ApplyRecentering,
0071     ApplyFlattening
0072   };
0073 
0074   void set_pass(int pass)
0075   {
0076     m_pass = validate_pass(pass);
0077   }
0078 
0079   void set_input_hist(std::string_view file)
0080   {
0081     m_input_hist = file;
0082   }
0083 
0084   void set_input_Q_calib(std::string_view file)
0085   {
0086     m_input_Q_calib = file;
0087   }
0088 
0089   void set_dst_tag(std::string_view tag)
0090   {
0091     m_dst_tag = tag;
0092   }
0093 
0094   void set_cdb_output_dir(std::string_view cdb_dir)
0095   {
0096     m_cdb_output_dir = cdb_dir;
0097   }
0098 
0099  private:
0100   static Pass validate_pass(int pass)
0101   {
0102     switch (pass)
0103     {
0104     case 0:
0105       return Pass::ComputeRecentering;
0106     case 1:
0107       return Pass::ApplyRecentering;
0108     case 2:
0109       return Pass::ApplyFlattening;
0110     default:
0111       throw std::invalid_argument("Invalid pass value");
0112     }
0113   }
0114 
0115   struct CorrectionData
0116   {
0117     QVecShared::QVec avg_Q{};
0118 
0119     double avg_Q_xx{0.0};
0120     double avg_Q_yy{0.0};
0121     double avg_Q_xy{0.0};
0122 
0123     std::array<std::array<double, 2>, 2> X_matrix{};
0124   };
0125 
0126   static constexpr size_t m_cent_bins = QVecShared::CENT_BINS;
0127   static constexpr auto m_harmonics = QVecShared::HARMONICS;
0128 
0129   static constexpr float SIGMA_HOT {6.0};
0130   static constexpr float SIGMA_COLD {-6.0};
0131 
0132   double m_cent_low{-0.5};
0133   double m_cent_high{79.5};
0134 
0135   std::string m_input_hist;
0136   std::string m_input_Q_calib;
0137   std::string m_dst_tag;
0138   std::string m_cdb_output_dir{"."};
0139   Pass m_pass{Pass::ComputeRecentering};
0140   EventPlaneData* m_evtdata{nullptr};
0141 
0142   int m_event{0};
0143   int m_runnumber{0};
0144 
0145   struct EventCounters
0146   {
0147     int bad_centrality_sepd_correlation{0};
0148     int zero_sepd_total_charge{0};
0149     int total_processed{0};
0150   };
0151 
0152   EventCounters m_event_counters;
0153 
0154   std::array<std::array<QVecShared::QVec, 2>, m_harmonics.size()> m_q_vectors{};
0155 
0156   static constexpr int PROGRESS_REPORT_INTERVAL = 10000;
0157 
0158   // Holds all correction data
0159   // key: [Cent][Harmonic][Subdetector]
0160   // Harmonics {2,3,4} -> 3 elements
0161   // Subdetectors {S,N,NS} -> 3 elements
0162   std::array<std::array<std::array<CorrectionData, 3>, m_harmonics.size()>, m_cent_bins> m_correction_data;
0163 
0164   // Store harmonic orders and subdetectors for easy iteration
0165   static constexpr std::array<QVecShared::Subdetector, 2> m_subdetectors = {QVecShared::Subdetector::S, QVecShared::Subdetector::N};
0166   static constexpr std::array<QVecShared::QComponent, 2> m_components = {QVecShared::QComponent::X, QVecShared::QComponent::Y};
0167 
0168   // [Harmonic Index][Channel Index] -> {cos, sin}
0169   std::vector<std::vector<std::pair<double, double>>> m_trig_cache;
0170 
0171   struct AverageHists
0172   {
0173     TProfile* S_x_avg{nullptr};
0174     TProfile* S_y_avg{nullptr};
0175     TProfile* N_x_avg{nullptr};
0176     TProfile* N_y_avg{nullptr};
0177 
0178     TH2* Psi_S{nullptr};
0179     TH2* Psi_N{nullptr};
0180     TH2* Psi_NS{nullptr};
0181   };
0182 
0183   struct RecenterHists
0184   {
0185     TProfile* S_x_corr_avg{nullptr};
0186     TProfile* S_y_corr_avg{nullptr};
0187     TProfile* N_x_corr_avg{nullptr};
0188     TProfile* N_y_corr_avg{nullptr};
0189 
0190     TProfile* S_xx_avg{nullptr};
0191     TProfile* S_yy_avg{nullptr};
0192     TProfile* S_xy_avg{nullptr};
0193     TProfile* N_xx_avg{nullptr};
0194     TProfile* N_yy_avg{nullptr};
0195     TProfile* N_xy_avg{nullptr};
0196 
0197     TProfile* NS_xx_avg{nullptr};
0198     TProfile* NS_yy_avg{nullptr};
0199     TProfile* NS_xy_avg{nullptr};
0200 
0201     TH2* Psi_S_corr{nullptr};
0202     TH2* Psi_N_corr{nullptr};
0203     TH2* Psi_NS_corr{nullptr};
0204   };
0205 
0206   struct FlatteningHists
0207   {
0208     TProfile* S_x_corr2_avg{nullptr};
0209     TProfile* S_y_corr2_avg{nullptr};
0210     TProfile* N_x_corr2_avg{nullptr};
0211     TProfile* N_y_corr2_avg{nullptr};
0212 
0213     TProfile* S_xx_corr_avg{nullptr};
0214     TProfile* S_yy_corr_avg{nullptr};
0215     TProfile* S_xy_corr_avg{nullptr};
0216 
0217     TProfile* N_xx_corr_avg{nullptr};
0218     TProfile* N_yy_corr_avg{nullptr};
0219     TProfile* N_xy_corr_avg{nullptr};
0220 
0221     TProfile* NS_xx_corr_avg{nullptr};
0222     TProfile* NS_yy_corr_avg{nullptr};
0223     TProfile* NS_xy_corr_avg{nullptr};
0224 
0225     TH2* Psi_S_corr2{nullptr};
0226     TH2* Psi_N_corr2{nullptr};
0227     TH2* Psi_NS_corr2{nullptr};
0228   };
0229 
0230   // sEPD Bad Channels
0231   std::unordered_set<int> m_bad_channels;
0232 
0233   double m_sEPD_min_avg_charge_threshold{1};
0234   double m_sEPD_sigma_threshold{3};
0235 
0236   // Hists
0237   TH1* hCentrality{nullptr};
0238 
0239   TH2* h2SEPD_Charge{nullptr};
0240   TH2* h2SEPD_Chargev2{nullptr};
0241 
0242   TH2* h2SEPD_South_Charge_rbin{nullptr};
0243   TH2* h2SEPD_North_Charge_rbin{nullptr};
0244 
0245   TH2* h2SEPD_South_Charge_rbinv2{nullptr};
0246   TH2* h2SEPD_North_Charge_rbinv2{nullptr};
0247 
0248   TProfile* hSEPD_Charge_Min{nullptr};
0249   TProfile* hSEPD_Charge_Max{nullptr};
0250 
0251   TProfile* hSEPD_Bad_Channels{nullptr};
0252 
0253   std::map<std::string, TH2*> m_hists2D;
0254   std::map<std::string, TProfile*> m_profiles;
0255 
0256   std::vector<AverageHists> m_average_hists;
0257   std::vector<RecenterHists> m_recenter_hists;
0258   std::vector<FlatteningHists> m_flattening_hists;
0259 
0260   /**
0261    * @brief Initializes all output histograms and profiles.
0262    * * Dynamically generates histogram names using the shared naming helper based on
0263    * the current calibration pass (e.g., adding "_corr" or "_corr2" suffixes).
0264    */
0265   void init_hists();
0266 
0267   /**
0268    * @brief Safely retrieves a ROOT object from a file and returns a managed pointer.
0269    * * Performs a dynamic_cast to verify the requested type T and Clones the object
0270    * to ensure it remains valid after the source file is closed.
0271    * * @tparam T The ROOT class type (e.g., TProfile).
0272    * @param file Pointer to the source TFile.
0273    * @param name The name of the object within the file.
0274    * @return T* A managed pointer to the cloned object.
0275    * @throws std::runtime_error If the object is not found or type mismatch occurs.
0276    */
0277   template <typename T>
0278   T* load_and_clone(TFile* file, const std::string& name);
0279 
0280   /**
0281    * @brief Loads the results of previous passes from a calibration file.
0282    * * Populates the internal correction data structure with averages and/or
0283    * matrices required for the current processing pass.
0284    */
0285   int load_correction_data();
0286 
0287   /**
0288    * @brief Validates events based on sEPD total charge vs. centrality correlation.
0289    * * Compares the current event's total charge against the 3-sigma bounds derived
0290    * from the QA histograms to reject pile-up or background-dominated events.
0291    * @return True if the event falls within the acceptable charge window.
0292    */
0293   bool process_event_check();
0294 
0295   /**
0296    * @brief Performs the primary tower-by-tower Q-vector calculation and normalization.
0297    * * Loops through sEPD channels, excludes bad channels, calculates the raw Q-vector
0298    * for all harmonics, and normalizes the results by the total arm charge.
0299    * @return True if both South and North arms have non-zero total charge.
0300    */
0301   bool process_sEPD();
0302 
0303   /**
0304    * @brief Calculates and fills profiles for the initial Q-vector averages.
0305    * @param cent The event centrality.
0306    * @param q_S The South arm normalized Q-vector.
0307    * @param q_N The North arm normalized Q-vector.
0308    * @param h Reference to the cache of profiles for the first pass.
0309    */
0310   static void process_averages(double cent, const QVecShared::QVec& q_S, const QVecShared::QVec& q_N, const AverageHists& h);
0311 
0312   /**
0313    * @brief Applies re-centering offsets and fills profiles for second-moment calculation.
0314    * @param cent The event centrality.
0315    * @param h_idx Harmonic index.
0316    * @param q_S The South arm normalized Q-vector.
0317    * @param q_N The North arm normalized Q-vector.
0318    * @param h Reference to the cache of profiles for the second pass.
0319    */
0320   void process_recentering(double cent, size_t h_idx, const QVecShared::QVec& q_S, const QVecShared::QVec& q_N, const RecenterHists& h);
0321 
0322   /**
0323    * @brief Applies the full correction (re-centering + flattening) for validation.
0324    * @param cent The event centrality.
0325    * @param h_idx Harmonic index.
0326    * @param q_S The South arm normalized Q-vector.
0327    * @param q_N The North arm normalized Q-vector.
0328    * @param h Reference to the cache of profiles for the third pass.
0329    */
0330   void process_flattening(double cent, size_t h_idx, const QVecShared::QVec& q_S, const QVecShared::QVec& q_N, const FlatteningHists& h);
0331 
0332   /**
0333    * @brief Calculates the 2x2 anisotropy correction (whitening) matrix.
0334    * * This matrix transforms the elliptical Q-vector distribution into a circularly
0335    * symmetric (isotropic) distribution. It effectively corrects for detector
0336    * acceptance effects and gain non-uniformities by normalizing the second-order
0337    * moments of the Q-vector.
0338    * * @param xx The <Qx^2> second moment.
0339    * @param yy The <Qy^2> second moment.
0340    * @param xy The <QxQy> cross-moment.
0341    * @param n Harmonic order (used for error logging context).
0342    * @param cent_bin Centrality bin (used for error logging context).
0343    * @param det_label Detector label ("S" or "N").
0344    * @return std::array<std::array<double, 2>, 2> The 2x2 correction matrix.
0345    */
0346   std::array<std::array<double, 2>, 2> calculate_flattening_matrix(double xx, double yy, double xy, int n, int cent_bin, const std::string& det_label);
0347 
0348   /**
0349    * @brief Computes 1st-order re-centering offsets for a specific centrality bin.
0350    * * Extracts average Q-vector components from histograms and stores them in the
0351    * correction data matrix for use in subsequent processing passes.
0352    * * @param cent_bin The index of the centrality bin.
0353    * @param h_idx The index of the harmonic order in the harmonics array.
0354    */
0355   void compute_averages(size_t cent_bin, int h_idx);
0356 
0357   /**
0358    * @brief Computes re-centering parameters and solves the flattening matrices.
0359    * * Extracts the re-centered second moments from the profiles and populates the
0360    * internal CorrectionData matrix with calculated flattening coefficients.
0361    * @param cent_bin The centrality bin index.
0362    * @param h_idx The harmonic index.
0363    */
0364   void compute_recentering(size_t cent_bin, int h_idx);
0365 
0366   /**
0367    * @brief Logs the final corrected moments to verify successful flattening.
0368    * @param cent_bin The centrality bin index.
0369    * @param n The harmonic order.
0370    */
0371   void print_flattening(size_t cent_bin, int n) const;
0372 
0373   void prepare_hists();
0374 
0375   /**
0376    * @brief Prepares a vector of pointers to histograms used in the first pass.
0377    * @return A vector of AverageHists structs, indexed by harmonic.
0378    */
0379   void prepare_average_hists();
0380 
0381   /**
0382    * @brief Prepares a vector of pointers to histograms used in the second pass.
0383    * @return A vector of RecenterHists structs, indexed by harmonic.
0384    */
0385   void prepare_recenter_hists();
0386 
0387   /**
0388    * @brief Prepares a vector of pointers to histograms used in the third pass.
0389    * @return A vector of FlatteningHists structs, indexed by harmonic.
0390    */
0391   void prepare_flattening_hists();
0392 
0393   /**
0394    * @brief Top-level driver for processing Quality Assurance histograms.
0395    * * Loads the reference histogram file to identify bad channels and establish
0396    * event-level charge thresholds as a function of centrality.
0397    */
0398   int process_QA_hist();
0399 
0400   /**
0401    * @brief Identifies and catalogs "Bad" (Hot, Cold, or Dead) sEPD channels.
0402    * * Uses a reference charge histogram to compute Z-scores based on mean charge
0403    * per radial bin. Channels exceeding the sigma threshold are added to the internal exclusion set.
0404    * * @param file Pointer to the open TFile containing QA histograms.
0405    */
0406   int process_bad_channels(TFile* file);
0407 
0408   /**
0409    * @brief Establishes sEPD charge-cut thresholds for event selection.
0410    * * Uses the 2D total charge vs. centrality distribution to derive mean and
0411    * sigma values, generating a 1D profile of the selection window.
0412    * @param file Pointer to the open QA histogram file.
0413    */
0414   int process_sEPD_event_thresholds(TFile* file);
0415 
0416   void write_cdb();
0417 
0418   /**
0419    * @brief Writes the Event Plane calibration constants to a CDB-formatted TTree.
0420    * * Formats the re-centering and flattening moments into a CDBTTree payload
0421    * indexed by centrality bin for sPHENIX database integration.
0422    * * @param output_dir The filesystem directory where the .root payload will be saved.
0423    */
0424   void write_cdb_EventPlane();
0425 
0426   /**
0427    * @brief Writes the Hot/Cold tower status map to a CDB-formatted TTree.
0428    * * Encodes sEPD channel indices into TowerInfo keys and maps status codes (1=Dead,
0429    * 2=Hot, 3=Cold) to the final database payload.
0430    * * @param output_dir The filesystem directory where the .root payload will be saved.
0431    */
0432   void write_cdb_BadTowers();
0433 };
0434 
0435 #endif  // SEPDEVENTPLANECALIB_QVECCALIB_H