fastjet 2.4.5
|
00001 //STARTHEADER 00002 // $Id: ClusterSequence.cc 3162 2013-07-17 14:40:02Z salam $ 00003 // 00004 // Copyright (c) 2005-2009, Matteo Cacciari, Gavin Salam and Gregory Soyez 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 "fastjet/version.hh" // stores the current version number 00035 #include<iostream> 00036 #include<sstream> 00037 #include<fstream> 00038 #include<cmath> 00039 #include<cstdlib> 00040 #include<cassert> 00041 #include<string> 00042 #include<set> 00043 00044 FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh 00045 00046 using namespace std; 00047 00049 JetAlgorithm ClusterSequence::_default_jet_algorithm = kt_algorithm; 00050 // 00051 00052 00053 // destructor that does nothing 00054 ClusterSequence::~ClusterSequence () {} 00055 00056 //---------------------------------------------------------------------- 00057 void ClusterSequence::_initialise_and_run ( 00058 const double & R, 00059 const Strategy & strategy, 00060 const bool & writeout_combinations) { 00061 00062 JetDefinition jet_def(_default_jet_algorithm, R, strategy); 00063 _initialise_and_run(jet_def, writeout_combinations); 00064 } 00065 00066 00067 //---------------------------------------------------------------------- 00068 void ClusterSequence::_initialise_and_run ( 00069 const JetDefinition & jet_def, 00070 const bool & writeout_combinations) { 00071 00072 // transfer all relevant info into internal variables 00073 _decant_options(jet_def, writeout_combinations); 00074 00075 // set up the history entries for the initial particles (those 00076 // currently in _jets) 00077 _fill_initial_history(); 00078 00079 // don't run anything if the event is empty 00080 if (n_particles() == 0) return; 00081 00082 // ----- deal with special cases: plugins & e+e- ------ 00083 if (_jet_algorithm == plugin_algorithm) { 00084 // allows plugin_xyz() functions to modify cluster sequence 00085 _plugin_activated = true; 00086 // let the plugin do its work here 00087 _jet_def.plugin()->run_clustering( (*this) ); 00088 _plugin_activated = false; 00089 return; 00090 } else if (_jet_algorithm == ee_kt_algorithm || 00091 _jet_algorithm == ee_genkt_algorithm) { 00092 // ignore requested strategy 00093 _strategy = N2Plain; 00094 if (_jet_algorithm == ee_kt_algorithm) { 00095 // make sure that R is large enough so that "beam" recomb only 00096 // occurs when a single particle is left 00097 // Normally, this should be automatically set to 4 from JetDefinition 00098 assert(_Rparam > 2.0); 00099 // this is used to renormalise the dij to get a "standard" form 00100 // and our convention in e+e- will be different from that 00101 // in long.inv case; NB: _invR2 name should be changed -> _renorm_dij? 00102 _invR2 = 1.0; 00103 } else { 00104 // as of 2009-01-09, choose R to be an angular distance, in 00105 // radians. Since the algorithm uses 2(1-cos(theta)) as its 00106 // squared angular measure, make sure that the _R2 is defined 00107 // in a similar way. 00108 if (_Rparam > pi) { 00109 // choose a value that ensures that back-to-back particles will 00110 // always recombine 00111 //_R2 = 4.0000000000001; 00112 _R2 = 2 * ( 3.0 + cos(_Rparam) ); 00113 } else { 00114 _R2 = 2 * ( 1.0 - cos(_Rparam) ); 00115 } 00116 _invR2 = 1.0/_R2; 00117 } 00118 //_simple_N2_cluster<EEBriefJet>(); 00119 _simple_N2_cluster_EEBriefJet(); 00120 return; 00121 } 00122 00123 00124 // automatically redefine the strategy according to N if that is 00125 // what the user requested -- transition points (and especially 00126 // their R-dependence) are based on empirical observations for a 00127 // R=0.4, 0.7 and 1.0, running on toth (3.4GHz, Pentium IV D [dual 00128 // core] with 2MB of cache). 00129 if (_strategy == Best) { 00130 int N = _jets.size(); 00131 if (N > 6200/pow(_Rparam,2.0) 00132 && jet_def.jet_algorithm() == cambridge_algorithm) { 00133 _strategy = NlnNCam;} 00134 else 00135 #ifndef DROP_CGAL 00136 if ((N > 16000/pow(_Rparam,1.15) && jet_def.jet_algorithm() != antikt_algorithm) 00137 || N > 35000/pow(_Rparam,1.15)) { 00138 _strategy = NlnN; } 00139 else 00140 #endif // DROP_CGAL 00141 if (N > 450) { 00142 _strategy = N2MinHeapTiled; 00143 } 00144 else if (N > 55*max(0.5,min(1.0,_Rparam))) {// empirical scaling with R 00145 _strategy = N2Tiled; 00146 } else { 00147 _strategy = N2Plain; 00148 } 00149 } 00150 00151 00152 // run the code containing the selected strategy 00153 if (_strategy == NlnN || _strategy == NlnN3pi 00154 || _strategy == NlnN4pi ) { 00155 this->_delaunay_cluster(); 00156 } else if (_strategy == N3Dumb ) { 00157 this->_really_dumb_cluster(); 00158 } else if (_strategy == N2Tiled) { 00159 this->_faster_tiled_N2_cluster(); 00160 } else if (_strategy == N2PoorTiled) { 00161 this->_tiled_N2_cluster(); 00162 } else if (_strategy == N2Plain) { 00163 // BriefJet provides standard long.invariant kt alg. 00164 //this->_simple_N2_cluster<BriefJet>(); 00165 this->_simple_N2_cluster_BriefJet(); 00166 } else if (_strategy == N2MinHeapTiled) { 00167 this->_minheap_faster_tiled_N2_cluster(); 00168 } else if (_strategy == NlnNCam4pi) { 00169 this->_CP2DChan_cluster(); 00170 } else if (_strategy == NlnNCam2pi2R) { 00171 this->_CP2DChan_cluster_2pi2R(); 00172 } else if (_strategy == NlnNCam) { 00173 this->_CP2DChan_cluster_2piMultD(); 00174 } else { 00175 ostringstream err; 00176 err << "Unrecognised value for strategy: "<<_strategy; 00177 throw Error(err.str()); 00178 //assert(false); 00179 } 00180 } 00181 00182 00183 // these needs to be defined outside the class definition. 00184 bool ClusterSequence::_first_time = true; 00185 int ClusterSequence::_n_exclusive_warnings = 0; 00186 00187 00188 //---------------------------------------------------------------------- 00189 // the version string 00190 string fastjet_version_string() { 00191 return "FastJet version "+string(fastjet_version); 00192 } 00193 00194 00195 //---------------------------------------------------------------------- 00196 // prints a banner on the first call 00197 void ClusterSequence::_print_banner() { 00198 00199 if (!_first_time) {return;} 00200 _first_time = false; 00201 00202 00203 //Symp. Discr. Alg, p.472 (2002) and CGAL (http://www.cgal.org); 00204 00205 cout << "#--------------------------------------------------------------------------\n"; 00206 cout << "# FastJet release " << fastjet_version << endl; 00207 cout << "# Written by M. Cacciari, G.P. Salam and G. Soyez \n"; 00208 cout << "# http://www.fastjet.fr \n"; 00209 cout << "# \n"; 00210 cout << "# Longitudinally invariant Kt, anti-Kt, and inclusive Cambridge/Aachen \n"; 00211 cout << "# clustering using fast geometric algorithms, with jet areas and optional\n"; 00212 cout << "# external jet-finder plugins. If you use this code towards a scientific \n"; 00213 cout << "# publication please cite EPJC72(2012)1896 [arXiv:1111.6097] and \n"; 00214 cout << "# optionally PLB641(2006)57 [hep-ph/0512210]. \n"; 00215 cout << "# \n"; 00216 cout << "# This package uses T.Chan's closest pair algorithm, Proc.13th ACM-SIAM \n"; 00217 cout << "# Symp. Discr. Alg, p.472 (2002), S.Fortune's Voronoi algorithm and code" ; 00218 #ifndef DROP_CGAL 00219 cout << endl << "# and CGAL: http://www.cgal.org/"; 00220 #endif // DROP_CGAL 00221 cout << ".\n"; 00222 cout << "#-------------------------------------------------------------------------\n"; 00223 // make sure we really have the output done. 00224 cout.flush(); 00225 } 00226 00227 //---------------------------------------------------------------------- 00228 // transfer all relevant info into internal variables 00229 void ClusterSequence::_decant_options(const JetDefinition & jet_def, 00230 const bool & writeout_combinations) { 00231 00232 // let the user know what's going on 00233 _print_banner(); 00234 00235 // make a local copy of the jet definition (for future use?) 00236 _jet_def = jet_def; 00237 00238 _writeout_combinations = writeout_combinations; 00239 _jet_algorithm = jet_def.jet_algorithm(); 00240 _Rparam = jet_def.R(); _R2 = _Rparam*_Rparam; _invR2 = 1.0/_R2; 00241 _strategy = jet_def.strategy(); 00242 00243 // disallow interference from the plugin 00244 _plugin_activated = false; 00245 00246 } 00247 00248 00249 //---------------------------------------------------------------------- 00250 // initialise the history in a standard way 00251 void ClusterSequence::_fill_initial_history () { 00252 00253 //if (_jets.size() == 0) {throw Error("Cannot run jet-finder on empty event");} 00254 00255 // reserve sufficient space for everything 00256 _jets.reserve(_jets.size()*2); 00257 _history.reserve(_jets.size()*2); 00258 00259 _Qtot = 0; 00260 00261 for (int i = 0; i < static_cast<int>(_jets.size()) ; i++) { 00262 history_element element; 00263 element.parent1 = InexistentParent; 00264 element.parent2 = InexistentParent; 00265 element.child = Invalid; 00266 element.jetp_index = i; 00267 element.dij = 0.0; 00268 element.max_dij_so_far = 0.0; 00269 00270 _history.push_back(element); 00271 00272 // do any momentum preprocessing needed by the recombination scheme 00273 _jet_def.recombiner()->preprocess(_jets[i]); 00274 00275 // get cross-referencing right from PseudoJets 00276 _jets[i].set_cluster_hist_index(i); 00277 00278 // determine the total energy in the event 00279 _Qtot += _jets[i].E(); 00280 } 00281 _initial_n = _jets.size(); 00282 } 00283 00284 00285 //---------------------------------------------------------------------- 00286 // Return the component corresponding to the specified index. 00287 // taken from CLHEP 00288 string ClusterSequence::strategy_string () const { 00289 string strategy; 00290 switch(_strategy) { 00291 case NlnN: 00292 strategy = "NlnN"; break; 00293 case NlnN3pi: 00294 strategy = "NlnN3pi"; break; 00295 case NlnN4pi: 00296 strategy = "NlnN4pi"; break; 00297 case N2Plain: 00298 strategy = "N2Plain"; break; 00299 case N2Tiled: 00300 strategy = "N2Tiled"; break; 00301 case N2MinHeapTiled: 00302 strategy = "N2MinHeapTiled"; break; 00303 case N2PoorTiled: 00304 strategy = "N2PoorTiled"; break; 00305 case N3Dumb: 00306 strategy = "N3Dumb"; break; 00307 case NlnNCam4pi: 00308 strategy = "NlnNCam4pi"; break; 00309 case NlnNCam2pi2R: 00310 strategy = "NlnNCam2pi2R"; break; 00311 case NlnNCam: 00312 strategy = "NlnNCam"; break; // 2piMultD 00313 case plugin_strategy: 00314 strategy = "plugin strategy"; break; 00315 default: 00316 strategy = "Unrecognized"; 00317 } 00318 return strategy; 00319 } 00320 00321 00322 double ClusterSequence::jet_scale_for_algorithm( 00323 const PseudoJet & jet) const { 00324 if (_jet_algorithm == kt_algorithm) {return jet.kt2();} 00325 else if (_jet_algorithm == cambridge_algorithm) {return 1.0;} 00326 else if (_jet_algorithm == antikt_algorithm) { 00327 double kt2=jet.kt2(); 00328 return kt2 > 1e-300 ? 1.0/kt2 : 1e300; 00329 } else if (_jet_algorithm == genkt_algorithm) { 00330 double kt2 = jet.kt2(); 00331 double p = jet_def().extra_param(); 00332 if (p <= 0 && kt2 < 1e-300) kt2 = 1e-300; // dodgy safety check 00333 return pow(kt2, p); 00334 } else if (_jet_algorithm == cambridge_for_passive_algorithm) { 00335 double kt2 = jet.kt2(); 00336 double lim = _jet_def.extra_param(); 00337 if (kt2 < lim*lim && kt2 != 0.0) { 00338 return 1.0/kt2; 00339 } else {return 1.0;} 00340 } else {throw Error("Unrecognised jet algorithm");} 00341 } 00342 00343 00344 //---------------------------------------------------------------------- 00348 void ClusterSequence::transfer_from_sequence(ClusterSequence & from_seq) { 00349 00350 // the metadata 00351 _jet_def = from_seq._jet_def ; 00352 _writeout_combinations = from_seq._writeout_combinations ; 00353 _initial_n = from_seq._initial_n ; 00354 _Rparam = from_seq._Rparam ; 00355 _R2 = from_seq._R2 ; 00356 _invR2 = from_seq._invR2 ; 00357 _strategy = from_seq._strategy ; 00358 _jet_algorithm = from_seq._jet_algorithm ; 00359 _plugin_activated = from_seq._plugin_activated ; 00360 00361 // the data 00362 _jets = from_seq._jets; 00363 _history = from_seq._history; 00364 // the following transferse ownership of the extras from the from_seq 00365 _extras = from_seq._extras; 00366 00367 } 00368 00369 //---------------------------------------------------------------------- 00370 // record an ij recombination and reset the _jets[newjet_k] momentum and 00371 // user index to be those of newjet 00372 void ClusterSequence::plugin_record_ij_recombination( 00373 int jet_i, int jet_j, double dij, 00374 const PseudoJet & newjet, int & newjet_k) { 00375 00376 plugin_record_ij_recombination(jet_i, jet_j, dij, newjet_k); 00377 00378 // now transfer newjet into place 00379 int tmp_index = _jets[newjet_k].cluster_hist_index(); 00380 _jets[newjet_k] = newjet; 00381 _jets[newjet_k].set_cluster_hist_index(tmp_index); 00382 } 00383 00384 00385 //---------------------------------------------------------------------- 00386 // return all inclusive jets with pt > ptmin 00387 vector<PseudoJet> ClusterSequence::inclusive_jets (const double & ptmin) const{ 00388 double dcut = ptmin*ptmin; 00389 int i = _history.size() - 1; // last jet 00390 vector<PseudoJet> jets; 00391 if (_jet_algorithm == kt_algorithm) { 00392 while (i >= 0) { 00393 // with our specific definition of dij and diB (i.e. R appears only in 00394 // dij), then dij==diB is the same as the jet.perp2() and we can exploit 00395 // this in selecting the jets... 00396 if (_history[i].max_dij_so_far < dcut) {break;} 00397 if (_history[i].parent2 == BeamJet && _history[i].dij >= dcut) { 00398 // for beam jets 00399 int parent1 = _history[i].parent1; 00400 jets.push_back(_jets[_history[parent1].jetp_index]);} 00401 i--; 00402 } 00403 } else if (_jet_algorithm == cambridge_algorithm) { 00404 while (i >= 0) { 00405 // inclusive jets are all at end of clustering sequence in the 00406 // Cambridge algorithm -- so if we find a non-exclusive jet, then 00407 // we can exit 00408 if (_history[i].parent2 != BeamJet) {break;} 00409 int parent1 = _history[i].parent1; 00410 const PseudoJet & jet = _jets[_history[parent1].jetp_index]; 00411 if (jet.perp2() >= dcut) {jets.push_back(jet);} 00412 i--; 00413 } 00414 } else if (_jet_algorithm == plugin_algorithm 00415 || _jet_algorithm == ee_kt_algorithm 00416 || _jet_algorithm == antikt_algorithm 00417 || _jet_algorithm == genkt_algorithm 00418 || _jet_algorithm == ee_genkt_algorithm 00419 || _jet_algorithm == cambridge_for_passive_algorithm) { 00420 // for inclusive jets with a plugin algorithm, we make no 00421 // assumptions about anything (relation of dij to momenta, 00422 // ordering of the dij, etc.) 00423 while (i >= 0) { 00424 if (_history[i].parent2 == BeamJet) { 00425 int parent1 = _history[i].parent1; 00426 const PseudoJet & jet = _jets[_history[parent1].jetp_index]; 00427 if (jet.perp2() >= dcut) {jets.push_back(jet);} 00428 } 00429 i--; 00430 } 00431 } else {throw Error("cs::inclusive_jets(...): Unrecognized jet algorithm");} 00432 return jets; 00433 } 00434 00435 00436 //---------------------------------------------------------------------- 00437 // return the number of exclusive jets that would have been obtained 00438 // running the algorithm in exclusive mode with the given dcut 00439 int ClusterSequence::n_exclusive_jets (const double & dcut) const { 00440 00441 // first locate the point where clustering would have stopped (i.e. the 00442 // first time max_dij_so_far > dcut) 00443 int i = _history.size() - 1; // last jet 00444 while (i >= 0) { 00445 if (_history[i].max_dij_so_far <= dcut) {break;} 00446 i--; 00447 } 00448 int stop_point = i + 1; 00449 // relation between stop_point, njets assumes one extra jet disappears 00450 // at each clustering. 00451 int njets = 2*_initial_n - stop_point; 00452 return njets; 00453 } 00454 00455 //---------------------------------------------------------------------- 00456 // return all exclusive jets that would have been obtained running 00457 // the algorithm in exclusive mode with the given dcut 00458 vector<PseudoJet> ClusterSequence::exclusive_jets (const double & dcut) const { 00459 int njets = n_exclusive_jets(dcut); 00460 return exclusive_jets(njets); 00461 } 00462 00463 00464 //---------------------------------------------------------------------- 00465 // return the jets obtained by clustering the event to n jets. 00466 vector<PseudoJet> ClusterSequence::exclusive_jets (const int & njets) const { 00467 00468 // make sure the user does not ask for more than jets than there 00469 // were particles in the first place. 00470 assert (njets <= _initial_n); 00471 00472 // provide a warning when extracting exclusive jets for algorithms 00473 // that does not support it explicitly. 00474 // Native algorithm that support it are: kt, ee_kt, cambridge, 00475 // genkt and ee_genkt (both with p>=0) 00476 // For plugins, we check Plugin::exclusive_sequence_meaningful() 00477 if (( _jet_def.jet_algorithm() != kt_algorithm) && 00478 ( _jet_def.jet_algorithm() != cambridge_algorithm) && 00479 ( _jet_def.jet_algorithm() != ee_kt_algorithm) && 00480 (((_jet_def.jet_algorithm() != genkt_algorithm) && 00481 (_jet_def.jet_algorithm() != ee_genkt_algorithm)) || 00482 (_jet_def.extra_param() <0)) && 00483 ((_jet_def.jet_algorithm() != plugin_algorithm) || 00484 (!_jet_def.plugin()->exclusive_sequence_meaningful())) && 00485 (_n_exclusive_warnings < 5)) { 00486 _n_exclusive_warnings++; 00487 cerr << "FastJet WARNING: dcut and exclusive jets for jet-finders other than kt should be interpreted with care." << endl; 00488 } 00489 00490 00491 // calculate the point where we have to stop the clustering. 00492 // relation between stop_point, njets assumes one extra jet disappears 00493 // at each clustering. 00494 int stop_point = 2*_initial_n - njets; 00495 00496 // some sanity checking to make sure that e+e- does not give us 00497 // surprises (should we ever implement e+e-)... 00498 if (2*_initial_n != static_cast<int>(_history.size())) { 00499 ostringstream err; 00500 err << "2*_initial_n != _history.size() -- this endangers internal assumptions!\n"; 00501 throw Error(err.str()); 00502 //assert(false); 00503 } 00504 00505 // now go forwards and reconstitute the jets that we have -- 00506 // basically for any history element, see if the parent jets to 00507 // which it refers were created before the stopping point -- if they 00508 // were then add them to the list, otherwise they are subsequent 00509 // recombinations of the jets that we are looking for. 00510 vector<PseudoJet> jets; 00511 for (unsigned int i = stop_point; i < _history.size(); i++) { 00512 int parent1 = _history[i].parent1; 00513 if (parent1 < stop_point) { 00514 jets.push_back(_jets[_history[parent1].jetp_index]); 00515 } 00516 int parent2 = _history[i].parent2; 00517 if (parent2 < stop_point && parent2 > 0) { 00518 jets.push_back(_jets[_history[parent2].jetp_index]); 00519 } 00520 00521 } 00522 00523 // sanity check... 00524 if (static_cast<int>(jets.size()) != njets) { 00525 ostringstream err; 00526 err << "ClusterSequence::exclusive_jets: size of returned vector (" 00527 <<jets.size()<<") does not coincide with requested number of jets (" 00528 <<njets<<")"; 00529 throw Error(err.str()); 00530 } 00531 00532 return jets; 00533 } 00534 00535 //---------------------------------------------------------------------- 00538 double ClusterSequence::exclusive_dmerge (const int & njets) const { 00539 assert(njets >= 0); 00540 if (njets >= _initial_n) {return 0.0;} 00541 return _history[2*_initial_n-njets-1].dij; 00542 } 00543 00544 00545 //---------------------------------------------------------------------- 00550 double ClusterSequence::exclusive_dmerge_max (const int & njets) const { 00551 assert(njets >= 0); 00552 if (njets >= _initial_n) {return 0.0;} 00553 return _history[2*_initial_n-njets-1].max_dij_so_far; 00554 } 00555 00556 00557 //---------------------------------------------------------------------- 00561 std::vector<PseudoJet> ClusterSequence::exclusive_subjets 00562 (const PseudoJet & jet, const double & dcut) const { 00563 00564 set<const history_element*> subhist; 00565 00566 // get the set of history elements that correspond to subjets at 00567 // scale dcut 00568 get_subhist_set(subhist, jet, dcut, 0); 00569 00570 // now transfer this into a sequence of jets 00571 vector<PseudoJet> subjets; 00572 subjets.reserve(subhist.size()); 00573 for (set<const history_element*>::iterator elem = subhist.begin(); 00574 elem != subhist.end(); elem++) { 00575 subjets.push_back(_jets[(*elem)->jetp_index]); 00576 } 00577 return subjets; 00578 } 00579 00580 //---------------------------------------------------------------------- 00584 int ClusterSequence::n_exclusive_subjets(const PseudoJet & jet, 00585 const double & dcut) const { 00586 set<const history_element*> subhist; 00587 // get the set of history elements that correspond to subjets at 00588 // scale dcut 00589 get_subhist_set(subhist, jet, dcut, 0); 00590 return subhist.size(); 00591 } 00592 00593 //---------------------------------------------------------------------- 00597 std::vector<PseudoJet> ClusterSequence::exclusive_subjets 00598 (const PseudoJet & jet, int n) const { 00599 00600 set<const history_element*> subhist; 00601 00602 // get the set of history elements that correspond to subjets at 00603 // scale dcut 00604 get_subhist_set(subhist, jet, -1.0, n); 00605 00606 // now transfer this into a sequence of jets 00607 vector<PseudoJet> subjets; 00608 subjets.reserve(subhist.size()); 00609 for (set<const history_element*>::iterator elem = subhist.begin(); 00610 elem != subhist.end(); elem++) { 00611 subjets.push_back(_jets[(*elem)->jetp_index]); 00612 } 00613 return subjets; 00614 } 00615 00616 00617 //---------------------------------------------------------------------- 00622 double ClusterSequence::exclusive_subdmerge(const PseudoJet & jet, int nsub) const { 00623 set<const history_element*> subhist; 00624 00625 // get the set of history elements that correspond to subjets at 00626 // scale dcut 00627 get_subhist_set(subhist, jet, -1.0, nsub); 00628 00629 set<const history_element*>::iterator highest = subhist.end(); 00630 highest--; 00633 return (*highest)->dij; 00634 } 00635 00636 00637 //---------------------------------------------------------------------- 00643 double ClusterSequence::exclusive_subdmerge_max(const PseudoJet & jet, int nsub) const { 00644 00645 set<const history_element*> subhist; 00646 00647 // get the set of history elements that correspond to subjets at 00648 // scale dcut 00649 get_subhist_set(subhist, jet, -1.0, nsub); 00650 00651 set<const history_element*>::iterator highest = subhist.end(); 00652 highest--; 00655 return (*highest)->max_dij_so_far; 00656 } 00657 00658 00659 00660 //---------------------------------------------------------------------- 00667 void ClusterSequence::get_subhist_set(set<const history_element*> & subhist, 00668 const PseudoJet & jet, 00669 double dcut, int maxjet) const { 00670 subhist.clear(); 00671 subhist.insert(&(_history[jet.cluster_hist_index()])); 00672 00673 // establish the set of jets that are relevant 00674 int njet = 1; 00675 while (true) { 00676 // first find out if we need to probe deeper into jet. 00677 // Get history element closest to end of sequence 00678 set<const history_element*>::iterator highest = subhist.end(); 00679 assert (highest != subhist.begin()); 00680 highest--; 00681 const history_element* elem = *highest; 00682 // make sure we haven't got too many jets 00683 if (njet == maxjet) break; 00684 // make sure it has parents 00685 if (elem->parent1 < 0) break; 00686 // make sure that we still resolve it at scale dcut 00687 if (elem->max_dij_so_far <= dcut) break; 00688 00689 // then do so: replace "highest" with its two parents 00690 subhist.erase(highest); 00691 subhist.insert(&(_history[elem->parent1])); 00692 subhist.insert(&(_history[elem->parent2])); 00693 njet++; 00694 } 00695 } 00696 00697 //---------------------------------------------------------------------- 00698 // work through the object's history until 00699 bool ClusterSequence::object_in_jet(const PseudoJet & object, 00700 const PseudoJet & jet) const { 00701 00702 // make sure the object conceivably belongs to this clustering 00703 // sequence 00704 assert(_potentially_valid(object) && _potentially_valid(jet)); 00705 00706 const PseudoJet * this_object = &object; 00707 const PseudoJet * childp; 00708 while(true) { 00709 if (this_object->cluster_hist_index() == jet.cluster_hist_index()) { 00710 return true; 00711 } else if (has_child(*this_object, childp)) {this_object = childp;} 00712 else {return false;} 00713 } 00714 } 00715 00716 //---------------------------------------------------------------------- 00722 bool ClusterSequence::has_parents(const PseudoJet & jet, PseudoJet & parent1, 00723 PseudoJet & parent2) const { 00724 00725 const history_element & hist = _history[jet.cluster_hist_index()]; 00726 00727 // make sure we do not run into any unexpected situations -- 00728 // i.e. both parents valid, or neither 00729 assert ((hist.parent1 >= 0 && hist.parent2 >= 0) || 00730 (hist.parent1 < 0 && hist.parent2 < 0)); 00731 00732 if (hist.parent1 < 0) { 00733 parent1 = PseudoJet(0.0,0.0,0.0,0.0); 00734 parent2 = parent1; 00735 return false; 00736 } else { 00737 parent1 = _jets[_history[hist.parent1].jetp_index]; 00738 parent2 = _jets[_history[hist.parent2].jetp_index]; 00739 // order the parents in decreasing pt 00740 if (parent1.perp2() < parent2.perp2()) std::swap(parent1,parent2); 00741 return true; 00742 } 00743 } 00744 00745 //---------------------------------------------------------------------- 00748 bool ClusterSequence::has_child(const PseudoJet & jet, PseudoJet & child) const { 00749 00750 //const history_element & hist = _history[jet.cluster_hist_index()]; 00751 // 00752 //if (hist.child >= 0) { 00753 // child = _jets[_history[hist.child].jetp_index]; 00754 // return true; 00755 //} else { 00756 // child = PseudoJet(0.0,0.0,0.0,0.0); 00757 // return false; 00758 //} 00759 const PseudoJet * childp; 00760 bool res = has_child(jet, childp); 00761 if (res) { 00762 child = *childp; 00763 return true; 00764 } else { 00765 child = PseudoJet(0.0,0.0,0.0,0.0); 00766 return false; 00767 } 00768 } 00769 00770 bool ClusterSequence::has_child(const PseudoJet & jet, const PseudoJet * & childp) const { 00771 00772 const history_element & hist = _history[jet.cluster_hist_index()]; 00773 00774 // check that this jet has a child and that the child corresponds to 00775 // a true jet [RETHINK-IF-CHANGE-NUMBERING: what is the right 00776 // behaviour if the child is the same jet but made inclusive...?] 00777 if (hist.child >= 0 && _history[hist.child].jetp_index >= 0) { 00778 childp = &(_jets[_history[hist.child].jetp_index]); 00779 return true; 00780 } else { 00781 childp = NULL; 00782 return false; 00783 } 00784 } 00785 00786 00787 //---------------------------------------------------------------------- 00791 bool ClusterSequence::has_partner(const PseudoJet & jet, 00792 PseudoJet & partner) const { 00793 00794 const history_element & hist = _history[jet.cluster_hist_index()]; 00795 00796 // make sure we have a child and that the child does not correspond 00797 // to a clustering with the beam (or some other invalid quantity) 00798 if (hist.child >= 0 && _history[hist.child].parent2 >= 0) { 00799 const history_element & child_hist = _history[hist.child]; 00800 if (child_hist.parent1 == jet.cluster_hist_index()) { 00801 // partner will be child's parent2 -- for iB clustering 00802 // parent2 will not be valid 00803 partner = _jets[_history[child_hist.parent2].jetp_index]; 00804 } else { 00805 // partner will be child's parent1 00806 partner = _jets[_history[child_hist.parent1].jetp_index]; 00807 } 00808 return true; 00809 } else { 00810 partner = PseudoJet(0.0,0.0,0.0,0.0); 00811 return false; 00812 } 00813 } 00814 00815 00816 //---------------------------------------------------------------------- 00817 // return a vector of the particles that make up a jet 00818 vector<PseudoJet> ClusterSequence::constituents (const PseudoJet & jet) const { 00819 vector<PseudoJet> subjets; 00820 add_constituents(jet, subjets); 00821 return subjets; 00822 } 00823 00824 //---------------------------------------------------------------------- 00833 void ClusterSequence::print_jets_for_root(const std::vector<PseudoJet> & jets, 00834 ostream & ostr) const { 00835 for (unsigned i = 0; i < jets.size(); i++) { 00836 ostr << i << " " 00837 << jets[i].px() << " " 00838 << jets[i].py() << " " 00839 << jets[i].pz() << " " 00840 << jets[i].E() << endl; 00841 vector<PseudoJet> cst = constituents(jets[i]); 00842 for (unsigned j = 0; j < cst.size() ; j++) { 00843 ostr << " " << j << " " 00844 << cst[j].rap() << " " 00845 << cst[j].phi() << " " 00846 << cst[j].perp() << endl; 00847 } 00848 ostr << "#END" << endl; 00849 } 00850 } 00851 00852 void ClusterSequence::print_jets_for_root(const std::vector<PseudoJet> & jets, 00853 const std::string & filename, 00854 const std::string & comment ) const { 00855 std::ofstream ostr(filename.c_str()); 00856 if (comment != "") ostr << "# " << comment << endl; 00857 print_jets_for_root(jets, ostr); 00858 } 00859 00860 00861 // Not yet. Perhaps in a future release 00862 // //---------------------------------------------------------------------- 00863 // // print out all inclusive jets with pt > ptmin 00864 // void ClusterSequence::print_jets (const double & ptmin) const{ 00865 // vector<PseudoJet> jets = sorted_by_pt(inclusive_jets(ptmin)); 00866 // 00867 // for (size_t j = 0; j < jets.size(); j++) { 00868 // printf("%5u %7.3f %7.3f %9.3f\n", 00869 // j,jets[j].rap(),jets[j].phi(),jets[j].perp()); 00870 // } 00871 // } 00872 00873 //---------------------------------------------------------------------- 00878 vector<int> ClusterSequence::particle_jet_indices( 00879 const vector<PseudoJet> & jets) const { 00880 00881 vector<int> indices(n_particles()); 00882 00883 // first label all particles as not belonging to any jets 00884 for (unsigned ipart = 0; ipart < n_particles(); ipart++) 00885 indices[ipart] = -1; 00886 00887 // then for each of the jets relabel its consituents as belonging to 00888 // that jet 00889 for (unsigned ijet = 0; ijet < jets.size(); ijet++) { 00890 00891 vector<PseudoJet> jet_constituents(constituents(jets[ijet])); 00892 00893 for (unsigned ip = 0; ip < jet_constituents.size(); ip++) { 00894 // a safe (if slightly redundant) way of getting the particle 00895 // index (for initial particles it is actually safe to assume 00896 // ipart=iclust). 00897 unsigned iclust = jet_constituents[ip].cluster_hist_index(); 00898 unsigned ipart = history()[iclust].jetp_index; 00899 indices[ipart] = ijet; 00900 } 00901 } 00902 00903 return indices; 00904 } 00905 00906 00907 //---------------------------------------------------------------------- 00908 // recursive routine that adds on constituents of jet to the subjet_vector 00909 void ClusterSequence::add_constituents ( 00910 const PseudoJet & jet, vector<PseudoJet> & subjet_vector) const { 00911 // find out position in cluster history 00912 int i = jet.cluster_hist_index(); 00913 int parent1 = _history[i].parent1; 00914 int parent2 = _history[i].parent2; 00915 00916 if (parent1 == InexistentParent) { 00917 // It is an original particle (labelled by its parent having value 00918 // InexistentParent), therefore add it on to the subjet vector 00919 // Note: we add the initial particle and not simply 'jet' so that 00920 // calling add_constituents with a subtracted jet containing 00921 // only one particle will work. 00922 subjet_vector.push_back(_jets[i]); 00923 return; 00924 } 00925 00926 // add parent 1 00927 add_constituents(_jets[_history[parent1].jetp_index], subjet_vector); 00928 00929 // see if parent2 is a real jet; if it is then add its constituents 00930 if (parent2 != BeamJet) { 00931 add_constituents(_jets[_history[parent2].jetp_index], subjet_vector); 00932 } 00933 } 00934 00935 00936 00937 //---------------------------------------------------------------------- 00938 // initialise the history in a standard way 00939 void ClusterSequence::_add_step_to_history ( 00940 const int & step_number, const int & parent1, 00941 const int & parent2, const int & jetp_index, 00942 const double & dij) { 00943 00944 history_element element; 00945 element.parent1 = parent1; 00946 element.parent2 = parent2; 00947 element.jetp_index = jetp_index; 00948 element.child = Invalid; 00949 element.dij = dij; 00950 element.max_dij_so_far = max(dij,_history[_history.size()-1].max_dij_so_far); 00951 _history.push_back(element); 00952 00953 int local_step = _history.size()-1; 00954 assert(local_step == step_number); 00955 00956 assert(parent1 >= 0); 00957 _history[parent1].child = local_step; 00958 if (parent2 >= 0) {_history[parent2].child = local_step;} 00959 00960 // get cross-referencing right from PseudoJets 00961 if (jetp_index != Invalid) { 00962 assert(jetp_index >= 0); 00963 //cout << _jets.size() <<" "<<jetp_index<<"\n"; 00964 _jets[jetp_index].set_cluster_hist_index(local_step); 00965 } 00966 00967 if (_writeout_combinations) { 00968 cout << local_step << ": " 00969 << parent1 << " with " << parent2 00970 << "; y = "<< dij<<endl; 00971 } 00972 00973 } 00974 00975 00976 00977 00978 //====================================================================== 00979 // Return an order in which to read the history such that _history[order[i]] 00980 // will always correspond to the same set of consituent particles if 00981 // two branching histories are equivalent in terms of the particles 00982 // contained in any given pseudojet. 00983 vector<int> ClusterSequence::unique_history_order() const { 00984 00985 // first construct an array that will tell us the lowest constituent 00986 // of a given jet -- this will always be one of the original 00987 // particles, whose order is well defined and so will help us to 00988 // follow the tree in a unique manner. 00989 valarray<int> lowest_constituent(_history.size()); 00990 int hist_n = _history.size(); 00991 lowest_constituent = hist_n; // give it a large number 00992 for (int i = 0; i < hist_n; i++) { 00993 // sets things up for the initial partons 00994 lowest_constituent[i] = min(lowest_constituent[i],i); 00995 // propagates them through to the children of this parton 00996 if (_history[i].child > 0) lowest_constituent[_history[i].child] 00997 = min(lowest_constituent[_history[i].child],lowest_constituent[i]); 00998 } 00999 01000 // establish an array for what we have and have not extracted so far 01001 valarray<bool> extracted(_history.size()); extracted = false; 01002 vector<int> unique_tree; 01003 unique_tree.reserve(_history.size()); 01004 01005 // now work our way through the tree 01006 for (unsigned i = 0; i < n_particles(); i++) { 01007 if (!extracted[i]) { 01008 unique_tree.push_back(i); 01009 extracted[i] = true; 01010 _extract_tree_children(i, extracted, lowest_constituent, unique_tree); 01011 } 01012 } 01013 01014 return unique_tree; 01015 } 01016 01017 //====================================================================== 01018 // helper for unique_history_order 01019 void ClusterSequence::_extract_tree_children( 01020 int position, 01021 valarray<bool> & extracted, 01022 const valarray<int> & lowest_constituent, 01023 vector<int> & unique_tree) const { 01024 if (!extracted[position]) { 01025 // that means we may have unidentified parents around, so go and 01026 // collect them (extracted[position]) will then be made true) 01027 _extract_tree_parents(position,extracted,lowest_constituent,unique_tree); 01028 } 01029 01030 // now look after the children... 01031 int child = _history[position].child; 01032 if (child >= 0) _extract_tree_children(child,extracted,lowest_constituent,unique_tree); 01033 } 01034 01035 01036 //====================================================================== 01037 // return the list of unclustered particles 01038 vector<PseudoJet> ClusterSequence::unclustered_particles() const { 01039 vector<PseudoJet> unclustered; 01040 for (unsigned i = 0; i < n_particles() ; i++) { 01041 if (_history[i].child == Invalid) 01042 unclustered.push_back(_jets[_history[i].jetp_index]); 01043 } 01044 return unclustered; 01045 } 01046 01047 01048 01049 //====================================================================== 01050 // helper for unique_history_order 01051 void ClusterSequence::_extract_tree_parents( 01052 int position, 01053 valarray<bool> & extracted, 01054 const valarray<int> & lowest_constituent, 01055 vector<int> & unique_tree) const { 01056 01057 if (!extracted[position]) { 01058 int parent1 = _history[position].parent1; 01059 int parent2 = _history[position].parent2; 01060 // where relevant order parents so that we will first treat the 01061 // one containing the smaller "lowest_constituent" 01062 if (parent1 >= 0 && parent2 >= 0) { 01063 if (lowest_constituent[parent1] > lowest_constituent[parent2]) 01064 std::swap(parent1, parent2); 01065 } 01066 // then actually run through the parents to extract the constituents... 01067 if (parent1 >= 0 && !extracted[parent1]) 01068 _extract_tree_parents(parent1,extracted,lowest_constituent,unique_tree); 01069 if (parent2 >= 0 && !extracted[parent2]) 01070 _extract_tree_parents(parent2,extracted,lowest_constituent,unique_tree); 01071 // finally declare this position to be accounted for and push it 01072 // onto our list. 01073 unique_tree.push_back(position); 01074 extracted[position] = true; 01075 } 01076 } 01077 01078 01079 //====================================================================== 01083 void ClusterSequence::_do_ij_recombination_step( 01084 const int & jet_i, const int & jet_j, 01085 const double & dij, 01086 int & newjet_k) { 01087 01088 // create the new jet by recombining the first two 01089 PseudoJet newjet; 01090 _jet_def.recombiner()->recombine(_jets[jet_i], _jets[jet_j], newjet); 01091 _jets.push_back(newjet); 01092 // original version... 01093 //_jets.push_back(_jets[jet_i] + _jets[jet_j]); 01094 01095 // get its index 01096 newjet_k = _jets.size()-1; 01097 01098 // get history index 01099 int newstep_k = _history.size(); 01100 // and provide jet with the info 01101 _jets[newjet_k].set_cluster_hist_index(newstep_k); 01102 01103 // finally sort out the history 01104 int hist_i = _jets[jet_i].cluster_hist_index(); 01105 int hist_j = _jets[jet_j].cluster_hist_index(); 01106 01107 _add_step_to_history(newstep_k, min(hist_i, hist_j), max(hist_i,hist_j), 01108 newjet_k, dij); 01109 01110 } 01111 01112 01113 //====================================================================== 01116 void ClusterSequence::_do_iB_recombination_step( 01117 const int & jet_i, const double & diB) { 01118 // get history index 01119 int newstep_k = _history.size(); 01120 01121 // recombine the jet with the beam 01122 _add_step_to_history(newstep_k,_jets[jet_i].cluster_hist_index(),BeamJet, 01123 Invalid, diB); 01124 01125 } 01126 01127 FASTJET_END_NAMESPACE 01128