FastJet  3.2.0
ClusterSequence_with_boost.cc
1 //STARTHEADER
2 // $Id: ClusterSequence.cc 2132 2011-05-16 15:05:18Z soyez $
3 //
4 // Copyright (c) 2005-2011, Matteo Cacciari, Gavin Salam and Gregory Soyez
5 //
6 //----------------------------------------------------------------------
7 // This file is part of FastJet.
8 //
9 // FastJet is free software; you can redistribute it and/or modify
10 // it under the terms of the GNU General Public License as published by
11 // the Free Software Foundation; either version 2 of the License, or
12 // (at your option) any later version.
13 //
14 // The algorithms that underlie FastJet have required considerable
15 // development and are described in hep-ph/0512210. If you use
16 // FastJet as part of work towards a scientific publication, please
17 // include a citation to the FastJet paper.
18 //
19 // FastJet is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with FastJet; if not, write to the Free Software
26 // Foundation, Inc.:
27 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 //----------------------------------------------------------------------
29 //ENDHEADER
30 
31 #include "fastjet/Error.hh"
32 #include "fastjet/PseudoJet.hh"
33 #include "fastjet/ClusterSequence.hh"
34 #include "fastjet/ClusterSequenceStructure.hh"
35 #include "fastjet/version.hh" // stores the current version number
36 #include<iostream>
37 #include<sstream>
38 #include<fstream>
39 #include<cmath>
40 #include<cstdlib>
41 #include<cassert>
42 #include<string>
43 #include<set>
44 
45 FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
46 
47 using namespace std;
48 
49 //// initialised static member has to go in the .cc code
50 JetAlgorithm ClusterSequence::_default_jet_algorithm = kt_algorithm;
51 //
52 
53 
54 // destructor that guarantees proper bookkeeping for the CS Structure
55 ClusterSequence::~ClusterSequence () {
56  // set the pointer in the wrapper to this object to NULL to say that
57  // we're going out of scope
58  if (_structure_shared_ptr()){
59  ClusterSequenceStructure* csi = dynamic_cast<ClusterSequenceStructure*>(_structure_shared_ptr());
60  // normally the csi is purely internal so it really should not be
61  // NULL i.e assert should be OK
62  // (we assert rather than throw an error, since failure here is a
63  // sign of major internal problems)
64  assert(csi != NULL);
65  csi->set_associated_cs(NULL);
66 
67  // if the user had given the CS responsibility to delete itself,
68  // but then deletes the CS themselves, the following lines of
69  // code will ensure that the structure_shared_ptr will have
70  // a proper object count (so that jets associated with the CS will
71  // throw the correct error if the user tries to access their
72  // constituents).
73  if (_deletes_self_when_unused) {
74  _structure_shared_ptr.set_count(_structure_shared_ptr.use_count()
75  + _structure_use_count_after_construction);
76  }
77  }
78 }
79 
80 //-----------
81 void ClusterSequence::signal_imminent_self_deletion() const {
82  // normally if the destructor is called when
83  // _deletes_self_when_unused is true, it assumes that it's been
84  // called by the user (and it therefore resets the shared pointer
85  // count to the true count).
86  //
87  // for self deletion (called from the destructor of the CSstructure,
88  // the shared_ptr to which has just had its pointer -> 0) you do
89  // _not_ want to reset the pointer count (otherwise you will end up
90  // with a double delete on the shared pointer once you start
91  // deleting the internal structure of the CS).
92  //
93  // the following modification ensures that the count reset will not
94  // take place in the destructor
95  assert(_deletes_self_when_unused);
96  _deletes_self_when_unused = false;
97 }
98 
99 //----------------------------------------------------------------------
100 void ClusterSequence::_initialise_and_run (
101  const double & R,
102  const Strategy & strategy,
103  const bool & writeout_combinations) {
104 
105  JetDefinition jet_def(_default_jet_algorithm, R, strategy);
106  _initialise_and_run(jet_def, writeout_combinations);
107 }
108 
109 
110 //----------------------------------------------------------------------
111 void ClusterSequence::_initialise_and_run (
112  const JetDefinition & jet_def,
113  const bool & writeout_combinations) {
114 
115  // transfer all relevant info into internal variables
116  _decant_options(jet_def, writeout_combinations);
117 
118  // set up the history entries for the initial particles (those
119  // currently in _jets)
120  _fill_initial_history();
121 
122  // don't run anything if the event is empty
123  if (n_particles() == 0) return;
124 
125  // ----- deal with special cases: plugins & e+e- ------
126  if (_jet_algorithm == plugin_algorithm) {
127  // allows plugin_xyz() functions to modify cluster sequence
128  _plugin_activated = true;
129  // let the plugin do its work here
130  _jet_def.plugin()->run_clustering( (*this) );
131  _plugin_activated = false;
132  _update_structure_use_count();
133  return;
134  } else if (_jet_algorithm == ee_kt_algorithm ||
135  _jet_algorithm == ee_genkt_algorithm) {
136  // ignore requested strategy
137  _strategy = N2Plain;
138  if (_jet_algorithm == ee_kt_algorithm) {
139  // make sure that R is large enough so that "beam" recomb only
140  // occurs when a single particle is left
141  // Normally, this should be automatically set to 4 from JetDefinition
142  assert(_Rparam > 2.0);
143  // this is used to renormalise the dij to get a "standard" form
144  // and our convention in e+e- will be different from that
145  // in long.inv case; NB: _invR2 name should be changed -> _renorm_dij?
146  _invR2 = 1.0;
147  } else {
148  // as of 2009-01-09, choose R to be an angular distance, in
149  // radians. Since the algorithm uses 2(1-cos(theta)) as its
150  // squared angular measure, make sure that the _R2 is defined
151  // in a similar way.
152  if (_Rparam > pi) {
153  // choose a value that ensures that back-to-back particles will
154  // always recombine
155  //_R2 = 4.0000000000001;
156  _R2 = 2 * ( 3.0 + cos(_Rparam) );
157  } else {
158  _R2 = 2 * ( 1.0 - cos(_Rparam) );
159  }
160  _invR2 = 1.0/_R2;
161  }
162  _simple_N2_cluster_EEBriefJet();
163  return;
164  } else if (_jet_algorithm == undefined_jet_algorithm) {
165  throw Error("A ClusterSequence cannot be created with an uninitialised JetDefinition");
166  }
167 
168 
169  // automatically redefine the strategy according to N if that is
170  // what the user requested -- transition points (and especially
171  // their R-dependence) are based on empirical observations for a
172  // R=0.4, 0.7 and 1.0, running on toth (3.4GHz, Pentium IV D [dual
173  // core] with 2MB of cache).
174  if (_strategy == Best) {
175  int N = _jets.size();
176  if (N <= 55*max(0.5,min(1.0,_Rparam))) {// empirical scaling with R
177  _strategy = N2Plain;
178  } else if (N > 6200/pow(_Rparam,2.0) && jet_def.jet_algorithm() == cambridge_algorithm) {
179  _strategy = NlnNCam;
180 #ifndef DROP_CGAL
181  } else if ((N > 16000/pow(_Rparam,1.15) && jet_def.jet_algorithm() != antikt_algorithm)
182  || N > 35000/pow(_Rparam,1.15)) {
183  _strategy = NlnN;
184 #endif // DROP_CGAL
185  } else if (N <= 450) {
186  _strategy = N2Tiled;
187  } else {
188  _strategy = N2MinHeapTiled;
189  }
190  }
191 
192  // R >= 2pi is not supported by all clustering strategies owing to
193  // periodicity issues (a particle might cluster with itself). When
194  // R>=2pi, we therefore automatically switch to a strategy that is
195  // known to work.
196  if (_Rparam >= twopi) {
197  if ( _strategy == NlnN
198  || _strategy == NlnN3pi
199  || _strategy == NlnNCam
200  || _strategy == NlnNCam2pi2R
201  || _strategy == NlnNCam4pi) {
202 #ifdef DROP_CGAL
203  _strategy = N2MinHeapTiled;
204 #else
205  _strategy = NlnN4pi;
206 #endif
207  }
208  if (jet_def.strategy() != Best && _strategy != jet_def.strategy()) {
209  ostringstream oss;
210  oss << "Cluster strategy " << strategy_string(jet_def.strategy())
211  << " automatically changed to " << strategy_string()
212  << " because the former is not supported for R = " << _Rparam
213  << " >= 2pi";
214  _changed_strategy_warning.warn(oss.str());
215  }
216  }
217 
218 
219  // run the code containing the selected strategy
220  //
221  // We order the strategies stqrting from the ones used by the Best
222  // strategy in the order of increasing N, then the remaining ones
223  // again in the order of increasing N.
224  if (_strategy == N2Plain) {
225  // BriefJet provides standard long.invariant kt alg.
226  this->_simple_N2_cluster_BriefJet();
227  } else if (_strategy == N2Tiled) {
228  this->_faster_tiled_N2_cluster();
229  } else if (_strategy == N2MinHeapTiled) {
230  this->_minheap_faster_tiled_N2_cluster();
231  } else if (_strategy == NlnN) {
232  this->_delaunay_cluster();
233  } else if (_strategy == NlnNCam) {
234  this->_CP2DChan_cluster_2piMultD();
235  } else if (_strategy == NlnN3pi || _strategy == NlnN4pi ) {
236  this->_delaunay_cluster();
237  } else if (_strategy == N3Dumb ) {
238  this->_really_dumb_cluster();
239  } else if (_strategy == N2PoorTiled) {
240  this->_tiled_N2_cluster();
241  } else if (_strategy == NlnNCam4pi) {
242  this->_CP2DChan_cluster();
243  } else if (_strategy == NlnNCam2pi2R) {
244  this->_CP2DChan_cluster_2pi2R();
245  } else {
246  ostringstream err;
247  err << "Unrecognised value for strategy: "<<_strategy;
248  throw Error(err.str());
249  }
250 
251 }
252 
253 
254 // these needs to be defined outside the class definition.
255 bool ClusterSequence::_first_time = true;
256 int ClusterSequence::_n_exclusive_warnings = 0;
257 
258 
259 //----------------------------------------------------------------------
260 // the version string
261 string fastjet_version_string() {
262  return "FastJet version "+string(fastjet_version);
263 }
264 
265 
266 //----------------------------------------------------------------------
267 // prints a banner on the first call
268 void ClusterSequence::_print_banner() {
269 
270  if (!_first_time) {return;}
271  _first_time = false;
272 
273 
274  //Symp. Discr. Alg, p.472 (2002) and CGAL (http://www.cgal.org);
275 
276  cout << "#--------------------------------------------------------------------------\n";
277  cout << "# FastJet release " << fastjet_version << endl;
278  cout << "# Written by M. Cacciari, G.P. Salam and G. Soyez \n";
279  cout << "# http://www.fastjet.fr \n";
280  cout << "# \n";
281  cout << "# Longitudinally invariant Kt, anti-Kt, and inclusive Cambridge/Aachen \n";
282  cout << "# clustering using fast geometric algorithms, with jet areas and optional\n";
283  cout << "# external jet-finder plugins. If you use this code towards a scientific \n";
284  cout << "# publication please cite Phys. Lett. B641 (2006) [hep-ph/0512210] and \n";
285  cout << "# M. Cacciari, G.P. Salam and G. Soyez, http://fastjet.fr/ \n";
286  cout << "# \n";
287  cout << "# This package uses T.Chan's closest pair algorithm, Proc.13th ACM-SIAM \n";
288  cout << "# Symp. Discr. Alg, p.472 (2002), S.Fortune's Voronoi algorithm and code" ;
289 #ifndef DROP_CGAL
290  cout << endl << "# and CGAL: http://www.cgal.org/";
291 #endif // DROP_CGAL
292  cout << ".\n";
293  cout << "#-------------------------------------------------------------------------\n";
294  // make sure we really have the output done.
295  cout.flush();
296 }
297 
298 //----------------------------------------------------------------------
299 // transfer all relevant info into internal variables
300 void ClusterSequence::_decant_options(const JetDefinition & jet_def,
301  const bool & writeout_combinations) {
302  // let the user know what's going on
303  _print_banner();
304 
305  // make a local copy of the jet definition (for future use?)
306  _jet_def = jet_def;
307 
308  _writeout_combinations = writeout_combinations;
309  _jet_algorithm = jet_def.jet_algorithm();
310  _Rparam = jet_def.R(); _R2 = _Rparam*_Rparam; _invR2 = 1.0/_R2;
311  _strategy = jet_def.strategy();
312 
313  // disallow interference from the plugin
314  _plugin_activated = false;
315 
316  // initialised the wrapper to the current CS
317  _structure_shared_ptr.reset(new ClusterSequenceStructure(this));
318  _update_structure_use_count(); // make sure it's correct already here
319 }
320 
321 
322 //----------------------------------------------------------------------
323 // initialise the history in a standard way
324 void ClusterSequence::_fill_initial_history () {
325 
326  //if (_jets.size() == 0) {throw Error("Cannot run jet-finder on empty event");}
327 
328  // reserve sufficient space for everything
329  _jets.reserve(_jets.size()*2);
330  _history.reserve(_jets.size()*2);
331 
332  _Qtot = 0;
333 
334  for (int i = 0; i < static_cast<int>(_jets.size()) ; i++) {
335  history_element element;
336  element.parent1 = InexistentParent;
337  element.parent2 = InexistentParent;
338  element.child = Invalid;
339  element.jetp_index = i;
340  element.dij = 0.0;
341  element.max_dij_so_far = 0.0;
342 
343  _history.push_back(element);
344 
345  // do any momentum preprocessing needed by the recombination scheme
346  _jet_def.recombiner()->preprocess(_jets[i]);
347 
348  // get cross-referencing right from PseudoJets
349  _jets[i].set_cluster_hist_index(i);
350  _set_structure_shared_ptr(_jets[i]);
351 
352  // determine the total energy in the event
353  _Qtot += _jets[i].E();
354  }
355  _initial_n = _jets.size();
356  _deletes_self_when_unused = false;
357 }
358 
359 
360 //----------------------------------------------------------------------
361 // Return the component corresponding to the specified index.
362 // taken from CLHEP
363 string ClusterSequence::strategy_string (Strategy strategy_in) const {
364  string strategy;
365  switch(strategy_in) {
366  case NlnN:
367  strategy = "NlnN"; break;
368  case NlnN3pi:
369  strategy = "NlnN3pi"; break;
370  case NlnN4pi:
371  strategy = "NlnN4pi"; break;
372  case N2Plain:
373  strategy = "N2Plain"; break;
374  case N2Tiled:
375  strategy = "N2Tiled"; break;
376  case N2MinHeapTiled:
377  strategy = "N2MinHeapTiled"; break;
378  case N2PoorTiled:
379  strategy = "N2PoorTiled"; break;
380  case N3Dumb:
381  strategy = "N3Dumb"; break;
382  case NlnNCam4pi:
383  strategy = "NlnNCam4pi"; break;
384  case NlnNCam2pi2R:
385  strategy = "NlnNCam2pi2R"; break;
386  case NlnNCam:
387  strategy = "NlnNCam"; break; // 2piMultD
388  case plugin_strategy:
389  strategy = "plugin strategy"; break;
390  default:
391  strategy = "Unrecognized";
392  }
393  return strategy;
394 }
395 
396 
397 double ClusterSequence::jet_scale_for_algorithm(
398  const PseudoJet & jet) const {
399  if (_jet_algorithm == kt_algorithm) {return jet.kt2();}
400  else if (_jet_algorithm == cambridge_algorithm) {return 1.0;}
401  else if (_jet_algorithm == antikt_algorithm) {
402  double kt2=jet.kt2();
403  return kt2 > 1e-300 ? 1.0/kt2 : 1e300;
404  } else if (_jet_algorithm == genkt_algorithm) {
405  double kt2 = jet.kt2();
406  double p = jet_def().extra_param();
407  if (p <= 0 && kt2 < 1e-300) kt2 = 1e-300; // dodgy safety check
408  return pow(kt2, p);
409  } else if (_jet_algorithm == cambridge_for_passive_algorithm) {
410  double kt2 = jet.kt2();
411  double lim = _jet_def.extra_param();
412  if (kt2 < lim*lim && kt2 != 0.0) {
413  return 1.0/kt2;
414  } else {return 1.0;}
415  } else {throw Error("Unrecognised jet algorithm");}
416 }
417 
418 
419 //----------------------------------------------------------------------
420 /// transfer the sequence contained in other_seq into our own;
421 /// any plugin "extras" contained in the from_seq will be lost
422 /// from there.
423 void ClusterSequence::transfer_from_sequence(ClusterSequence & from_seq) {
424 
425  if (will_delete_self_when_unused())
426  throw(Error("cannot use CS::transfer_from_sequence after a call to delete_self_when_unused()"));
427 
428  // the metadata
429  _jet_def = from_seq._jet_def ;
430  _writeout_combinations = from_seq._writeout_combinations ;
431  _initial_n = from_seq._initial_n ;
432  _Rparam = from_seq._Rparam ;
433  _R2 = from_seq._R2 ;
434  _invR2 = from_seq._invR2 ;
435  _strategy = from_seq._strategy ;
436  _jet_algorithm = from_seq._jet_algorithm ;
437  _plugin_activated = from_seq._plugin_activated ;
438 
439  // the data
440  _jets = from_seq._jets;
441  _history = from_seq._history;
442  // the following transferse ownership of the extras from the from_seq
443  _extras = from_seq._extras;
444 
445  // transfer of ownership
446  if (_structure_shared_ptr()) {
447  // anything that is currently associated with the cluster sequence
448  // should be told that its cluster sequence no longer exists
449  ClusterSequenceStructure* csi = dynamic_cast<ClusterSequenceStructure*>(_structure_shared_ptr());
450  assert(csi != NULL);
451  csi->set_associated_cs(NULL);
452  }
453  // create a new _structure_shared_ptr to reflect the fact that
454  // this CS is essentially a new one
455  _structure_shared_ptr.reset(new ClusterSequenceStructure(this));
456  _update_structure_use_count();
457 
458  for (vector<PseudoJet>::iterator jit = _jets.begin(); jit != _jets.end(); jit++)
459  _set_structure_shared_ptr(*jit);
460 }
461 
462 
463 //----------------------------------------------------------------------
464 // transfer the sequence contained in other_seq into our own;
465 // any plugin "extras" contained in the from_seq will be lost
466 // from there.
467 //
468 // It also sets the ClusterSequence pointers of the PseudoJets in
469 // the history to point to this ClusterSequence
470 //
471 // The second argument is an action that will be applied on every
472 // jets in the resulting ClusterSequence
473 void ClusterSequence::transfer_from_sequence(ClusterSequence & from_seq,
474  const FunctionOfPseudoJet<PseudoJet> &action_on_jets){
475  // first do the transfer
476  transfer_from_sequence(from_seq);
477 
478  // then apply the transformation
479  for (vector<PseudoJet>::iterator jit = _jets.begin(); jit != _jets.end(); jit++)
480  *jit = action_on_jets(*jit);
481 }
482 
483 
484 //----------------------------------------------------------------------
485 // record an ij recombination and reset the _jets[newjet_k] momentum and
486 // user index to be those of newjet
487 void ClusterSequence::plugin_record_ij_recombination(
488  int jet_i, int jet_j, double dij,
489  const PseudoJet & newjet, int & newjet_k) {
490 
491  plugin_record_ij_recombination(jet_i, jet_j, dij, newjet_k);
492 
493  // now transfer newjet into place
494  int tmp_index = _jets[newjet_k].cluster_hist_index();
495  _jets[newjet_k] = newjet;
496  _jets[newjet_k].set_cluster_hist_index(tmp_index);
497  _set_structure_shared_ptr(_jets[newjet_k]);
498 }
499 
500 
501 //----------------------------------------------------------------------
502 // return all inclusive jets with pt > ptmin
503 vector<PseudoJet> ClusterSequence::inclusive_jets (const double & ptmin) const{
504  double dcut = ptmin*ptmin;
505  int i = _history.size() - 1; // last jet
506  vector<PseudoJet> jets;
507  if (_jet_algorithm == kt_algorithm) {
508  while (i >= 0) {
509  // with our specific definition of dij and diB (i.e. R appears only in
510  // dij), then dij==diB is the same as the jet.perp2() and we can exploit
511  // this in selecting the jets...
512  if (_history[i].max_dij_so_far < dcut) {break;}
513  if (_history[i].parent2 == BeamJet && _history[i].dij >= dcut) {
514  // for beam jets
515  int parent1 = _history[i].parent1;
516  jets.push_back(_jets[_history[parent1].jetp_index]);}
517  i--;
518  }
519  } else if (_jet_algorithm == cambridge_algorithm) {
520  while (i >= 0) {
521  // inclusive jets are all at end of clustering sequence in the
522  // Cambridge algorithm -- so if we find a non-exclusive jet, then
523  // we can exit
524  if (_history[i].parent2 != BeamJet) {break;}
525  int parent1 = _history[i].parent1;
526  const PseudoJet & jet = _jets[_history[parent1].jetp_index];
527  if (jet.perp2() >= dcut) {jets.push_back(jet);}
528  i--;
529  }
530  } else if (_jet_algorithm == plugin_algorithm
531  || _jet_algorithm == ee_kt_algorithm
532  || _jet_algorithm == antikt_algorithm
533  || _jet_algorithm == genkt_algorithm
534  || _jet_algorithm == ee_genkt_algorithm
535  || _jet_algorithm == cambridge_for_passive_algorithm) {
536  // for inclusive jets with a plugin algorithm, we make no
537  // assumptions about anything (relation of dij to momenta,
538  // ordering of the dij, etc.)
539  while (i >= 0) {
540  if (_history[i].parent2 == BeamJet) {
541  int parent1 = _history[i].parent1;
542  const PseudoJet & jet = _jets[_history[parent1].jetp_index];
543  if (jet.perp2() >= dcut) {jets.push_back(jet);}
544  }
545  i--;
546  }
547  } else {throw Error("cs::inclusive_jets(...): Unrecognized jet algorithm");}
548  return jets;
549 }
550 
551 
552 //----------------------------------------------------------------------
553 // return the number of exclusive jets that would have been obtained
554 // running the algorithm in exclusive mode with the given dcut
555 int ClusterSequence::n_exclusive_jets (const double & dcut) const {
556 
557  // first locate the point where clustering would have stopped (i.e. the
558  // first time max_dij_so_far > dcut)
559  int i = _history.size() - 1; // last jet
560  while (i >= 0) {
561  if (_history[i].max_dij_so_far <= dcut) {break;}
562  i--;
563  }
564  int stop_point = i + 1;
565  // relation between stop_point, njets assumes one extra jet disappears
566  // at each clustering.
567  int njets = 2*_initial_n - stop_point;
568  return njets;
569 }
570 
571 //----------------------------------------------------------------------
572 // return all exclusive jets that would have been obtained running
573 // the algorithm in exclusive mode with the given dcut
574 vector<PseudoJet> ClusterSequence::exclusive_jets (const double & dcut) const {
575  int njets = n_exclusive_jets(dcut);
576  return exclusive_jets(njets);
577 }
578 
579 
580 //----------------------------------------------------------------------
581 // return the jets obtained by clustering the event to n jets.
582 vector<PseudoJet> ClusterSequence::exclusive_jets (const int & njets) const {
583 
584  // make sure the user does not ask for more than jets than there
585  // were particles in the first place.
586  assert (njets <= _initial_n);
587 
588  // provide a warning when extracting exclusive jets for algorithms
589  // that does not support it explicitly.
590  // Native algorithm that support it are: kt, ee_kt, cambridge,
591  // genkt and ee_genkt (both with p>=0)
592  // For plugins, we check Plugin::exclusive_sequence_meaningful()
593  if (( _jet_def.jet_algorithm() != kt_algorithm) &&
594  ( _jet_def.jet_algorithm() != cambridge_algorithm) &&
595  ( _jet_def.jet_algorithm() != ee_kt_algorithm) &&
596  (((_jet_def.jet_algorithm() != genkt_algorithm) &&
597  (_jet_def.jet_algorithm() != ee_genkt_algorithm)) ||
598  (_jet_def.extra_param() <0)) &&
599  ((_jet_def.jet_algorithm() != plugin_algorithm) ||
600  (!_jet_def.plugin()->exclusive_sequence_meaningful())) &&
601  (_n_exclusive_warnings < 5)) {
602  _n_exclusive_warnings++;
603  cerr << "FastJet WARNING: dcut and exclusive jets for jet-finders other than kt should be interpreted with care." << endl;
604  }
605 
606 
607  // calculate the point where we have to stop the clustering.
608  // relation between stop_point, njets assumes one extra jet disappears
609  // at each clustering.
610  int stop_point = 2*_initial_n - njets;
611 
612  // some sanity checking to make sure that e+e- does not give us
613  // surprises (should we ever implement e+e-)...
614  if (2*_initial_n != static_cast<int>(_history.size())) {
615  ostringstream err;
616  err << "2*_initial_n != _history.size() -- this endangers internal assumptions!\n";
617  throw Error(err.str());
618  //assert(false);
619  }
620 
621  // now go forwards and reconstitute the jets that we have --
622  // basically for any history element, see if the parent jets to
623  // which it refers were created before the stopping point -- if they
624  // were then add them to the list, otherwise they are subsequent
625  // recombinations of the jets that we are looking for.
626  vector<PseudoJet> jets;
627  for (unsigned int i = stop_point; i < _history.size(); i++) {
628  int parent1 = _history[i].parent1;
629  if (parent1 < stop_point) {
630  jets.push_back(_jets[_history[parent1].jetp_index]);
631  }
632  int parent2 = _history[i].parent2;
633  if (parent2 < stop_point && parent2 > 0) {
634  jets.push_back(_jets[_history[parent2].jetp_index]);
635  }
636 
637  }
638 
639  // sanity check...
640  if (static_cast<int>(jets.size()) != njets) {
641  ostringstream err;
642  err << "ClusterSequence::exclusive_jets: size of returned vector ("
643  <<jets.size()<<") does not coincide with requested number of jets ("
644  <<njets<<")";
645  throw Error(err.str());
646  }
647 
648  return jets;
649 }
650 
651 //----------------------------------------------------------------------
652 /// return the dmin corresponding to the recombination that went from
653 /// n+1 to n jets
654 double ClusterSequence::exclusive_dmerge (const int & njets) const {
655  assert(njets >= 0);
656  if (njets >= _initial_n) {return 0.0;}
657  return _history[2*_initial_n-njets-1].dij;
658 }
659 
660 
661 //----------------------------------------------------------------------
662 /// return the maximum of the dmin encountered during all recombinations
663 /// up to the one that led to an n-jet final state; identical to
664 /// exclusive_dmerge, except in cases where the dmin do not increase
665 /// monotonically.
666 double ClusterSequence::exclusive_dmerge_max (const int & njets) const {
667  assert(njets >= 0);
668  if (njets >= _initial_n) {return 0.0;}
669  return _history[2*_initial_n-njets-1].max_dij_so_far;
670 }
671 
672 
673 //----------------------------------------------------------------------
674 /// return a vector of all subjets of the current jet (in the sense
675 /// of the exclusive algorithm) that would be obtained when running
676 /// the algorithm with the given dcut.
677 std::vector<PseudoJet> ClusterSequence::exclusive_subjets
678  (const PseudoJet & jet, const double & dcut) const {
679 
680  set<const history_element*> subhist;
681 
682  // get the set of history elements that correspond to subjets at
683  // scale dcut
684  get_subhist_set(subhist, jet, dcut, 0);
685 
686  // now transfer this into a sequence of jets
687  vector<PseudoJet> subjets;
688  subjets.reserve(subhist.size());
689  for (set<const history_element*>::iterator elem = subhist.begin();
690  elem != subhist.end(); elem++) {
691  subjets.push_back(_jets[(*elem)->jetp_index]);
692  }
693  return subjets;
694 }
695 
696 //----------------------------------------------------------------------
697 /// return the size of exclusive_subjets(...); still n ln n with same
698 /// coefficient, but marginally more efficient than manually taking
699 /// exclusive_subjets.size()
700 int ClusterSequence::n_exclusive_subjets(const PseudoJet & jet,
701  const double & dcut) const {
702  set<const history_element*> subhist;
703  // get the set of history elements that correspond to subjets at
704  // scale dcut
705  get_subhist_set(subhist, jet, dcut, 0);
706  return subhist.size();
707 }
708 
709 //----------------------------------------------------------------------
710 /// return the list of subjets obtained by unclustering the supplied
711 /// jet down to n subjets (or all constituents if there are fewer
712 /// than n).
713 std::vector<PseudoJet> ClusterSequence::exclusive_subjets
714  (const PseudoJet & jet, int n) const {
715 
716  set<const history_element*> subhist;
717 
718  // get the set of history elements that correspond to subjets at
719  // scale dcut
720  get_subhist_set(subhist, jet, -1.0, n);
721 
722  // now transfer this into a sequence of jets
723  vector<PseudoJet> subjets;
724  subjets.reserve(subhist.size());
725  for (set<const history_element*>::iterator elem = subhist.begin();
726  elem != subhist.end(); elem++) {
727  subjets.push_back(_jets[(*elem)->jetp_index]);
728  }
729  return subjets;
730 }
731 
732 
733 //----------------------------------------------------------------------
734 /// return the dij that was present in the merging nsub+1 -> nsub
735 /// subjets inside this jet.
736 ///
737 /// If the jet has nsub or fewer constituents, it will return 0.
738 double ClusterSequence::exclusive_subdmerge(const PseudoJet & jet, int nsub) const {
739  set<const history_element*> subhist;
740 
741  // get the set of history elements that correspond to subjets at
742  // scale dcut
743  get_subhist_set(subhist, jet, -1.0, nsub);
744 
745  set<const history_element*>::iterator highest = subhist.end();
746  highest--;
747  /// will be zero if nconst <= nsub, since highest will be an original
748  /// particle have zero dij
749  return (*highest)->dij;
750 }
751 
752 
753 //----------------------------------------------------------------------
754 /// return the maximum dij that occurred in the whole event at the
755 /// stage that the nsub+1 -> nsub merge of subjets occurred inside
756 /// this jet.
757 ///
758 /// If the jet has nsub or fewer constituents, it will return 0.
759 double ClusterSequence::exclusive_subdmerge_max(const PseudoJet & jet, int nsub) const {
760 
761  set<const history_element*> subhist;
762 
763  // get the set of history elements that correspond to subjets at
764  // scale dcut
765  get_subhist_set(subhist, jet, -1.0, nsub);
766 
767  set<const history_element*>::iterator highest = subhist.end();
768  highest--;
769  /// will be zero if nconst <= nsub, since highest will be an original
770  /// particle have zero dij
771  return (*highest)->max_dij_so_far;
772 }
773 
774 
775 
776 //----------------------------------------------------------------------
777 /// return a set of pointers to history entries corresponding to the
778 /// subjets of this jet; one stops going working down through the
779 /// subjets either when
780 /// - there is no further to go
781 /// - one has found maxjet entries
782 /// - max_dij_so_far <= dcut
783 void ClusterSequence::get_subhist_set(set<const history_element*> & subhist,
784  const PseudoJet & jet,
785  double dcut, int maxjet) const {
786  assert(contains(jet));
787 
788  subhist.clear();
789  subhist.insert(&(_history[jet.cluster_hist_index()]));
790 
791  // establish the set of jets that are relevant
792  int njet = 1;
793  while (true) {
794  // first find out if we need to probe deeper into jet.
795  // Get history element closest to end of sequence
796  set<const history_element*>::iterator highest = subhist.end();
797  assert (highest != subhist.begin());
798  highest--;
799  const history_element* elem = *highest;
800  // make sure we haven't got too many jets
801  if (njet == maxjet) break;
802  // make sure it has parents
803  if (elem->parent1 < 0) break;
804  // make sure that we still resolve it at scale dcut
805  if (elem->max_dij_so_far <= dcut) break;
806 
807  // then do so: replace "highest" with its two parents
808  subhist.erase(highest);
809  subhist.insert(&(_history[elem->parent1]));
810  subhist.insert(&(_history[elem->parent2]));
811  njet++;
812  }
813 }
814 
815 //----------------------------------------------------------------------
816 // work through the object's history until
817 bool ClusterSequence::object_in_jet(const PseudoJet & object,
818  const PseudoJet & jet) const {
819 
820  // make sure the object conceivably belongs to this clustering
821  // sequence
822  assert(contains(object) && contains(jet));
823 
824  const PseudoJet * this_object = &object;
825  const PseudoJet * childp;
826  while(true) {
827  if (this_object->cluster_hist_index() == jet.cluster_hist_index()) {
828  return true;
829  } else if (has_child(*this_object, childp)) {
830  this_object = childp;
831  } else {
832  return false;
833  }
834  }
835 }
836 
837 //----------------------------------------------------------------------
838 /// if the jet has parents in the clustering, it returns true
839 /// and sets parent1 and parent2 equal to them.
840 ///
841 /// if it has no parents it returns false and sets parent1 and
842 /// parent2 to zero
843 bool ClusterSequence::has_parents(const PseudoJet & jet, PseudoJet & parent1,
844  PseudoJet & parent2) const {
845 
846  const history_element & hist = _history[jet.cluster_hist_index()];
847 
848  // make sure we do not run into any unexpected situations --
849  // i.e. both parents valid, or neither
850  assert ((hist.parent1 >= 0 && hist.parent2 >= 0) ||
851  (hist.parent1 < 0 && hist.parent2 < 0));
852 
853  if (hist.parent1 < 0) {
854  parent1 = PseudoJet(0.0,0.0,0.0,0.0);
855  parent2 = parent1;
856  return false;
857  } else {
858  parent1 = _jets[_history[hist.parent1].jetp_index];
859  parent2 = _jets[_history[hist.parent2].jetp_index];
860  // order the parents in decreasing pt
861  if (parent1.perp2() < parent2.perp2()) std::swap(parent1,parent2);
862  return true;
863  }
864 }
865 
866 //----------------------------------------------------------------------
867 /// if the jet has a child then return true and give the child jet
868 /// otherwise return false and set the child to zero
869 bool ClusterSequence::has_child(const PseudoJet & jet, PseudoJet & child) const {
870 
871  //const history_element & hist = _history[jet.cluster_hist_index()];
872  //
873  //if (hist.child >= 0) {
874  // child = _jets[_history[hist.child].jetp_index];
875  // return true;
876  //} else {
877  // child = PseudoJet(0.0,0.0,0.0,0.0);
878  // return false;
879  //}
880  const PseudoJet * childp;
881  bool res = has_child(jet, childp);
882  if (res) {
883  child = *childp;
884  return true;
885  } else {
886  child = PseudoJet(0.0,0.0,0.0,0.0);
887  return false;
888  }
889 }
890 
891 bool ClusterSequence::has_child(const PseudoJet & jet, const PseudoJet * & childp) const {
892 
893  const history_element & hist = _history[jet.cluster_hist_index()];
894 
895  // check that this jet has a child and that the child corresponds to
896  // a true jet [RETHINK-IF-CHANGE-NUMBERING: what is the right
897  // behaviour if the child is the same jet but made inclusive...?]
898  if (hist.child >= 0 && _history[hist.child].jetp_index >= 0) {
899  childp = &(_jets[_history[hist.child].jetp_index]);
900  return true;
901  } else {
902  childp = NULL;
903  return false;
904  }
905 }
906 
907 
908 //----------------------------------------------------------------------
909 /// if this jet has a child (and so a partner) return true
910 /// and give the partner, otherwise return false and set the
911 /// partner to zero
912 bool ClusterSequence::has_partner(const PseudoJet & jet,
913  PseudoJet & partner) const {
914 
915  const history_element & hist = _history[jet.cluster_hist_index()];
916 
917  // make sure we have a child and that the child does not correspond
918  // to a clustering with the beam (or some other invalid quantity)
919  if (hist.child >= 0 && _history[hist.child].parent2 >= 0) {
920  const history_element & child_hist = _history[hist.child];
921  if (child_hist.parent1 == jet.cluster_hist_index()) {
922  // partner will be child's parent2 -- for iB clustering
923  // parent2 will not be valid
924  partner = _jets[_history[child_hist.parent2].jetp_index];
925  } else {
926  // partner will be child's parent1
927  partner = _jets[_history[child_hist.parent1].jetp_index];
928  }
929  return true;
930  } else {
931  partner = PseudoJet(0.0,0.0,0.0,0.0);
932  return false;
933  }
934 }
935 
936 
937 //----------------------------------------------------------------------
938 // return a vector of the particles that make up a jet
939 vector<PseudoJet> ClusterSequence::constituents (const PseudoJet & jet) const {
940  vector<PseudoJet> subjets;
941  add_constituents(jet, subjets);
942  return subjets;
943 }
944 
945 //----------------------------------------------------------------------
946 /// output the supplied vector of jets in a format that can be read
947 /// by an appropriate root script; the format is:
948 /// jet-n jet-px jet-py jet-pz jet-E
949 /// particle-n particle-rap particle-phi particle-pt
950 /// particle-n particle-rap particle-phi particle-pt
951 /// ...
952 /// #END
953 /// ... [i.e. above repeated]
954 void ClusterSequence::print_jets_for_root(const std::vector<PseudoJet> & jets,
955  ostream & ostr) const {
956  for (unsigned i = 0; i < jets.size(); i++) {
957  ostr << i << " "
958  << jets[i].px() << " "
959  << jets[i].py() << " "
960  << jets[i].pz() << " "
961  << jets[i].E() << endl;
962  vector<PseudoJet> cst = constituents(jets[i]);
963  for (unsigned j = 0; j < cst.size() ; j++) {
964  ostr << " " << j << " "
965  << cst[j].rap() << " "
966  << cst[j].phi() << " "
967  << cst[j].perp() << endl;
968  }
969  ostr << "#END" << endl;
970  }
971 }
972 
973 void ClusterSequence::print_jets_for_root(const std::vector<PseudoJet> & jets,
974  const std::string & filename,
975  const std::string & comment ) const {
976  std::ofstream ostr(filename.c_str());
977  if (comment != "") ostr << "# " << comment << endl;
978  print_jets_for_root(jets, ostr);
979 }
980 
981 
982 // Not yet. Perhaps in a future release
983 // //----------------------------------------------------------------------
984 // // print out all inclusive jets with pt > ptmin
985 // void ClusterSequence::print_jets (const double & ptmin) const{
986 // vector<PseudoJet> jets = sorted_by_pt(inclusive_jets(ptmin));
987 //
988 // for (size_t j = 0; j < jets.size(); j++) {
989 // printf("%5u %7.3f %7.3f %9.3f\n",
990 // j,jets[j].rap(),jets[j].phi(),jets[j].perp());
991 // }
992 // }
993 
994 //----------------------------------------------------------------------
995 /// returns a vector of size n_particles() which indicates, for
996 /// each of the initial particles (in the order in which they were
997 /// supplied), which of the supplied jets it belongs to; if it does
998 /// not belong to any of the supplied jets, the index is set to -1;
999 vector<int> ClusterSequence::particle_jet_indices(
1000  const vector<PseudoJet> & jets) const {
1001 
1002  vector<int> indices(n_particles());
1003 
1004  // first label all particles as not belonging to any jets
1005  for (unsigned ipart = 0; ipart < n_particles(); ipart++)
1006  indices[ipart] = -1;
1007 
1008  // then for each of the jets relabel its consituents as belonging to
1009  // that jet
1010  for (unsigned ijet = 0; ijet < jets.size(); ijet++) {
1011 
1012  vector<PseudoJet> jet_constituents(constituents(jets[ijet]));
1013 
1014  for (unsigned ip = 0; ip < jet_constituents.size(); ip++) {
1015  // a safe (if slightly redundant) way of getting the particle
1016  // index (for initial particles it is actually safe to assume
1017  // ipart=iclust).
1018  unsigned iclust = jet_constituents[ip].cluster_hist_index();
1019  unsigned ipart = history()[iclust].jetp_index;
1020  indices[ipart] = ijet;
1021  }
1022  }
1023 
1024  return indices;
1025 }
1026 
1027 
1028 //----------------------------------------------------------------------
1029 // recursive routine that adds on constituents of jet to the subjet_vector
1030 void ClusterSequence::add_constituents (
1031  const PseudoJet & jet, vector<PseudoJet> & subjet_vector) const {
1032  // find out position in cluster history
1033  int i = jet.cluster_hist_index();
1034  int parent1 = _history[i].parent1;
1035  int parent2 = _history[i].parent2;
1036 
1037  if (parent1 == InexistentParent) {
1038  // It is an original particle (labelled by its parent having value
1039  // InexistentParent), therefore add it on to the subjet vector
1040  // Note: we add the initial particle and not simply 'jet' so that
1041  // calling add_constituents with a subtracted jet containing
1042  // only one particle will work.
1043  subjet_vector.push_back(_jets[i]);
1044  return;
1045  }
1046 
1047  // add parent 1
1048  add_constituents(_jets[_history[parent1].jetp_index], subjet_vector);
1049 
1050  // see if parent2 is a real jet; if it is then add its constituents
1051  if (parent2 != BeamJet) {
1052  add_constituents(_jets[_history[parent2].jetp_index], subjet_vector);
1053  }
1054 }
1055 
1056 
1057 
1058 //----------------------------------------------------------------------
1059 // initialise the history in a standard way
1060 void ClusterSequence::_add_step_to_history (
1061  const int & step_number, const int & parent1,
1062  const int & parent2, const int & jetp_index,
1063  const double & dij) {
1064 
1065  history_element element;
1066  element.parent1 = parent1;
1067  element.parent2 = parent2;
1068  element.jetp_index = jetp_index;
1069  element.child = Invalid;
1070  element.dij = dij;
1071  element.max_dij_so_far = max(dij,_history[_history.size()-1].max_dij_so_far);
1072  _history.push_back(element);
1073 
1074  int local_step = _history.size()-1;
1075  assert(local_step == step_number);
1076 
1077  assert(parent1 >= 0);
1078  _history[parent1].child = local_step;
1079  if (parent2 >= 0) {_history[parent2].child = local_step;}
1080 
1081  // get cross-referencing right from PseudoJets
1082  if (jetp_index != Invalid) {
1083  assert(jetp_index >= 0);
1084  //cout << _jets.size() <<" "<<jetp_index<<"\n";
1085  _jets[jetp_index].set_cluster_hist_index(local_step);
1086  _set_structure_shared_ptr(_jets[jetp_index]);
1087  }
1088 
1089  if (_writeout_combinations) {
1090  cout << local_step << ": "
1091  << parent1 << " with " << parent2
1092  << "; y = "<< dij<<endl;
1093  }
1094 
1095 }
1096 
1097 
1098 
1099 
1100 //======================================================================
1101 // Return an order in which to read the history such that _history[order[i]]
1102 // will always correspond to the same set of consituent particles if
1103 // two branching histories are equivalent in terms of the particles
1104 // contained in any given pseudojet.
1105 vector<int> ClusterSequence::unique_history_order() const {
1106 
1107  // first construct an array that will tell us the lowest constituent
1108  // of a given jet -- this will always be one of the original
1109  // particles, whose order is well defined and so will help us to
1110  // follow the tree in a unique manner.
1111  valarray<int> lowest_constituent(_history.size());
1112  int hist_n = _history.size();
1113  lowest_constituent = hist_n; // give it a large number
1114  for (int i = 0; i < hist_n; i++) {
1115  // sets things up for the initial partons
1116  lowest_constituent[i] = min(lowest_constituent[i],i);
1117  // propagates them through to the children of this parton
1118  if (_history[i].child > 0) lowest_constituent[_history[i].child]
1119  = min(lowest_constituent[_history[i].child],lowest_constituent[i]);
1120  }
1121 
1122  // establish an array for what we have and have not extracted so far
1123  valarray<bool> extracted(_history.size()); extracted = false;
1124  vector<int> unique_tree;
1125  unique_tree.reserve(_history.size());
1126 
1127  // now work our way through the tree
1128  for (unsigned i = 0; i < n_particles(); i++) {
1129  if (!extracted[i]) {
1130  unique_tree.push_back(i);
1131  extracted[i] = true;
1132  _extract_tree_children(i, extracted, lowest_constituent, unique_tree);
1133  }
1134  }
1135 
1136  return unique_tree;
1137 }
1138 
1139 //======================================================================
1140 // helper for unique_history_order
1141 void ClusterSequence::_extract_tree_children(
1142  int position,
1143  valarray<bool> & extracted,
1144  const valarray<int> & lowest_constituent,
1145  vector<int> & unique_tree) const {
1146  if (!extracted[position]) {
1147  // that means we may have unidentified parents around, so go and
1148  // collect them (extracted[position]) will then be made true)
1149  _extract_tree_parents(position,extracted,lowest_constituent,unique_tree);
1150  }
1151 
1152  // now look after the children...
1153  int child = _history[position].child;
1154  if (child >= 0) _extract_tree_children(child,extracted,lowest_constituent,unique_tree);
1155 }
1156 
1157 
1158 //======================================================================
1159 // return the list of unclustered particles
1160 vector<PseudoJet> ClusterSequence::unclustered_particles() const {
1161  vector<PseudoJet> unclustered;
1162  for (unsigned i = 0; i < n_particles() ; i++) {
1163  if (_history[i].child == Invalid)
1164  unclustered.push_back(_jets[_history[i].jetp_index]);
1165  }
1166  return unclustered;
1167 }
1168 
1169 
1170 
1171 //----------------------------------------------------------------------
1172 // returns true if the cluster sequence contains this jet (i.e. jet's
1173 // structure is this cluster sequence's and the cluster history index
1174 // is in a consistent range)
1175 bool ClusterSequence::contains(const PseudoJet & jet) const {
1176  return jet.cluster_hist_index() >= 0
1177  && jet.cluster_hist_index() < int(_history.size())
1178  && jet.structure_shared_ptr() == structure_shared_ptr();
1179 }
1180 
1181 
1182 
1183 //======================================================================
1184 // helper for unique_history_order
1185 void ClusterSequence::_extract_tree_parents(
1186  int position,
1187  valarray<bool> & extracted,
1188  const valarray<int> & lowest_constituent,
1189  vector<int> & unique_tree) const {
1190 
1191  if (!extracted[position]) {
1192  int parent1 = _history[position].parent1;
1193  int parent2 = _history[position].parent2;
1194  // where relevant order parents so that we will first treat the
1195  // one containing the smaller "lowest_constituent"
1196  if (parent1 >= 0 && parent2 >= 0) {
1197  if (lowest_constituent[parent1] > lowest_constituent[parent2])
1198  std::swap(parent1, parent2);
1199  }
1200  // then actually run through the parents to extract the constituents...
1201  if (parent1 >= 0 && !extracted[parent1])
1202  _extract_tree_parents(parent1,extracted,lowest_constituent,unique_tree);
1203  if (parent2 >= 0 && !extracted[parent2])
1204  _extract_tree_parents(parent2,extracted,lowest_constituent,unique_tree);
1205  // finally declare this position to be accounted for and push it
1206  // onto our list.
1207  unique_tree.push_back(position);
1208  extracted[position] = true;
1209  }
1210 }
1211 
1212 
1213 //======================================================================
1214 /// carries out the bookkeeping associated with the step of recombining
1215 /// jet_i and jet_j (assuming a distance dij) and returns the index
1216 /// of the recombined jet, newjet_k.
1217 void ClusterSequence::_do_ij_recombination_step(
1218  const int & jet_i, const int & jet_j,
1219  const double & dij,
1220  int & newjet_k) {
1221 
1222  // create the new jet by recombining the first two
1223  PseudoJet newjet;
1224  _jet_def.recombiner()->recombine(_jets[jet_i], _jets[jet_j], newjet);
1225  _jets.push_back(newjet);
1226  // original version...
1227  //_jets.push_back(_jets[jet_i] + _jets[jet_j]);
1228 
1229  // get its index
1230  newjet_k = _jets.size()-1;
1231 
1232  // get history index
1233  int newstep_k = _history.size();
1234  // and provide jet with the info
1235  _jets[newjet_k].set_cluster_hist_index(newstep_k);
1236 
1237  // finally sort out the history
1238  int hist_i = _jets[jet_i].cluster_hist_index();
1239  int hist_j = _jets[jet_j].cluster_hist_index();
1240 
1241  _add_step_to_history(newstep_k, min(hist_i, hist_j), max(hist_i,hist_j),
1242  newjet_k, dij);
1243 
1244 }
1245 
1246 
1247 //======================================================================
1248 /// carries out the bookkeeping associated with the step of recombining
1249 /// jet_i with the beam
1250 void ClusterSequence::_do_iB_recombination_step(
1251  const int & jet_i, const double & diB) {
1252  // get history index
1253  int newstep_k = _history.size();
1254 
1255  // recombine the jet with the beam
1256  _add_step_to_history(newstep_k,_jets[jet_i].cluster_hist_index(),BeamJet,
1257  Invalid, diB);
1258 
1259 }
1260 
1261 
1262 
1263 //======================================================================
1264 // transform the whole ClusterSequence jet (given in the rest frame
1265 // of prest) into a ClusterSequence in the lab frame [NOT FULLY
1266 // TESTED]
1267 void ClusterSequence::boost(const PseudoJet & prest){
1268  // boost every PseudoJet in the history
1269  for (vector<PseudoJet>::iterator jit=_jets.begin(); jit!=_jets.end(); jit++)
1270  jit->boost(prest);
1271 }
1272 
1273 // transform the whole ClusterSequence (given in lab) into a
1274 // ClusterSequence in the rest frame of prest [NOT FULLY TESTED]
1275 void ClusterSequence::unboost(const PseudoJet & prest){
1276  // boost every PseudoJet in the history
1277  for (vector<PseudoJet>::iterator jit=_jets.begin(); jit!=_jets.end(); jit++)
1278  jit->unboost(prest);
1279 }
1280 
1281 
1282 // make sure the static member _changed_strategy_warning is defined.
1283 LimitedWarning ClusterSequence::_changed_strategy_warning;
1284 
1285 
1286 //----------------------------------------------------------------------
1287 void ClusterSequence::_set_structure_shared_ptr(PseudoJet & j) {
1288  j.set_structure_shared_ptr(_structure_shared_ptr);
1289  // record the use count of the structure shared point to help
1290  // in case we want to ask the CS to handle its own memory
1291  _update_structure_use_count();
1292 }
1293 
1294 
1295 //----------------------------------------------------------------------
1296 void ClusterSequence::_update_structure_use_count() {
1297  // record the use count of the structure shared point to help
1298  // in case we want to ask the CS to handle its own memory
1299  _structure_use_count_after_construction = _structure_shared_ptr.use_count();
1300 }
1301 
1302 //----------------------------------------------------------------------
1303 /// by calling this routine you tell the ClusterSequence to delete
1304 /// itself when all the Pseudojets associated with it have gone out
1305 /// of scope.
1306 void ClusterSequence::delete_self_when_unused() {
1307  // the trick we use to handle this is to modify the use count;
1308  // that way the structure will be deleted when there are no external
1309  // objects left associated the CS and the structure's destructor will then
1310  // look after deleting the cluster sequence
1311 
1312  // first make sure that there is at least one other object
1313  // associated with the CS
1314  int new_count = _structure_shared_ptr.use_count() - _structure_use_count_after_construction;
1315  if (new_count <= 0) {
1316  throw Error("delete_self_when_unused may only be called if at least one object outside the CS (e.g. a jet) is already associated with the CS");
1317  }
1318 
1319  _structure_shared_ptr.set_count(new_count);
1320  _deletes_self_when_unused = true;
1321 }
1322 
1323 
1324 FASTJET_END_NAMESPACE
1325 
like the k_t but with distance measures dij = min(kti^{2p},ktj^{2p}) Delta R_{ij}^2 / R^2 diB = 1/kti...
Chan&#39;s closest pair method (in a variant with 4pi coverage), for use exclusively with the Cambridge a...
best of the NlnN variants – best overall for N>10^4.
legacy N ln N using 4pi coverage of cylinder
the e+e- genkt algorithm (R > 2 and p=1 gives ee_kt)
fastest from about 50..500
the longitudinally invariant kt algorithm
Chan&#39;s closest pair method (in a variant with 2pi+minimal extra variant), for use exclusively with th...
any plugin algorithm supplied by the user
the plugin has been used...
worse even than the usual N^3 algorithms
string fastjet_version_string()
return a string containing information about the release
automatic selection of the best (based on N), including the LazyTiled strategies that are new to FJ3...
fastest below 50
like the k_t but with distance measures dij = min(1/kti^2,1/ktj^2) Delta R_{ij}^2 / R^2 diB = 1/kti^2...
the e+e- kt algorithm
the longitudinally invariant variant of the cambridge algorithm (aka Aachen algoithm).
JetAlgorithm
the various families of jet-clustering algorithm
legacy N ln N using 3pi coverage of cylinder.
faster that N2Tiled above about 500 particles; differs from it by retainig the di(closest j) distance...
Chan&#39;s closest pair method (in a variant with 2pi+2R coverage), for use exclusively with the Cambridg...