Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-24 09:08:22

0001 // $Id: LundEEHelpers.hh 1460 2024-12-11 13:45:48Z 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 // This file is part of FastJet contrib.
0007 //
0008 // It is free software; you can redistribute it and/or modify it under
0009 // the terms of the GNU General Public License as published by the
0010 // Free Software Foundation; either version 2 of the License, or (at
0011 // your option) any later version.
0012 //
0013 // It is distributed in the hope that it will be useful, but WITHOUT
0014 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
0015 // or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
0016 // License for more details.
0017 //
0018 // You should have received a copy of the GNU General Public License
0019 // along with this code. If not, see <http://www.gnu.org/licenses/>.
0020 //----------------------------------------------------------------------
0021 
0022 #ifndef __FASTJET_CONTRIB_EEHELPERS_HH__
0023 #define __FASTJET_CONTRIB_EEHELPERS_HH__
0024 
0025 #include "fastjet/PseudoJet.hh"
0026 #include <array>
0027 #include <limits>
0028 
0029 FASTJET_BEGIN_NAMESPACE
0030 
0031 namespace contrib{
0032 namespace lund_plane {
0033   
0034 //----------------------------------------------------------------------
0035 /// Returns the 3-vector cross-product of p1 and p2. If lightlike is false
0036 /// then the energy component is zero; if it's true the the energy component
0037 /// is arranged so that the vector is lighlike
0038 inline PseudoJet cross_product(const PseudoJet & p1, const PseudoJet & p2, bool lightlike=false) {
0039   double px = p1.py() * p2.pz() - p2.py() * p1.pz();
0040   double py = p1.pz() * p2.px() - p2.pz() * p1.px();
0041   double pz = p1.px() * p2.py() - p2.px() * p1.py();
0042 
0043   double E;
0044   if (lightlike) {
0045     E = sqrt(px*px + py*py + pz*pz);
0046   } else {
0047     E = 0.0;
0048   }
0049   return PseudoJet(px, py, pz, E);
0050 }
0051 
0052 /// Map angles to [-pi, pi]
0053 inline const double map_to_pi(const double &phi) {
0054   if (phi < -M_PI)     return phi + 2 * M_PI;
0055   else if (phi > M_PI) return phi - 2 * M_PI;
0056   else                 return phi;
0057 }
0058 
0059 inline double dot_product_3d(const PseudoJet & a, const PseudoJet & b) {
0060   return a.px()*b.px() + a.py()*b.py() + a.pz()*b.pz();
0061 }
0062 
0063 /// Returns (1-cos theta) where theta is the angle between p1 and p2
0064 inline double one_minus_costheta(const PseudoJet & p1, const PseudoJet & p2) {
0065 
0066   if (p1.m2() == 0 && p2.m2() == 0) {
0067     // use the 4-vector dot product. 
0068     // For massless particles it gives us E1*E2*(1-cos theta)
0069     return dot_product(p1,p2) / (p1.E() * p2.E());
0070   } else {
0071     double p1mod = p1.modp();
0072     double p2mod = p2.modp();
0073     double p1p2mod = p1mod*p2mod;
0074     double dot = dot_product_3d(p1,p2);
0075 
0076     if (dot > (1-std::numeric_limits<double>::epsilon()) * p1p2mod) {
0077       PseudoJet cross_result = cross_product(p1, p2, false);
0078       // the mass^2 of cross_result is equal to 
0079       // -(px^2 + py^2 + pz^2) = (p1mod*p2mod*sintheta_ab)^2
0080       // so we can get
0081       return -cross_result.m2()/(p1p2mod * (p1p2mod+dot));
0082     }
0083 
0084     return 1.0 - dot/p1p2mod;
0085     
0086   }
0087 }
0088 
0089 /// Get the angle between two planes defined by normalized vectors
0090 /// n1, n2. The sign is decided by the direction of a vector n, such that
0091 /// if n1 x n2 points in the same direction as n, the angle is positive.
0092 /// The result is in the range -pi < theta < pi.
0093 ///
0094 /// For example, labelling (x,y,z), and taking n1 = (1,0,0), 
0095 /// n2 = (0,1,0), n = (0,0,1), then the angle is +pi/2.
0096 inline double signed_angle_between_planes(const PseudoJet& n1,
0097       const PseudoJet& n2, const PseudoJet& n) {
0098 
0099   // Two vectors passed as arguments should be normalised to 1.
0100   assert(fabs(n1.modp()-1) < sqrt(std::numeric_limits<double>::epsilon()) && fabs(n2.modp()-1) < sqrt(std::numeric_limits<double>::epsilon()));
0101 
0102   double omcost = one_minus_costheta(n1,n2);
0103   double theta;
0104 
0105   // If theta ~ pi, we return pi.
0106   if(fabs(omcost-2) < sqrt(std::numeric_limits<double>::epsilon())) {
0107     theta = M_PI;
0108   } else if (omcost > sqrt(std::numeric_limits<double>::epsilon())) {
0109     double cos_theta = 1.0 - omcost;
0110     theta = acos(cos_theta);
0111   } else {
0112     // we are at small angles, so use small-angle formulas
0113     theta = sqrt(2. * omcost);
0114   }
0115 
0116   PseudoJet cp = cross_product(n1,n2);
0117   double sign = dot_product_3d(cp,n);
0118 
0119   if (sign > 0) return theta;
0120   else          return -theta;
0121 }
0122 
0123 class Matrix3 {
0124 public:
0125   /// constructs an empty matrix
0126   Matrix3() : matrix_({{{{0,0,0}},   {{0,0,0}},   {{0,0,0}}}}) {}
0127 
0128   /// constructs a diagonal matrix with "unit" along each diagonal entry
0129   Matrix3(double unit) : matrix_({{{{unit,0,0}},   {{0,unit,0}},   {{0,0,unit}}}}) {}
0130 
0131   /// constructs a matrix from the array<array<...,3>,3> object
0132   Matrix3(const std::array<std::array<double,3>,3> & mat) : matrix_(mat) {}
0133 
0134   /// returns the entry at row i, column j
0135   inline double operator()(int i, int j) const {
0136     return matrix_[i][j];
0137   }
0138 
0139   /// returns a matrix for carrying out azimuthal rotation
0140   /// around the z direction by an angle phi
0141   static Matrix3 azimuthal_rotation(double phi) {
0142     double cos_phi = cos(phi);
0143     double sin_phi = sin(phi);
0144     Matrix3 phi_rot( {{ {{cos_phi, sin_phi, 0}},
0145                  {{-sin_phi, cos_phi, 0}},
0146                  {{0,0,1}}}});
0147     return phi_rot;
0148   }
0149 
0150   /// returns a matrix for carrying out a polar-angle
0151   /// rotation, in the z-x plane, by an angle theta
0152   static Matrix3 polar_rotation(double theta) {
0153     double cos_theta = cos(theta);
0154     double sin_theta = sin(theta);
0155     Matrix3 theta_rot( {{ {{cos_theta, 0, sin_theta}},
0156                    {{0,1,0}},
0157                    {{-sin_theta, 0, cos_theta}}}});
0158     return theta_rot;
0159   }
0160 
0161   /// This provides a rotation matrix that takes the z axis to the
0162   /// direction of p. With skip_pre_rotation = false (the default), it
0163   /// has the characteristic that if p is close to the z axis then the
0164   /// azimuthal angle of anything at much larger angle is conserved.
0165   ///
0166   /// If skip_pre_rotation is true, then the azimuthal angles are not
0167   /// when p is close to the z axis.
0168   template<class T>
0169   static Matrix3 from_direction(const T & p, bool skip_pre_rotation = false) {
0170     double pt = p.pt();
0171     double modp = p.modp();
0172     double cos_theta = p.pz() / modp;
0173     double sin_theta = pt / modp;
0174     double cos_phi, sin_phi;
0175     if (pt > 0.0) {
0176       cos_phi = p.px()/pt;
0177       sin_phi = p.py()/pt;
0178     } else {
0179       cos_phi = 1.0;
0180       sin_phi = 0.0;
0181     }
0182 
0183     Matrix3 phi_rot({{ {{ cos_phi,-sin_phi, 0 }},
0184                          {{ sin_phi, cos_phi, 0 }},
0185                          {{       0,       0, 1 }} }});
0186     Matrix3 theta_rot( {{ {{ cos_theta, 0, sin_theta }},
0187                             {{         0, 1,         0 }},
0188                             {{-sin_theta, 0, cos_theta }} }});
0189 
0190     // since we have orthogonal matrices, the inverse and transpose
0191     // are identical; we use the transpose for the frontmost rotation
0192     // because 
0193     if (skip_pre_rotation) {
0194       return phi_rot * theta_rot;
0195     } else {
0196       return phi_rot * (theta_rot * phi_rot.transpose());
0197     }
0198   }
0199 
0200   template<class T>
0201   static Matrix3 from_direction_no_pre_rotn(const T & p) {
0202     return from_direction(p,true);
0203   }
0204 
0205 
0206 
0207   /// returns the transposed matrix
0208   Matrix3 transpose() const {
0209     // 00 01 02
0210     // 10 11 12
0211     // 20 21 22
0212     Matrix3 result = *this;
0213     std::swap(result.matrix_[0][1],result.matrix_[1][0]);
0214     std::swap(result.matrix_[0][2],result.matrix_[2][0]);
0215     std::swap(result.matrix_[1][2],result.matrix_[2][1]);
0216     return result;
0217   }
0218 
0219   // returns the product with another matrix
0220   Matrix3 operator*(const Matrix3 & other) const {
0221     Matrix3 result;
0222     // r_{ij} = sum_k this_{ik} & other_{kj}
0223     for (int i = 0; i < 3; i++) {
0224       for (int j = 0; j < 3; j++) {
0225         for (int k = 0; k < 3; k++) {
0226           result.matrix_[i][j] += this->matrix_[i][k] * other.matrix_[k][j];
0227         }
0228       }
0229     }
0230     return result;
0231   }
0232   
0233   friend std::ostream & operator<<(std::ostream & ostr, const Matrix3 & mat);
0234 private:
0235   std::array<std::array<double,3>,3> matrix_;
0236 };
0237 
0238 inline std::ostream & operator<<(std::ostream & ostr, const Matrix3 & mat) {
0239   ostr << mat.matrix_[0][0] << " " << mat.matrix_[0][1] << " " << mat.matrix_[0][2] << std::endl;
0240   ostr << mat.matrix_[1][0] << " " << mat.matrix_[1][1] << " " << mat.matrix_[1][2] << std::endl;
0241   ostr << mat.matrix_[2][0] << " " << mat.matrix_[2][1] << " " << mat.matrix_[2][2] << std::endl;
0242   return ostr;
0243 }
0244 
0245 /// returns the project of this matrix with the PseudoJet,
0246 /// maintaining the 4th component of the PseudoJet unchanged
0247 inline PseudoJet operator*(const Matrix3 & mat, const PseudoJet & p) {
0248   // r_{i} = m_{ij} p_j
0249   std::array<double,3> res3{{0,0,0}};
0250   for (unsigned i = 0; i < 3; i++) {
0251     for (unsigned j = 0; j < 3; j++) {
0252       res3[i] += mat(i,j) * p[j];
0253     }
0254   }
0255   // return a jet that maintains all internal pointers by
0256   // initialising the result from the input jet and
0257   // then resetting the momentum.
0258   PseudoJet result(p);
0259   // maintain the energy component as it was
0260   result.reset_momentum(res3[0], res3[1], res3[2], p[3]);
0261   return result;
0262 }
0263 
0264 } // namespace lund_plane
0265 } // namespace contrib
0266 
0267 FASTJET_END_NAMESPACE
0268 
0269 #endif // __FASTJET_CONTRIB_EEHELPERS_HH__
0270