Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-21 08:23:36

0001 // Copyright (c) 2023, Fabrizio Caola, Radoslaw Grabarczyk,
0002 // Maxwell Hutt, Gavin P. Salam, Ludovic Scyboz, and Jesse Thaler
0003 
0004 #ifndef __FJCONTRIB_FLAVINFO_HH__
0005 #define __FJCONTRIB_FLAVINFO_HH__
0006 
0007 #ifdef __FJC_FLAVINFO_USEFJCORE__
0008 #define fastjet fjcore
0009 #include "fjcore_local.hh"
0010 #define FASTJET_BEGIN_NAMESPACE namespace fjcore {
0011 #define FASTJET_OVERRIDE override
0012 #define FASTJET_END_NAMESPACE }
0013 #else
0014 #include "fastjet/JetDefinition.hh"
0015 //#include "fastjet/LimitedWarning.hh"
0016 //#include "fastjet/ClusterSequence.hh"
0017 //#include "fastjet/SharedPtr.hh"
0018 #endif
0019 
0020 
0021 #include <iostream>
0022 
0023 //using namespace std;
0024 
0025 FASTJET_BEGIN_NAMESPACE      // defined in fastjet/internal/base.hh
0026 namespace contrib{
0027 //----------------------------------------------------------------------
0028 /// class to allow representation of flavour, including concepts
0029 /// such as the fact that a particle is an incoming "beam", or
0030 /// should be a "spectator" during the clustering.
0031 ///
0032 /// The class also provides a facility to interpret PDG codes
0033 /// and deduce the corresponding flavour content of the particle.
0034 ///
0035 /// For jet clustering with IFN algorithms, it is often easier
0036 /// to use the FlavHistory class, which is given below
0037 class FlavInfo : public PseudoJet::UserInfoBase {
0038 
0039 public:
0040   /// constructs a flavour info from a pdg code. This can handle most
0041   /// standard model codes without difficulty. A zero code produces a flavourless object.
0042   ///
0043   /// The flags argument is optional, and can be set either to
0044   /// FlavInfo::beam or FlavInfo::spectator
0045   ///
0046   FlavInfo (int pdg_code = 0, int flags = 0);
0047   /// constructs a flavour info object from the individual flavours
0048   FlavInfo (int n_d, int n_u, int n_s, int n_c, int n_b, int n_t, int flags = 0);
0049 
0050   /// apply modulo 2 to all flavours, for hadronic info
0051   void apply_modulo_2();
0052 
0053   /// apply any abs to all flavours (so any non-zero number becomes 1)
0054   void apply_any_abs();
0055 
0056   /// returns the net number of quarks of flavour iflv
0057   /// (iflv runs from 1=d to 6=top).
0058   int operator[](int iflv) const {return _flav_content[iflv];}
0059 
0060   /// sets the number of objects of flavour iflv (1=d to 6=top) to be n
0061   void set_flav(int iflv, int n) {_flav_content[iflv] = n; update_flavourless_attribute();}
0062 
0063   /// allows comparison of flavours
0064   bool operator==(const FlavInfo &) const;
0065   bool operator!=(const FlavInfo &) const;
0066 
0067   /// resets all flavours to zero except iflv; this should be called,
0068   /// for example, when considering b-flavour at hadron level, so that
0069   /// the algorithm doesn't get confused by the many other quark
0070   /// flavours that are around (and also because, experimentally,
0071   /// those other flavours may not be well known).
0072   void reset_all_but_flav(int iflv);
0073 
0074   /// returns the pdg_code, or 0 if it's unknown (e.g. due to result
0075   /// of recombination)
0076   int pdg_code() const {return _pdg_code;}
0077 
0078   /// label this particle as being an incoming beam particle
0079   void label_as_beam() {_flav_content[0] |= beam;}
0080   /// returns true if this particle is a beam particle.
0081   bool is_beam() const {return (_flav_content[0] & beam);}
0082 
0083   /// label this object as being a "spectator", such as a W, which is
0084   /// relevant for calculating the beam distance in flavour clusterings
0085   /// but does itself take part in the clustering
0086   void label_as_spectator() {_flav_content[0] |= spectator;}
0087   /// returns true if this particle is a spectator.
0088   bool is_spectator() const {return (_flav_content[0] & spectator);}
0089 
0090   /// returns true if the object has no net flavour
0091   bool is_flavourless() const {return (_flav_content[0] & _is_flavourless);}
0092   bool is_flavorless() const {return is_flavourless();}
0093 
0094   /// returns true if the object has more than one unit of flavour
0095   bool is_multiflavoured() const;
0096   bool is_multiflavored() const {return is_multiflavoured();}
0097 
0098   /// returns true if the particle has associated FlavInfo and if there
0099   /// exists a flavour for which (*this) and the particle have
0100   /// positive/negative (or vice versa) net amounts of that flavour. 
0101   ///
0102   /// BEWARE: do not use this to check if the flavours cancel. 
0103   /// Instead add the flavours and check that the result is flavourless
0104   bool has_opposite_flavour(const PseudoJet & particle) const;
0105   bool has_opposite_flavor(const PseudoJet & particle) const {return has_opposite_flavour(particle);}
0106 
0107   /// allows addition of flavour: note that beam, spectator and PDG status are lost
0108   FlavInfo operator+(const FlavInfo &) const;
0109   /// allows subtraction of flavour: note that beam, spectator and PDG status are lost
0110   FlavInfo operator-(const FlavInfo &) const;
0111 
0112   /// returns a string such as "u d", "cbar", etc.; "g" means gluon or anything
0113   /// else with no flavour
0114   std::string description() const;
0115 
0116   /// returns the flavour of a particle if that particle has flavour, otherwise
0117   /// just the default flavour
0118   static const FlavInfo & flavour_of(const PseudoJet & particle);
0119   // {
0120   //  if (particle.has_user_info<FlavInfo>()) return particle.user_info<FlavInfo>();
0121   //  else                                    return _no_flav;
0122   //}
0123 
0124   /// value of flag to indicate that the particle is an incoming beam particle
0125   static const int beam = 2;
0126   /// value of flag to indicate that the particle is a "spectator",
0127   /// such as a W, which is relevant for calculating the beam distance
0128   /// in flavour clusterings but does itself take part in the
0129   /// clustering
0130   static const int spectator = 4;
0131   int _flav_content[7];
0132   void update_flavourless_attribute();
0133   static const int _is_flavourless = 1;
0134 private:
0135   int _pdg_code;
0136   static const FlavInfo _no_flav;
0137 
0138 };
0139 
0140 //----------------------------------------------------------------------
0141 /// Class to keep track of flavour history of a given jet
0142 ///
0143 class FlavHistory : public PseudoJet::UserInfoBase {
0144 public:
0145   /// Constructor for generic initial history step from a PDG ID code
0146   FlavHistory(int initial_flavour_pdg_id) {
0147     _flavour_history.push_back(std::make_pair(-1, FlavInfo(initial_flavour_pdg_id)));
0148   }
0149 
0150   /// Constructor for generic initial history step from a FlavInfo object
0151   FlavHistory(const FlavInfo & initial_flavour) {
0152     _flavour_history.push_back(std::make_pair(-1, initial_flavour));
0153   }
0154 
0155   /// Constructor for known initial history step from a FlavInfo object
0156   FlavHistory(const FlavInfo & initial_flavour, 
0157               const int initial_hist_step) {
0158     _flavour_history.push_back(std::make_pair(initial_hist_step, initial_flavour));
0159   }
0160 
0161   /// returns the current flavour of the particle
0162   const FlavInfo & current_flavour() const {return _flavour_history.back().second;}
0163   /// returns the initial flavour of the particle
0164   const FlavInfo & initial_flavour() const {return _flavour_history.front().second;}
0165 
0166   /// Returns the index of the cluster_sequence history step at which this
0167   /// jet acquired its current flavour
0168   int current_hist_index() const {return _flavour_history.back().first;}
0169 
0170   /// Returns the index of the cluster_sequence history step at which this
0171   /// jet was created with its initial flavour
0172   int initial_hist_index() const {return _flavour_history.front().first;}
0173 
0174 
0175 
0176 
0177 
0178   /// Apply flavour modulo 2 to all elements of the history
0179   void apply_modulo_2() {
0180     for (unsigned i = 0; i < _flavour_history.size(); i++) {
0181       _flavour_history[i].second.apply_modulo_2();
0182     }
0183   }
0184 
0185   /// Label the most recent history element as a beam particle
0186   void label_as_beam() {
0187     _flavour_history.back().second.label_as_beam();
0188   }
0189 
0190   /// Add element to flavour history of a jet
0191   void update_flavour_history(FlavInfo new_flavour, int hist_step) {
0192     if (new_flavour != _flavour_history.back().second) {
0193       _flavour_history.push_back(std::make_pair(hist_step, new_flavour));
0194     }
0195   }
0196 
0197   /// Change the last history step of the flavour history. This could
0198   /// be used to, e.g., set the initial history step when the constructor
0199   /// with history step -1 is used.
0200   void amend_last_history_index(int new_hist_step) {
0201     _flavour_history.back().first = new_hist_step;
0202   }
0203 
0204   /// Return the flavour history vector
0205   const std::vector<std::pair<int, FlavInfo>> & history() const {return _flavour_history;}
0206 
0207   /// Return the current (final) flavour element of the history of a PseudoJet.
0208   /// For particles with just a FlavInfo, it returns that. 
0209   static const FlavInfo & current_flavour_of(const PseudoJet & particle);
0210 
0211   /// Return the final history step of the history of a PseudoJet
0212   static const int current_index_of(const PseudoJet &particle);
0213 
0214   /// Return the first flavour element of the history of a PseudoJet
0215   /// For particles with just a FlavInfo, it returns just that. 
0216   static const FlavInfo & initial_flavour_of(const PseudoJet &jet);
0217 
0218   /// Return the first history step of the history of a PseudoJet
0219   static const int initial_index_of(const PseudoJet &jet) {
0220     if (jet.has_user_info<FlavHistory>()) {
0221       return jet.user_info<FlavHistory>().history()[0].first;
0222     } else {
0223       throw fastjet::Error(
0224           "A particle without FlavHistory was searched for FlavHistory.");
0225     }
0226   }
0227 
0228   /// Return the flavour at a given history step
0229   const FlavInfo & flavour_at_step(int step) const {
0230     if (_flavour_history[0].first > step) {
0231       throw fastjet::Error(
0232           "A particle without FlavHistory was searched for FlavHistory.");
0233     }
0234     int index_needed = -1;
0235     for (unsigned i = 1; i < _flavour_history.size(); i++) {
0236       if ((_flavour_history[i].first > step) &&
0237           (_flavour_history[i - 1].first <= step))
0238         index_needed = i - 1;
0239     }
0240     if (index_needed == -1) {
0241       return _flavour_history.back().second;
0242     } else {
0243       return _flavour_history[index_needed].second;
0244     }
0245   }
0246 
0247 
0248   /// Reset the _flavour_history such that it has one element whose flavour is the initial
0249   /// flavour of the jet, and the hist_step is initiated to its initial value.
0250   void reset_flavour_history() {
0251     std::pair<int, FlavInfo> orig_flavour = _flavour_history[0];
0252     std::vector<std::pair<int, FlavInfo>> _new_history;
0253     _new_history.push_back(orig_flavour);
0254     _flavour_history = _new_history;
0255   }
0256   const std::vector<std::pair<int, FlavInfo>> & history() {return _flavour_history;}
0257 
0258   // US spelling
0259   static const FlavInfo & current_flavor_of(const PseudoJet & particle) {return current_flavour_of(particle);}
0260   static const FlavInfo & initial_flavor_of(const PseudoJet & particle) {return initial_flavour_of(particle);}  
0261   const FlavInfo & flavor_at_step(int step) const {return flavour_at_step(step);}
0262   const FlavInfo & current_flavor() const {return current_flavour();}
0263   const FlavInfo & initial_flavor() const {return initial_flavour();}
0264 
0265 
0266  private:
0267   std::vector<std::pair<int, FlavInfo>> _flavour_history;
0268 };
0269 
0270 /// ----------------------------------------------------------------
0271 /// Class for a flavour recombiner
0272 class FlavRecombiner : public JetDefinition::DefaultRecombiner {
0273 public:
0274 
0275   enum FlavSummation {
0276     /// net flavour handling, 
0277     /// - b        = +1
0278     /// - bbar     = -1
0279     /// - b + bbar =  0
0280     /// - b + b    = +2
0281     net, // -> net_flav
0282 
0283     /// flavour is handled modulo 2
0284     /// - b        = 1
0285     /// - bbar     = 1
0286     /// - b + bbar = 0
0287     /// - b + b    = 0
0288     modulo_2,  // -> mod2_flav
0289 
0290     /// a given flavour is set to 1 if any non-zero number of quarks or anti-quarks is present. 
0291     /// So e.g. 
0292     /// - b        = 1
0293     /// - bbar     = 1
0294     /// - b + bbar = 1
0295     /// - b + b    = 1
0296     any_abs // -> any_flav
0297   };
0298 
0299   /// Constructor
0300   FlavRecombiner(FlavSummation flav_summation_in = net) : 
0301          DefaultRecombiner(), _flav_summation(flav_summation_in) {
0302     //assert(flav_summation == net && "handling of non-net flavour summation inside FlavRecombiner is still to come");
0303   }
0304 
0305   /// return the flavour summation choice
0306   FlavSummation flav_summation() const {return _flav_summation;}
0307 
0308   void preprocess(PseudoJet & p) const FASTJET_OVERRIDE {
0309 
0310     FlavInfo flav;
0311     if (p.has_user_info<FlavInfo>()) {
0312       flav = p.user_info<FlavInfo>();
0313     } else if (p.has_user_info<FlavHistory>()) {
0314       /// WARNING: not sure this is right -- depends very much on context
0315       /// (e.g. whether reclustering another algorithm's constituents, or clustering
0316       /// the flavoured jets that have come out of another algorithm)
0317       flav = p.user_info<FlavHistory>().initial_flavour();
0318     } else {
0319       throw Error("Could not identify FlavInfo or FlavHistory");
0320     }
0321 
0322     // make the initial flavour consistent with the summation choice
0323     apply_summation_choice(flav);
0324     p.set_user_info(new FlavHistory(flav));
0325   }
0326 
0327   /// Perform a recombination taking flavour into account
0328   void recombine(const PseudoJet &pa,
0329                  const PseudoJet &pb,
0330                  PseudoJet &pab) const FASTJET_OVERRIDE {
0331 
0332     // Recombine using the default recombiner
0333     DefaultRecombiner::recombine(pa, pb, pab);
0334 
0335     // put this condition early on
0336     assert(!pab.has_user_info<FlavHistory>());
0337 
0338     // Then, check if the resulting pseudojet is actually flavourless
0339     // If it isn't, add flavours. If it is, set it to a gluon
0340     FlavInfo flav = FlavHistory::current_flavour_of(pa) + FlavHistory::current_flavour_of(pb);
0341 
0342     // make the flavour consistent with the summation choice
0343     apply_summation_choice(flav);
0344 
0345     /// why don't we just do the following?
0346     pab.set_user_info(new FlavHistory(flav,pab.cluster_hist_index()));
0347 
0348   }
0349 
0350   // make the flavour consistent with the summation choice
0351   void apply_summation_choice(FlavInfo & flav) const {
0352 
0353     if      (_flav_summation == modulo_2) flav.apply_modulo_2();
0354     else if (_flav_summation == any_abs) flav.apply_any_abs();
0355     else if (_flav_summation != net) throw Error("FlavRecombiner: unknown FlavSummation");
0356   }
0357 
0358   /// returns the description of the recombiner
0359   std::string description() const FASTJET_OVERRIDE { return DefaultRecombiner::description() + " and " + to_string(_flav_summation) + " flavour recombination "; }
0360 
0361   std::string to_string(FlavSummation flav_summation) const {
0362     return (flav_summation == net ? "net_flav" : 
0363             flav_summation == modulo_2 ? "mod2_flav" : 
0364             flav_summation == any_abs ? "any_flav" : "unknown");
0365   }
0366 
0367 private:
0368   FlavSummation _flav_summation;
0369 
0370 };
0371 
0372 
0373 } // namespace contrib
0374 FASTJET_END_NAMESPACE        // defined in fastjet/internal/base.hh
0375 #endif // __FJCONTRIB_FLAVINFO_HH__