Thrust.cc

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 
00003 #include "Rivet/Projections/Thrust.hh"
00004 #include "Rivet/RivetCLHEP.hh"
00005 
00006 using namespace CLHEP;
00007 
00008 namespace Rivet {
00009 
00010 
00011   void Thrust::calcT(const vector<Vector3>& p, double& t, Vector3& taxis) const {
00012     t = 0.0;
00013     Vector3 tv, ptot;
00014     vector<Vector3> cpm(4);
00015     const size_t psize = p.size();
00016     for (size_t k = 1; k < psize; ++k) {
00017       for (size_t j = 0; j < k; ++j) {
00018         tv = p[j].cross(p[k]);
00019         ptot = Vector3();
00020         for (size_t l = 0; l < psize; ++l) {
00021           if (l != j && l != k) {
00022             if (p[l].dot(tv) > 0.0) { 
00023               ptot += p[l];
00024             } else {
00025               ptot -= p[l];
00026             }
00027           }
00028         }
00029         cpm[0] = ptot - p[j] - p[k];
00030         cpm[1] = ptot - p[j] + p[k];
00031         cpm[2] = ptot + p[j] - p[k];
00032         cpm[3] = ptot + p[j] + p[k];
00033         for (size_t i = 0; i < 4; ++i) {
00034           double tval = cpm[i].mag2();
00035           if (tval > t) {
00036             t = tval;
00037             taxis = cpm[i];
00038           }
00039         }
00040       } // j loop
00041     } // k loop
00042   }
00043 
00044 
00045 
00046   void Thrust::calcM(const vector<Vector3>& p, double& m, Vector3& maxis) const {
00047     double mval;
00048     m = 0.0;
00049     Vector3 tv, ptot;
00050     vector<Vector3> cpm;
00051     for (unsigned int j = 0; j < p.size(); ++j) {
00052       tv = p[j];
00053       ptot = Vector3();
00054       for (unsigned int l = 0; l < p.size(); ++l) {
00055         if (l != j) {
00056           if (p[l].dot(tv) > 0.0) { 
00057             ptot += p[l];
00058           } else {
00059             ptot -= p[l];
00060           }
00061         }
00062       }
00063       cpm.clear();
00064       cpm.push_back(ptot - p[j]);
00065       cpm.push_back(ptot + p[j]);
00066       for (vector<Vector3>::iterator it = cpm.begin(); it != cpm.end(); ++it) {
00067         mval = it->mag2();
00068         if (mval > m) {
00069           m = mval;
00070           maxis = *it;
00071         }
00072       }
00073     } // j loop
00074   }
00075 
00076 
00077 
00078   void Thrust::calcThrust(const FinalState& fs) {
00079     // If we've already been here, stop
00080     //if (_calculatedThrust) return;
00081 
00082     // Make a vector of the three-momenta in the final state
00083     vector<Vector3> threeMomenta;
00084     double momentumSum(0.0);
00085     for (ParticleVector::const_iterator p = fs.particles().begin(); p != fs.particles().end(); ++p) {
00086       Vector3 p3 = p->getMomentum().vect();
00087       threeMomenta.push_back(p3);
00088       momentumSum += threeMomenta.back().mag();
00089     }
00090 
00091     // Clear the caches
00092     _thrusts.clear();
00093     _thrustAxes.clear(); 
00094     //_calculatedThrust = true; // Pre-emptive strike to avoid repeats in each special case
00095 
00096 
00097     // If there are fewer than 2 visible particles, we can't do much
00098     if (threeMomenta.size() < 2) {
00099       for (int i = 0; i < 3; ++i) {
00100         _thrusts.push_back(-1);
00101         _thrustAxes.push_back(Vector3());
00102       }
00103       return;
00104     }
00105 
00106 
00107     // Handle special case of thrust = 1 if there are only 2 particles
00108     if (threeMomenta.size() == 2) {
00109       Vector3 axis;
00110       _thrusts.push_back(1.0);
00111       _thrusts.push_back(0.0);
00112       _thrusts.push_back(0.0);
00113       axis = threeMomenta[0].unit();
00114       if (axis.z() < 0) axis = -axis;
00115       _thrustAxes.push_back(axis);
00116       _thrustAxes.push_back(axis.orthogonal());
00117       _thrustAxes.push_back( _thrustAxes[0].cross(_thrustAxes[1]) );
00118       return;
00119     }
00120 
00121 
00122     // Handle special case where thrust = max{x1,x2,x3} if there are 3 particles
00123     if (threeMomenta.size() == 3) {
00124       Vector3 axis;
00125       // Order by magnitude
00126       /// @todo rsort with mag2() functor is much neater
00127       if (threeMomenta[0].mag2() < threeMomenta[1].mag2()) std::swap(threeMomenta[0], threeMomenta[1]);
00128       if (threeMomenta[0].mag2() < threeMomenta[2].mag2()) std::swap(threeMomenta[0], threeMomenta[2]);
00129       if (threeMomenta[1].mag2() < threeMomenta[2].mag2()) std::swap(threeMomenta[1], threeMomenta[2]);
00130       // Thrust
00131       axis = threeMomenta[0].unit();
00132       if (axis.z() < 0) axis = -axis;
00133       _thrusts.push_back(2.0 * threeMomenta[0].mag() / momentumSum);
00134       _thrustAxes.push_back(axis);
00135       // Thrust major (independent part of next largest momentum)
00136       axis = ( threeMomenta[1] - (axis.dot(threeMomenta[1])) * axis ).unit();
00137       if (axis.x() < 0) axis = -axis;
00138       _thrusts.push_back( (fabs(threeMomenta[1].dot(axis)) + 
00139                            fabs(threeMomenta[2].dot(axis)))  / momentumSum);
00140       _thrustAxes.push_back(axis);
00141       // Thrust minor
00142       _thrusts.push_back(0.0);
00143       _thrustAxes.push_back( _thrustAxes[0].cross(_thrustAxes[1]) );
00144       return;
00145     }
00146 
00147 
00148     // If the special cases don't apply, we have to use a general method. Here
00149     // we explicitly calculate in units of MeV using an algorithm based on 
00150     // Brandt/Dahmen Z Phys C1 (1978) and 'tasso' code from HERWIG. Re-coded
00151     // from Herwig++ implementation by Stefan Gieseke by Andy Buckley.
00152     // NB. special case with >= 4 coplanar particles will still fail. 
00153     // probably not too important... 
00154     /// @todo Thrust assumes all momenta are in the CoM system: no explicit boost is performed.
00155 
00156     // Temporary variables for calcs
00157     Vector3 axis; double val;
00158 
00159     // Get thrust
00160     calcT(threeMomenta, val, axis);
00161     _thrusts.push_back(sqrt(val) / momentumSum);
00162     if (axis.z() < 0) axis = -axis;
00163     _thrustAxes.push_back(axis.unit()); 
00164 
00165     // Get thrust major 
00166     threeMomenta.clear();
00167     for (ParticleVector::const_iterator p = fs.particles().begin(); p != fs.particles().end(); ++p) {
00168       // Get the part of each 3-momentum which is perpendicular to the thrust axis
00169       Vector3 v = p->getMomentum().vect();
00170       Vector3 vpar = v.dot(axis.unit()) * axis.unit();
00171       threeMomenta.push_back(v - vpar);
00172     }
00173     calcM(threeMomenta, val, axis);
00174     _thrusts.push_back(sqrt(val) / momentumSum);
00175     if (axis.x() < 0) axis = -axis;
00176     _thrustAxes.push_back(axis.unit()); 
00177 
00178     // Get thrust minor
00179     if (_thrustAxes[0].dot(_thrustAxes[1]) < 1e-10) {
00180       axis = _thrustAxes[0].cross(_thrustAxes[1]);
00181       _thrustAxes.push_back(axis);
00182       val = 0.0;
00183       for (ParticleVector::const_iterator p = fs.particles().begin(); p != fs.particles().end(); ++p) {
00184         val += fabs(axis * p->getMomentum().vect());
00185       }
00186       _thrusts.push_back(val / momentumSum);
00187     } else {
00188       _thrusts.push_back(-1.0);
00189       _thrustAxes.push_back(Vector3());
00190     }
00191 
00192   }
00193 
00194 
00195   void Thrust::project(const Event& e) {
00196     const FinalState& fs = e.applyProjection(*_fsproj);
00197     calcThrust(fs);
00198   }
00199 
00200 
00201 }