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