Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-14 09:06:59

0001 // $Id: RecursiveLundEEGenerator.hh 1465 2024-12-11 14:53:28Z gsoyez $
0002 //
0003 // Copyright (c) 2018-, Frederic A. Dreyer, Keith Hamilton, Alexander Karlberg,
0004 // Gavin P. Salam, Ludovic Scyboz, Gregory Soyez, Rob Verheyen
0005 //
0006 //----------------------------------------------------------------------
0007 // This file is part of FastJet contrib.
0008 //
0009 // It is free software; you can redistribute it and/or modify it under
0010 // the terms of the GNU General Public License as published by the
0011 // Free Software Foundation; either version 2 of the License, or (at
0012 // your option) any later version.
0013 //
0014 // It is distributed in the hope that it will be useful, but WITHOUT
0015 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0016 // or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
0017 // License for more details.
0018 //
0019 // You should have received a copy of the GNU General Public License
0020 // along with this code. If not, see <http://www.gnu.org/licenses/>.
0021 //----------------------------------------------------------------------
0022 
0023 #ifndef __FASTJET_CONTRIB_RECURSIVELUNDEEGENERATOR_HH__
0024 #define __FASTJET_CONTRIB_RECURSIVELUNDEEGENERATOR_HH__
0025 
0026 #include "fastjet/contrib/LundEEHelpers.hh"
0027 
0028 #include <fastjet/internal/base.hh>
0029 #include "fastjet/tools/Recluster.hh"
0030 #include "fastjet/JetDefinition.hh"
0031 #include "fastjet/PseudoJet.hh"
0032 #include <string>
0033 #include <vector>
0034 #include <utility>
0035 #include <queue>
0036 
0037 using namespace std;
0038 
0039 FASTJET_BEGIN_NAMESPACE
0040 
0041 namespace contrib{
0042 
0043 //----------------------------------------------------------------------
0044 /// \class LundEEDeclustering
0045 /// Contains the declustering variables associated with a single node
0046 /// on the LundEE plane
0047 class LundEEDeclustering {
0048 public:
0049 
0050   /// return the pair PseudoJet, i.e. sum of the two subjets
0051   const PseudoJet & pair()  const {return pair_;}
0052   /// returns the subjet with larger transverse momentum
0053   const PseudoJet & harder() const {return harder_;}
0054   /// returns the subjet with smaller transverse momentum
0055   const PseudoJet & softer() const {return softer_;}
0056 
0057 
0058   /// returns pair().m() [cached]
0059   double m()         const {return m_;}
0060 
0061   /// returns the effective pseudorapidity of the emission [cached]
0062   double eta()       const {return eta_;}
0063 
0064   /// returns sin(theta) of the branching [cached]
0065   double sin_theta() const {return sin_theta_;}
0066 
0067   /// returns softer().modp() / (softer().modp() + harder().modp()) [cached]
0068   double z()         const {return z_;}
0069 
0070   /// returns softer().modp() * sin(theta()) [cached]
0071   double kt()        const {return kt_;}
0072 
0073   /// returns ln(softer().modp() * sin(theta())) [cached]
0074   double lnkt()      const {return lnkt_;}
0075 
0076   /// returns z() * Delta() [cached]
0077   double kappa()     const {return kappa_;}
0078 
0079   /// returns the index of the plane to which this branching belongs
0080   int iplane() const {return iplane_;}
0081 
0082   /// returns the depth of the plane on which this declustering
0083   /// occurred. 0 is the primary plane, 1 is the first set of leaves, etc. 
0084   int depth() const {return depth_;}
0085   
0086   /// returns iplane (plane index) of the leaf associated with the
0087   /// potential further declustering of the softer of the objects in
0088   /// this splitting
0089   int leaf_iplane() const {return leaf_iplane_;}
0090 
0091   /// Returns sign_s, indicating the initial parent jet index of this splitting
0092   int sign_s() const {return sign_s_;}
0093   
0094   /// returns an azimuthal angle psibar associated with this
0095   /// declustering. The actual value of psibar is arbitrary and IR
0096   /// unsafe (but see below), while differences in psibar values between
0097   /// different clusterings are meaningful.
0098   ///
0099   /// The absolute value of psibar should in general not be used,  but
0100   /// it has the following properties. (1) the first (largest-angle) splitting
0101   /// ("ref") in the positive-z hemisphere defines psibar=0 (if there is
0102   /// no splitting in that hemisphere then the first splitting in
0103   /// the negative-z hemisphere does). If the +z jet aligns along the z
0104   /// axis, then psibar of collinear splitting i in that jet is 
0105   ///
0106   ///   psibar_i = phi_i - phi_ref
0107   ///
0108   /// For the negative-z jet, the psibar of collinear splitting i is
0109   ///
0110   ///   psibar_i = phi_i - pi - phi_ref
0111   ///
0112   double psibar()    const {return psibar_;}
0113 
0114   /// (DEPRECATED)
0115   /// returns an azimuthal type angle between this declustering plane and the previous one
0116   /// Note: one should use psibar() instead, since we found that this definition of psi is
0117   /// not invariant under rotations of the event
0118   double psi()       const {return psi_;}
0119 
0120   /// update the azimuthal angle (deprecated)
0121   void set_psi(double psi) {psi_ = psi;}
0122 
0123 
0124   /// returns the coordinates in the Lund plane
0125   std::pair<double,double> const lund_coordinates() const {
0126     return std::pair<double,double>(eta_,lnkt_);
0127   }
0128 
0129   virtual ~LundEEDeclustering() {}
0130 
0131 private:
0132   int iplane_;
0133   double psi_, psibar_, lnkt_, eta_;
0134   double m_, z_, kt_, kappa_, sin_theta_;
0135   PseudoJet pair_, harder_, softer_;
0136   int  depth_ = -1, leaf_iplane_ = -1;
0137   int sign_s_;
0138 
0139 protected:
0140 
0141   /// default ctor (protected, should not normally be needed by users,
0142   /// but can be useful for derived classes)
0143   LundEEDeclustering() {}
0144 
0145   /// the constructor is protected, because users will not generally be
0146   /// constructing a LundEEDeclustering element themselves.
0147   LundEEDeclustering(const PseudoJet& pair,
0148              const PseudoJet& j1, const PseudoJet& j2,
0149              int iplane = -1, double psi = 0.0, double psibar = 0.0, int depth = -1, int leaf_iplane = -1, int sign_s = 1);
0150 
0151   friend class RecursiveLundEEGenerator;
0152 
0153 };
0154 
0155 
0156 /// Default comparison operator for LundEEDeclustering, using kt as the ordering.
0157 /// Useful when including declusterings in structures like priority queues
0158 inline bool operator<(const LundEEDeclustering& d1, const LundEEDeclustering& d2) {
0159   return d1.kt() < d2.kt();
0160 }
0161 
0162 //----------------------------------------------------------------------  
0163 /// Class to carry out Lund declustering to get anything from the
0164 /// primary Lund plane declusterings to the full Lund diagram with all
0165 /// its leaves, etc.
0166 class RecursiveLundEEGenerator {
0167  public:
0168   /// constructs a RecursiveLundEEGenerator with the specified depth.
0169   /// - depth = 0 means only primary declusterings are registered
0170   /// - depth = 1 means the first set of leaves are declustered
0171   /// - ...
0172   /// - depth < 0 means no limit, i.e. recurse through all leaves
0173   ///
0174   /// The psibar values that are set in the result Lund tree have the
0175   /// following property:
0176   ///
0177   /// - if the jet with the larger pz has splittings, then its
0178   ///   first splitting has psibar = 0
0179   /// - otherwise the first splitting of the other jet has psibar = 0
0180   ///
0181   /// Note that this makes psibar IR unsafe (because an arbitrarily soft
0182   /// splitting can be the one that gets the reference psibar=0 value), 
0183   /// but differences between psibar values are IR safe.
0184   /// 
0185   /// NB: The dynamical_psi_ref option relates to the deprecated definition of psi
0186   /// New code should use the psibar() function and dynamical_psi_ref
0187   /// is irrelevant.
0188   RecursiveLundEEGenerator(int max_depth = 0, bool dynamical_psi_ref = false) :
0189     max_depth_(max_depth), nx_(1,0,0,0), ny_(0,1,0,0), dynamical_psi_ref_(dynamical_psi_ref)
0190   {}
0191 
0192   /// destructor
0193   virtual ~RecursiveLundEEGenerator() {}
0194 
0195   /// This takes a cluster sequence with an e+e- C/A style algorithm, e.g.
0196   /// EECambridgePlugin(ycut=1.0).
0197   ///
0198   /// The output is a vector of LundEEDeclustering objects, ordered
0199   /// according to kt
0200   virtual std::vector<LundEEDeclustering> result(const ClusterSequence & cs) const {
0201     std::vector<PseudoJet> exclusive_jets = cs.exclusive_jets(2);
0202     assert(exclusive_jets.size() == 2);
0203     
0204     // order the two jets according to momentum along z axis
0205     if (exclusive_jets[0].pz() < exclusive_jets[1].pz()) {
0206       std::swap(exclusive_jets[0],exclusive_jets[1]);
0207     }
0208 
0209     PseudoJet d_ev = exclusive_jets[0] - exclusive_jets[1];
0210     lund_plane::Matrix3 rotmat = lund_plane::Matrix3::from_direction(d_ev);
0211     
0212     std::vector<LundEEDeclustering> declusterings;
0213     int depth = 0;
0214     int max_iplane_sofar = 1;
0215 
0216 // 2024-01: new code, that fixes up issue of psibar differences
0217 // between hemispheres. If RLEEG_NEWPSIBAR is false, answers
0218 // will come out wrong.
0219 #define RLEEG_NEWPSIBAR
0220 #ifdef RLEEG_NEWPSIBAR
0221     // 2024-01 -- attempt at new definition of psibar
0222     PseudoJet ref_plane;
0223     double last_psibar = 0.;
0224     bool first_time = true;
0225 
0226     for (unsigned ijet = 0; ijet < exclusive_jets.size(); ijet++) {
0227       int sign_s = ijet == 0? +1 : -1;
0228       append_to_vector(declusterings, exclusive_jets[ijet], depth, ijet, max_iplane_sofar,
0229                         rotmat, sign_s, ref_plane, last_psibar, first_time);
0230     }
0231 #else 
0232     for (unsigned ijet = 0; ijet < exclusive_jets.size(); ijet++) {
0233 
0234       // reference direction for psibar calculation
0235       PseudoJet axis = d_ev/sqrt(d_ev.modp2());
0236       PseudoJet ref_plane = axis;
0237 
0238       int sign_s = ijet == 0? +1 : -1;
0239       bool
0240       // We can pass a vector normal to a plane of reference for phi definitions
0241       append_to_vector(declusterings, exclusive_jets[ijet], depth, ijet, max_iplane_sofar,
0242                        rotmat, sign_s, ref_plane, 0., true);
0243     }
0244 #endif
0245 
0246     // a typedef to save typing below
0247     typedef LundEEDeclustering LD;
0248     // sort so that declusterings are returned in order of decreasing
0249     // kt (if result of the lambda is true, then first object appears
0250     // before the second one in the final sorted list)
0251     sort(declusterings.begin(), declusterings.end(),
0252          [](const LD & d1, const LD & d2){return d1.kt() > d2.kt();});
0253 
0254     return declusterings;
0255   }
0256   
0257  private:
0258 
0259   /// internal routine to recursively carry out the declusterings,
0260   /// adding each one to the declusterings vector; the primary
0261   /// ones are dealt with first (from large to small angle),
0262   /// and then secondary ones take place.
0263   void append_to_vector(std::vector<LundEEDeclustering> & declusterings,
0264                         const PseudoJet & jet, int depth,
0265                         int iplane, int & max_iplane_sofar,
0266                         const lund_plane::Matrix3 & rotmat, int sign_s,
0267                         PseudoJet & psibar_ref_plane,
0268                         const double & last_psibar, bool & first_time) const {
0269     PseudoJet j1, j2;
0270     if (!jet.has_parents(j1, j2)) return;
0271     if (j1.modp2() < j2.modp2()) std::swap(j1,j2);
0272 
0273     // calculation of azimuth psi
0274     lund_plane::Matrix3 new_rotmat;
0275     if (dynamical_psi_ref_) {
0276       new_rotmat = lund_plane::Matrix3::from_direction(rotmat.transpose()*(sign_s*jet)) * rotmat;
0277     } else {
0278       new_rotmat = rotmat;
0279     }
0280     PseudoJet rx = new_rotmat * nx_;
0281     PseudoJet ry = new_rotmat * ny_;
0282     PseudoJet u1 = j1/j1.modp(), u2 = j2/j2.modp();
0283     PseudoJet du = u2 - u1;
0284     double x = du.px() * rx.px() + du.py() * rx.py() + du.pz() * rx.pz();
0285     double y = du.px() * ry.px() + du.py() * ry.py() + du.pz() * ry.pz();
0286     double psi = atan2(y,x);
0287 
0288     // calculation of psibar
0289     double psibar = 0.;
0290     PseudoJet n1, n2;
0291 
0292     // First psibar for this jet
0293     if (first_time) {
0294 
0295 #ifdef RLEEG_NEWPSIBAR
0296 // 2024-01: new code, that fixes up issue of psibar differences
0297 // between hemispheres
0298       assert(last_psibar == 0.0);
0299       psibar = 0.0;
0300       n2 = lund_plane::cross_product(j1,j2);
0301       n2 /= n2.modp();
0302       psibar_ref_plane = n2;
0303       first_time = false;
0304 #else 
0305       // Compute the angle between the planes spanned by (some axis,j1) and by (j1,j2)
0306       n1 = lund_plane::cross_product(psibar_ref_plane,j1);
0307       n2 = lund_plane::cross_product(j1,j2);
0308 
0309       double signed_angle = 0.;
0310       n2 /= n2.modp();
0311       if (n1.modp() != 0) {
0312         n1 /= n1.modp();
0313         signed_angle = lund_plane::signed_angle_between_planes(n1,n2,j1);
0314       }
0315 
0316       psibar = lund_plane::map_to_pi(j1.phi() + signed_angle);
0317 #endif
0318     }
0319     // Else take the value of psibar_i and the plane from the last splitting to define psibar_{i+1}
0320     // The signed angle is multiplied by sign_s (+1 for the +z hemisphere, -1 for the -z hemisphere)
0321     // to take the correct orientation into account.
0322     else {
0323       n2 = lund_plane::cross_product(j1,j2);
0324       n2 /= n2.modp();
0325       psibar = lund_plane::map_to_pi(last_psibar + sign_s*lund_plane::signed_angle_between_planes(psibar_ref_plane, n2, j1));
0326     }
0327 
0328     int leaf_iplane = -1;
0329     // we will recurse into the softer "parent" only if the depth is
0330     // not yet at its limit or if there is no limit on the depth (max_depth<0)
0331     bool recurse_into_softer = (depth < max_depth_ || max_depth_ < 0);
0332     if (recurse_into_softer) {
0333       max_iplane_sofar += 1;
0334       leaf_iplane = max_iplane_sofar;
0335     }
0336     
0337     LundEEDeclustering declust(jet, j1, j2, iplane, psi, psibar, depth, leaf_iplane, sign_s);
0338     declusterings.push_back(declust);
0339 
0340     // now recurse
0341     // for the definition of psibar, we recursively pass the last splitting plane (normal to n2) and the last value
0342     // of psibar
0343     bool lcl_first_time = false;
0344     append_to_vector(declusterings, j1, depth, iplane, max_iplane_sofar, new_rotmat, sign_s, n2, psibar, lcl_first_time);
0345     if (recurse_into_softer) {
0346       append_to_vector(declusterings, j2, depth+1, leaf_iplane, max_iplane_sofar, new_rotmat, sign_s, n2, psibar, lcl_first_time);
0347     }
0348   }
0349   
0350   int max_depth_ = 0;
0351   /// vectors used to help define psi
0352   PseudoJet nx_;
0353   PseudoJet ny_;
0354   bool dynamical_psi_ref_;
0355 };
0356   
0357 } // namespace contrib
0358 
0359 FASTJET_END_NAMESPACE
0360 
0361 /// for output of declustering information
0362 std::ostream & operator<<(std::ostream & ostr, const fastjet::contrib::LundEEDeclustering & d);
0363 
0364 #endif  // __FASTJET_CONTRIB_RECURSIVELUNDEEGENERATOR_HH__