Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

ClusterSequence.cc

Go to the documentation of this file.
00001 //STARTHEADER
00002 // $Id: ClusterSequence.cc 331 2006-10-09 16:47:53Z salam $
00003 //
00004 // Copyright (c) 2005-2006, Matteo Cacciari and Gavin Salam
00005 //
00006 //----------------------------------------------------------------------
00007 // This file is part of FastJet.
00008 //
00009 //  FastJet is free software; you can redistribute it and/or modify
00010 //  it under the terms of the GNU General Public License as published by
00011 //  the Free Software Foundation; either version 2 of the License, or
00012 //  (at your option) any later version.
00013 //
00014 //  The algorithms that underlie FastJet have required considerable
00015 //  development and are described in hep-ph/0512210. If you use
00016 //  FastJet as part of work towards a scientific publication, please
00017 //  include a citation to the FastJet paper.
00018 //
00019 //  FastJet is distributed in the hope that it will be useful,
00020 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022 //  GNU General Public License for more details.
00023 //
00024 //  You should have received a copy of the GNU General Public License
00025 //  along with FastJet; if not, write to the Free Software
00026 //  Foundation, Inc.:
00027 //      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00028 //----------------------------------------------------------------------
00029 //ENDHEADER
00030 
00031 #include "fastjet/Error.hh"
00032 #include "fastjet/PseudoJet.hh"
00033 #include "fastjet/ClusterSequence.hh"
00034 #include<iostream>
00035 #include<sstream>
00036 #include<cmath>
00037 #include<cstdlib>
00038 #include<cassert>
00039 #include<string>
00040 
00041 FASTJET_BEGIN_NAMESPACE      // defined in fastjet/internal/base.hh
00042 
00043 using namespace std;
00044 
00046 JetFinder ClusterSequence::_default_jet_finder = kt_algorithm;
00047 //
00048 
00049 
00050 //----------------------------------------------------------------------
00051 void ClusterSequence::_initialise_and_run (
00052                                   const double & R,
00053                                   const Strategy & strategy,
00054                                   const bool & writeout_combinations) {
00055 
00056   JetDefinition jet_def(_default_jet_finder, R, strategy);
00057   _initialise_and_run(jet_def, writeout_combinations);
00058 }
00059 
00060 
00061 //----------------------------------------------------------------------
00062 void ClusterSequence::_initialise_and_run (
00063                                   const JetDefinition & jet_def,
00064                                   const bool & writeout_combinations) {
00065 
00066   // transfer all relevant info into internal variables
00067   _decant_options(jet_def, writeout_combinations);
00068 
00069   // set up the history entries for the initial particles (those
00070   // currently in _jets)
00071   _fill_initial_history();
00072 
00073 //  // automatically redefine the strategy according to N if that is
00074 //  // what the user requested
00075 //  if (_strategy == Best) {
00076 //    int N = _jets.size();
00077 //#ifndef DROP_CGAL
00078 //    if (N > 14500/_Rparam) { // empirical observation of how it scales with R
00079 //      _strategy = NlnN; }    // see GPS CCN27-57 (Numbers have changed since 
00080 //    else                     // introducing N2MinHeapTiled; scaling is approx.)
00081 //#endif  // DROP_CGAL
00082 //      if (N > 450) {
00083 //      _strategy = N2MinHeapTiled;
00084 //    }
00085 //    else if (N > 55*max(0.5,min(1.0,_Rparam))) {// empirical scaling with R
00086 //      _strategy = N2Tiled;
00087 //    } else {
00088 //      _strategy = N2Plain;
00089 //    }
00090 //  }
00091 
00092 
00093   // automatically redefine the strategy according to N if that is
00094   // what the user requested -- transition points (and especially
00095   // their R-dependence) are based on empirical observations for a
00096   // R=0.4, 0.7 and 1.0, running on toth (3.4GHz, Pentium IV D [dual
00097   // core] with 2MB of cache).
00098   if (_strategy == Best) {
00099     int N = _jets.size();
00100     if (N > 6200/pow(_Rparam,2.0) 
00101         && jet_def.jet_finder() == cambridge_algorithm) {
00102       _strategy = NlnNCam;}
00103     else
00104 #ifndef DROP_CGAL
00105     if (N > 16000/pow(_Rparam,1.15)) {
00106       _strategy = NlnN; }   
00107     else                    
00108 #endif  // DROP_CGAL
00109       if (N > 450) {
00110       _strategy = N2MinHeapTiled;
00111     }
00112     else if (N > 55*max(0.5,min(1.0,_Rparam))) {// empirical scaling with R
00113       _strategy = N2Tiled;
00114     } else {
00115       _strategy = N2Plain;
00116     }
00117   }
00118 
00119 
00120   // run the code containing the selected strategy
00121   if (_strategy == NlnN || _strategy == NlnN3pi 
00122       || _strategy == NlnN4pi ) {
00123     this->_delaunay_cluster();
00124   } else if (_strategy ==  N3Dumb ) {
00125     this->_really_dumb_cluster();
00126   } else if (_strategy == N2Tiled) {
00127     this->_faster_tiled_N2_cluster();
00128   } else if (_strategy == N2PoorTiled) {
00129     this->_tiled_N2_cluster();
00130   } else if (_strategy == N2Plain) {
00131     this->_simple_N2_cluster();
00132   } else if (_strategy == N2MinHeapTiled) {
00133     this->_minheap_faster_tiled_N2_cluster();
00134   } else if (_strategy == NlnNCam4pi) {
00135     this->_CP2DChan_cluster();
00136   } else if (_strategy == NlnNCam2pi2R) {
00137     this->_CP2DChan_cluster_2pi2R();
00138   } else if (_strategy == NlnNCam) {
00139     this->_CP2DChan_cluster_2piMultD();
00140   } else {
00141     ostringstream err;
00142     err << "Unrecognised value for strategy: "<<_strategy;
00143     throw Error(err.str());
00144     //assert(false);
00145   }
00146 }
00147 
00148 
00149 // this needs to be defined outside the class definition.
00150 bool ClusterSequence::_first_time = true;
00151 //----------------------------------------------------------------------
00152 // prints a banner on the first call
00153 void ClusterSequence::_print_banner() {
00154 
00155   if (!_first_time) {return;}
00156   _first_time = false;
00157   
00158   cout << "#---------------------------------------------------------------------\n";
00159   cout << "#                        FastJet release 2.0                          \n";
00160   cout << "#            Written by Matteo Cacciari and Gavin Salam               \n"; 
00161   cout << "#            http://www.lpthe.jussieu.fr/~salam/fastjet               \n"; 
00162   cout << "#                                                                     \n";
00163   cout << "# Longitudinally invariant Kt Jet clustering, using the fast geometric\n";
00164   cout << "# algorithms of hep-ph/0512210 (please cite this if you use FastJet)  \n";
00165 #ifndef DROP_CGAL
00166   cout << "# This package makes use of the CGAL library: http://www.cgal.org/    \n";
00167 #endif  // DROP_CGAL
00168   cout << "# This package incorporates T.Chan's closest-pair algorithm, Proc. 13th     \n";
00169   cout << "# ACM-SIAM Symposium on Discrete Algorithms, pp.472-473, 2002\n";
00170   cout << "#---------------------------------------------------------------------\n";
00171 }
00172 
00173 //----------------------------------------------------------------------
00174 // transfer all relevant info into internal variables
00175 void ClusterSequence::_decant_options(const JetDefinition & jet_def,
00176                                       const bool & writeout_combinations) {
00177 
00178   // let the user know what's going on
00179   _print_banner();
00180 
00181   // make a local copy of the jet definition (for future use?)
00182   _jet_def = jet_def;
00183   
00184   _writeout_combinations = writeout_combinations;
00185   _jet_finder = jet_def.jet_finder();
00186   _Rparam = jet_def.R();  _R2 = _Rparam*_Rparam; _invR2 = 1.0/_R2;
00187   _strategy = jet_def.strategy();
00188 }
00189 
00190 
00191 //----------------------------------------------------------------------
00192 // initialise the history in a standard way
00193 void ClusterSequence::_fill_initial_history () {
00194 
00195   if (_jets.size() == 0) {throw Error("Cannot run jet-finder on empty event");}
00196 
00197   // reserve sufficient space for everything
00198   _jets.reserve(_jets.size()*2);
00199   _history.reserve(_jets.size()*2);
00200 
00201   for (int i = 0; i < static_cast<int>(_jets.size()) ; i++) {
00202     history_element element;
00203     element.parent1 = InexistentParent;
00204     element.parent2 = InexistentParent;
00205     element.child   = Invalid;
00206     element.jetp_index = i;
00207     element.dij     = 0.0;
00208     element.max_dij_so_far = 0.0;
00209 
00210     _history.push_back(element);
00211     
00212     // get cross-referencing right from PseudoJets
00213     _jets[i].set_cluster_hist_index(i);
00214   }
00215   _initial_n = _jets.size();
00216 }
00217 
00218 
00219 //----------------------------------------------------------------------
00220 // Return the component corresponding to the specified index.
00221 // taken from CLHEP
00222 string ClusterSequence::strategy_string ()  const {
00223   string strategy;
00224   switch(_strategy) {
00225   case NlnN:
00226     strategy = "NlnN"; break;
00227   case NlnN3pi:
00228     strategy = "NlnN3pi"; break;
00229   case NlnN4pi:
00230     strategy = "NlnN4pi"; break;
00231   case N2Plain:
00232     strategy = "N2Plain"; break;
00233   case N2Tiled:
00234     strategy = "N2Tiled"; break;
00235   case N2MinHeapTiled:
00236     strategy = "N2MinHeapTiled"; break;
00237   case N2PoorTiled:
00238     strategy = "N2PoorTiled"; break;
00239   case N3Dumb:
00240     strategy = "N3Dumb"; break;
00241   case NlnNCam4pi:
00242     strategy = "NlnNCam4pi"; break;
00243   case NlnNCam2pi2R:
00244     strategy = "NlnNCam2pi2R"; break;
00245   case NlnNCam:
00246     strategy = "NlnNCam"; break; // 2piMultD
00247   default:
00248     strategy = "Unrecognized";
00249   }
00250   return strategy;
00251 }  
00252 
00253 
00254 //----------------------------------------------------------------------
00255 // return all inclusive jets with pt > ptmin
00256 vector<PseudoJet> ClusterSequence::inclusive_jets (const double & ptmin) const{
00257   double dcut = ptmin*ptmin;
00258   int i = _history.size() - 1; // last jet
00259   vector<PseudoJet> jets;
00260   if (_jet_finder == kt_algorithm) {
00261     while (i >= 0) {
00262       // with our specific definition of dij and diB (i.e. R appears only in 
00263       // dij), then dij==diB is the same as the jet.perp2() and we can exploit
00264       // this in selecting the jets...
00265       if (_history[i].max_dij_so_far < dcut) {break;}
00266       if (_history[i].parent2 == BeamJet && _history[i].dij >= dcut) {
00267         // for beam jets
00268         int parent1 = _history[i].parent1;
00269         jets.push_back(_jets[_history[parent1].jetp_index]);}
00270       i--;
00271     }
00272   } else if (_jet_finder == cambridge_algorithm) {
00273     while (i >= 0) {
00274       // inclusive jets are all at end of clustering sequence in the
00275       // Cambridge algorithm -- so if we find a non-exclusive jet, then
00276       // we can exit
00277       if (_history[i].parent2 != BeamJet) {break;}
00278       int parent1 = _history[i].parent1;
00279       const PseudoJet & jet = _jets[_history[parent1].jetp_index];
00280       if (jet.perp2() >= dcut) {jets.push_back(jet);}
00281       i--;
00282     }
00283   } else {throw Error("Unrecognized jet algorithm");}
00284   return jets;
00285 }
00286 
00287 
00288 //----------------------------------------------------------------------
00289 // return the number of exclusive jets that would have been obtained
00290 // running the algorithm in exclusive mode with the given dcut
00291 int ClusterSequence::n_exclusive_jets (const double & dcut) const {
00292 
00293   // first locate the point where clustering would have stopped (i.e. the
00294   // first time max_dij_so_far > dcut)
00295   int i = _history.size() - 1; // last jet
00296   while (i >= 0) {
00297     if (_history[i].max_dij_so_far <= dcut) {break;}
00298     i--;
00299   }
00300   int stop_point = i + 1;
00301   // relation between stop_point, njets assumes one extra jet disappears
00302   // at each clustering.
00303   int njets = 2*_initial_n - stop_point;
00304   return njets;
00305 }
00306 
00307 //----------------------------------------------------------------------
00308 // return all exclusive jets that would have been obtained running
00309 // the algorithm in exclusive mode with the given dcut
00310 vector<PseudoJet> ClusterSequence::exclusive_jets (const double & dcut) const {
00311   int njets = n_exclusive_jets(dcut);
00312   return exclusive_jets(njets);
00313 }
00314 
00315 
00316 //----------------------------------------------------------------------
00317 // return the jets obtained by clustering the event to n jets.
00318 vector<PseudoJet> ClusterSequence::exclusive_jets (const int & njets) const {
00319 
00320   // make sure the user does not ask for more than jets than there
00321   // were particles in the first place.
00322   assert (njets <= _initial_n);
00323 
00324   // calculate the point where we have to stop the clustering.
00325   // relation between stop_point, njets assumes one extra jet disappears
00326   // at each clustering.
00327   int stop_point = 2*_initial_n - njets;
00328 
00329   // some sanity checking to make sure that e+e- does not give us
00330   // surprises (should we ever implement e+e-)...
00331   if (2*_initial_n != static_cast<int>(_history.size())) {
00332     ostringstream err;
00333     err << "2*_initial_n != _history.size() -- this endangers internal assumptions!\n";
00334     throw Error(err.str());
00335     //assert(false);
00336   }
00337 
00338   // now go forwards and reconstitute the jets that we have --
00339   // basically for any history element, see if the parent jets to
00340   // which it refers were created before the stopping point -- if they
00341   // were then add them to the list, otherwise they are subsequent
00342   // recombinations of the jets that we are looking for.
00343   vector<PseudoJet> jets;
00344   for (unsigned int i = stop_point; i < _history.size(); i++) {
00345     int parent1 = _history[i].parent1;
00346     if (parent1 < stop_point) {
00347       jets.push_back(_jets[_history[parent1].jetp_index]);
00348     }
00349     int parent2 = _history[i].parent2;
00350     if (parent2 < stop_point && parent2 > 0) {
00351       jets.push_back(_jets[_history[parent2].jetp_index]);
00352     }
00353     
00354   }
00355 
00356   // sanity check...
00357   if (static_cast<int>(jets.size()) != njets) {
00358     ostringstream err;
00359     err << "ClusterSequence::exclusive_jets: size of returned vector ("
00360          <<jets.size()<<") does not coincide with requested number of jets ("
00361          <<njets<<")";
00362     throw Error(err.str());
00363   }
00364 
00365   return jets;
00366 }
00367 
00368 //----------------------------------------------------------------------
00371 double ClusterSequence::exclusive_dmerge (const int & njets) const {
00372   assert(njets > 0);
00373   if (njets >= _initial_n) {return 0.0;}
00374   return _history[2*_initial_n-njets-1].dij;
00375 }
00376 
00377 
00378 //----------------------------------------------------------------------
00383 double ClusterSequence::exclusive_dmerge_max (const int & njets) const {
00384   assert(njets > 0);
00385   if (njets >= _initial_n) {return 0.0;}
00386   return _history[2*_initial_n-njets-1].max_dij_so_far;
00387 }
00388 
00389 
00390 //----------------------------------------------------------------------
00391 // return a vector of the particles that make up a jet
00392 vector<PseudoJet> ClusterSequence::constituents (const PseudoJet & jet) const {
00393   vector<PseudoJet> subjets;
00394   add_constituents(jet, subjets);
00395   return subjets;
00396 }
00397 
00398 
00399 //----------------------------------------------------------------------
00400 // recursive routine that adds on constituents of jet to the subjet_vector
00401 void ClusterSequence::add_constituents (
00402            const PseudoJet & jet, vector<PseudoJet> & subjet_vector) const {
00403   // find out position in cluster history
00404   int i = jet.cluster_hist_index();
00405   int parent1 = _history[i].parent1;
00406   int parent2 = _history[i].parent2;
00407 
00408   if (parent1 == InexistentParent) {
00409     // It is an original particle (labelled by its parent having value
00410     // InexistentParent), therefore add it on to the subjet vector
00411     subjet_vector.push_back(jet);
00412     return;
00413   } 
00414 
00415   // add parent 1
00416   add_constituents(_jets[_history[parent1].jetp_index], subjet_vector);
00417 
00418   // see if parent2 is a real jet; if it is then add its constituents
00419   if (parent2 != BeamJet) {
00420     add_constituents(_jets[_history[parent2].jetp_index], subjet_vector);
00421   }
00422 }
00423 
00424 
00425 
00426 //----------------------------------------------------------------------
00427 // initialise the history in a standard way
00428 void ClusterSequence::_add_step_to_history (
00429                const int & step_number, const int & parent1, 
00430                const int & parent2, const int & jetp_index,
00431                const double & dij) {
00432 
00433   history_element element;
00434   element.parent1 = parent1;
00435   element.parent2 = parent2;
00436   element.jetp_index = jetp_index;
00437   element.child = Invalid;
00438   element.dij   = dij;
00439   element.max_dij_so_far = max(dij,_history[_history.size()-1].max_dij_so_far);
00440   _history.push_back(element);
00441 
00442   int local_step = _history.size()-1;
00443   assert(local_step == step_number);
00444 
00445   assert(parent1 >= 0);
00446   _history[parent1].child = local_step;
00447   if (parent2 >= 0) {_history[parent2].child = local_step;}
00448 
00449   // get cross-referencing right from PseudoJets
00450   if (jetp_index != Invalid) {
00451     assert(jetp_index >= 0);
00452     //cout << _jets.size() <<" "<<jetp_index<<"\n";
00453     _jets[jetp_index].set_cluster_hist_index(local_step);
00454   }
00455 
00456   if (_writeout_combinations) {
00457     cout << local_step << ": " 
00458          << parent1 << " with " << parent2
00459          << "; y = "<< dij<<endl;
00460   }
00461 
00462 }
00463 
00464 
00465 
00466 
00467 //======================================================================
00468 // Return an order in which to read the history such that _history[order[i]] 
00469 // will always correspond to the same set of consituent particles if 
00470 // two branching histories are equivalent in terms of the particles
00471 // contained in any given pseudojet.
00472 vector<int> ClusterSequence::unique_history_order() const {
00473 
00474   // first construct an array that will tell us the lowest constituent
00475   // of a given jet -- this will always be one of the original
00476   // particles, whose order is well defined and so will help us to
00477   // follow the tree in a unique manner.
00478   valarray<int> lowest_constituent(_history.size());
00479   int hist_n = _history.size();
00480   lowest_constituent = hist_n; // give it a large number
00481   for (int i = 0; i < hist_n; i++) {
00482     // sets things up for the initial partons
00483     lowest_constituent[i] = min(lowest_constituent[i],i); 
00484     // propagates them through to the children of this parton
00485     if (_history[i].child > 0) lowest_constituent[_history[i].child] 
00486       = min(lowest_constituent[_history[i].child],lowest_constituent[i]);
00487   }
00488 
00489   // establish an array for what we have and have not extracted so far
00490   valarray<bool> extracted(_history.size()); extracted = false;
00491   vector<int> unique_tree;
00492   unique_tree.reserve(_history.size());
00493 
00494   // now work our way through the tree
00495   for (unsigned i = 0; i < n_particles(); i++) {
00496     if (!extracted[i]) {
00497       unique_tree.push_back(i);
00498       extracted[i] = true;
00499       _extract_tree_children(i, extracted, lowest_constituent, unique_tree);
00500     }
00501   }
00502 
00503   return unique_tree;
00504 }
00505 
00506 //======================================================================
00507 // helper for unique_history_order
00508 void ClusterSequence::_extract_tree_children(
00509        int position, 
00510        valarray<bool> & extracted, 
00511        const valarray<int> & lowest_constituent,
00512        vector<int> & unique_tree) const {
00513   if (!extracted[position]) {
00514     // that means we may have unidentified parents around, so go and
00515     // collect them (extracted[position]) will then be made true)
00516     _extract_tree_parents(position,extracted,lowest_constituent,unique_tree);
00517   } 
00518   
00519   // now look after the children...
00520   int child = _history[position].child;
00521   if (child  >= 0) _extract_tree_children(child,extracted,lowest_constituent,unique_tree);
00522 }
00523 
00524 //======================================================================
00525 // helper for unique_history_order
00526 void ClusterSequence::_extract_tree_parents(
00527        int position, 
00528        valarray<bool> & extracted, 
00529        const valarray<int> & lowest_constituent,
00530        vector<int> & unique_tree) const {
00531 
00532   if (!extracted[position]) {
00533     int parent1 = _history[position].parent1;
00534     int parent2 = _history[position].parent2;
00535     // where relevant order parents so that we will first treat the
00536     // one containing the smaller "lowest_constituent"
00537     if (parent1 >= 0 && parent2 >= 0) {
00538       if (lowest_constituent[parent1] > lowest_constituent[parent2]) 
00539         swap(parent1, parent2);
00540     }
00541     // then actually run through the parents to extract the constituents...
00542     if (parent1 >= 0 && !extracted[parent1]) 
00543       _extract_tree_parents(parent1,extracted,lowest_constituent,unique_tree);
00544     if (parent2 >= 0 && !extracted[parent2]) 
00545       _extract_tree_parents(parent2,extracted,lowest_constituent,unique_tree);
00546     // finally declare this position to be accounted for and push it
00547     // onto our list.
00548     unique_tree.push_back(position);
00549     extracted[position] = true;
00550   }
00551 }
00552 
00553 
00554 //======================================================================
00558 void ClusterSequence::_do_ij_recombination_step(
00559                                const int & jet_i, const int & jet_j, 
00560                                const double & dij, 
00561                                int & newjet_k) {
00562 
00563   // create the new jet
00564   _jets.push_back(_jets[jet_i] + _jets[jet_j]);
00565 
00566   // get its index
00567   newjet_k = _jets.size()-1;
00568 
00569   // get history index
00570   int newstep_k = _history.size();
00571   // and provide jet with the info
00572   _jets[newjet_k].set_cluster_hist_index(newstep_k);
00573 
00574   // finally sort out the history 
00575   int hist_i = _jets[jet_i].cluster_hist_index();
00576   int hist_j = _jets[jet_j].cluster_hist_index();
00577 
00578   _add_step_to_history(newstep_k, min(hist_i, hist_j), max(hist_i,hist_j),
00579                        newjet_k, dij);
00580 
00581 }
00582 
00583 
00584 //======================================================================
00587 void ClusterSequence::_do_iB_recombination_step(
00588                                   const int & jet_i, const double & diB) {
00589   // get history index
00590   int newstep_k = _history.size();
00591 
00592   // recombine the jet with the beam
00593   _add_step_to_history(newstep_k,_jets[jet_i].cluster_hist_index(),BeamJet,
00594                        Invalid, diB);
00595 
00596 }
00597 
00598 FASTJET_END_NAMESPACE
00599 

Generated on Thu Oct 12 17:36:34 2006 for fastjet by  doxygen 1.4.2