Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:14

0001 // $Id: LundEEHelpers.hh 1345 2023-03-01 08:49:41Z salam $
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.
0091 inline double signed_angle_between_planes(const PseudoJet& n1,
0092       const PseudoJet& n2, const PseudoJet& n) {
0093 
0094   // Two vectors passed as arguments should be normalised to 1.
0095   assert(fabs(n1.modp()-1) < sqrt(std::numeric_limits<double>::epsilon()) && fabs(n2.modp()-1) < sqrt(std::numeric_limits<double>::epsilon()));
0096 
0097   double omcost = one_minus_costheta(n1,n2);
0098   double theta;
0099 
0100   // If theta ~ pi, we return pi.
0101   if(fabs(omcost-2) < sqrt(std::numeric_limits<double>::epsilon())) {
0102     theta = M_PI;
0103   } else if (omcost > sqrt(std::numeric_limits<double>::epsilon())) {
0104     double cos_theta = 1.0 - omcost;
0105     theta = acos(cos_theta);
0106   } else {
0107     // we are at small angles, so use small-angle formulas
0108     theta = sqrt(2. * omcost);
0109   }
0110 
0111   PseudoJet cp = cross_product(n1,n2);
0112   double sign = dot_product_3d(cp,n);
0113 
0114   if (sign > 0) return theta;
0115   else          return -theta;
0116 }
0117 
0118 class Matrix3 {
0119 public:
0120   /// constructs an empty matrix
0121   Matrix3() : matrix_({{{{0,0,0}},   {{0,0,0}},   {{0,0,0}}}}) {}
0122 
0123   /// constructs a diagonal matrix with "unit" along each diagonal entry
0124   Matrix3(double unit) : matrix_({{{{unit,0,0}},   {{0,unit,0}},   {{0,0,unit}}}}) {}
0125 
0126   /// constructs a matrix from the array<array<...,3>,3> object
0127   Matrix3(const std::array<std::array<double,3>,3> & mat) : matrix_(mat) {}
0128 
0129   /// returns the entry at row i, column j
0130   inline double operator()(int i, int j) const {
0131     return matrix_[i][j];
0132   }
0133 
0134   /// returns a matrix for carrying out azimuthal rotation
0135   /// around the z direction by an angle phi
0136   static Matrix3 azimuthal_rotation(double phi) {
0137     double cos_phi = cos(phi);
0138     double sin_phi = sin(phi);
0139     Matrix3 phi_rot( {{ {{cos_phi, sin_phi, 0}},
0140                  {{-sin_phi, cos_phi, 0}},
0141                  {{0,0,1}}}});
0142     return phi_rot;
0143   }
0144 
0145   /// returns a matrix for carrying out a polar-angle
0146   /// rotation, in the z-x plane, by an angle theta
0147   static Matrix3 polar_rotation(double theta) {
0148     double cos_theta = cos(theta);
0149     double sin_theta = sin(theta);
0150     Matrix3 theta_rot( {{ {{cos_theta, 0, sin_theta}},
0151                    {{0,1,0}},
0152                    {{-sin_theta, 0, cos_theta}}}});
0153     return theta_rot;
0154   }
0155 
0156   /// This provides a rotation matrix that takes the z axis to the
0157   /// direction of p. With skip_pre_rotation = false (the default), it
0158   /// has the characteristic that if p is close to the z axis then the
0159   /// azimuthal angle of anything at much larger angle is conserved.
0160   ///
0161   /// If skip_pre_rotation is true, then the azimuthal angles are not
0162   /// when p is close to the z axis.
0163   template<class T>
0164   static Matrix3 from_direction(const T & p, bool skip_pre_rotation = false) {
0165     double pt = p.pt();
0166     double modp = p.modp();
0167     double cos_theta = p.pz() / modp;
0168     double sin_theta = pt / modp;
0169     double cos_phi, sin_phi;
0170     if (pt > 0.0) {
0171       cos_phi = p.px()/pt;
0172       sin_phi = p.py()/pt;
0173     } else {
0174       cos_phi = 1.0;
0175       sin_phi = 0.0;
0176     }
0177 
0178     Matrix3 phi_rot({{ {{ cos_phi,-sin_phi, 0 }},
0179                          {{ sin_phi, cos_phi, 0 }},
0180                          {{       0,       0, 1 }} }});
0181     Matrix3 theta_rot( {{ {{ cos_theta, 0, sin_theta }},
0182                             {{         0, 1,         0 }},
0183                             {{-sin_theta, 0, cos_theta }} }});
0184 
0185     // since we have orthogonal matrices, the inverse and transpose
0186     // are identical; we use the transpose for the frontmost rotation
0187     // because 
0188     if (skip_pre_rotation) {
0189       return phi_rot * theta_rot;
0190     } else {
0191       return phi_rot * (theta_rot * phi_rot.transpose());
0192     }
0193   }
0194 
0195   template<class T>
0196   static Matrix3 from_direction_no_pre_rotn(const T & p) {
0197     return from_direction(p,true);
0198   }
0199 
0200 
0201 
0202   /// returns the transposed matrix
0203   Matrix3 transpose() const {
0204     // 00 01 02
0205     // 10 11 12
0206     // 20 21 22
0207     Matrix3 result = *this;
0208     std::swap(result.matrix_[0][1],result.matrix_[1][0]);
0209     std::swap(result.matrix_[0][2],result.matrix_[2][0]);
0210     std::swap(result.matrix_[1][2],result.matrix_[2][1]);
0211     return result;
0212   }
0213 
0214   // returns the product with another matrix
0215   Matrix3 operator*(const Matrix3 & other) const {
0216     Matrix3 result;
0217     // r_{ij} = sum_k this_{ik} & other_{kj}
0218     for (int i = 0; i < 3; i++) {
0219       for (int j = 0; j < 3; j++) {
0220         for (int k = 0; k < 3; k++) {
0221           result.matrix_[i][j] += this->matrix_[i][k] * other.matrix_[k][j];
0222         }
0223       }
0224     }
0225     return result;
0226   }
0227   
0228   friend std::ostream & operator<<(std::ostream & ostr, const Matrix3 & mat);
0229 private:
0230   std::array<std::array<double,3>,3> matrix_;
0231 };
0232 
0233 inline std::ostream & operator<<(std::ostream & ostr, const Matrix3 & mat) {
0234   ostr << mat.matrix_[0][0] << " " << mat.matrix_[0][1] << " " << mat.matrix_[0][2] << std::endl;
0235   ostr << mat.matrix_[1][0] << " " << mat.matrix_[1][1] << " " << mat.matrix_[1][2] << std::endl;
0236   ostr << mat.matrix_[2][0] << " " << mat.matrix_[2][1] << " " << mat.matrix_[2][2] << std::endl;
0237   return ostr;
0238 }
0239 
0240 /// returns the project of this matrix with the PseudoJet,
0241 /// maintaining the 4th component of the PseudoJet unchanged
0242 inline PseudoJet operator*(const Matrix3 & mat, const PseudoJet & p) {
0243   // r_{i} = m_{ij} p_j
0244   std::array<double,3> res3{{0,0,0}};
0245   for (unsigned i = 0; i < 3; i++) {
0246     for (unsigned j = 0; j < 3; j++) {
0247       res3[i] += mat(i,j) * p[j];
0248     }
0249   }
0250   // return a jet that maintains all internal pointers by
0251   // initialising the result from the input jet and
0252   // then resetting the momentum.
0253   PseudoJet result(p);
0254   // maintain the energy component as it was
0255   result.reset_momentum(res3[0], res3[1], res3[2], p[3]);
0256   return result;
0257 }
0258 
0259 } // namespace lund_plane
0260 } // namespace contrib
0261 
0262 FASTJET_END_NAMESPACE
0263 
0264 #endif // __FASTJET_CONTRIB_EEHELPERS_HH__
0265