|
SISCone
2.0.5
|
00001 00002 // File: split_merge.cpp // 00003 // Description: source file for splitting/merging (contains the CJet class) // 00004 // This file is part of the SISCone project. // 00005 // For more details, see http://projects.hepforge.org/siscone // 00006 // // 00007 // Copyright (c) 2006 Gavin Salam and Gregory Soyez // 00008 // // 00009 // This program 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 // This program is distributed in the hope that it will be useful, // 00015 // but WITHOUT ANY WARRANTY; without even the implied warranty of // 00016 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // 00017 // GNU General Public License for more details. // 00018 // // 00019 // You should have received a copy of the GNU General Public License // 00020 // along with this program; if not, write to the Free Software // 00021 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA // 00022 // // 00023 // $Revision:: 322 $// 00024 // $Date:: 2011-11-15 10:12:36 +0100 (Tue, 15 Nov 2011) $// 00026 00027 #include "split_merge.h" 00028 #include "siscone_error.h" 00029 #include "momentum.h" 00030 #include <math.h> 00031 #include <limits> // for max 00032 #include <iostream> 00033 #include <algorithm> 00034 #include <sstream> 00035 #include <cassert> 00036 00037 namespace siscone{ 00038 00039 using namespace std; 00040 00041 /******************************************************** 00042 * class Cjet implementation * 00043 * real Jet information. * 00044 * This class contains information for one single jet. * 00045 * That is, first, its momentum carrying information * 00046 * about its centre and pT, and second, its particle * 00047 * contents * 00048 ********************************************************/ 00049 // default ctor 00050 //-------------- 00051 Cjet::Cjet(){ 00052 n = 0; 00053 v = Cmomentum(); 00054 pt_tilde = 0.0; 00055 sm_var2 = 0.0; 00056 } 00057 00058 // default dtor 00059 //-------------- 00060 Cjet::~Cjet(){ 00061 00062 } 00063 00064 // ordering of jets in pt (e.g. used in final jets ordering) 00065 //----------------------------------------------------------- 00066 bool jets_pt_less(const Cjet &j1, const Cjet &j2){ 00067 return j1.v.perp2() > j2.v.perp2(); 00068 } 00069 00070 00071 /******************************************************** 00072 * Csplit_merge_ptcomparison implementation * 00073 * This deals with the ordering of the jets candidates * 00074 ********************************************************/ 00075 00076 // odering of two jets 00077 // The variable of the ordering is pt or mt 00078 // depending on 'split_merge_scale' choice 00079 // 00080 // with EPSILON_SPLITMERGE defined, this attempts to identify 00081 // delicate cases where two jets have identical momenta except for 00082 // softish particles -- the difference of pt's may not be correctly 00083 // identified normally and so lead to problems for the fate of the 00084 // softish particle. 00085 // 00086 // NB: there is a potential issue in momentum-conserving events, 00087 // whereby the harder of two jets becomes ill-defined when a soft 00088 // particle is emitted --- this may have a knock-on effect on 00089 // subsequent split-merge steps in cases with sufficiently large R 00090 // (but we don't know what the limit is...) 00091 //------------------------------------------------------------------ 00092 bool Csplit_merge_ptcomparison::operator ()(const Cjet &jet1, const Cjet &jet2) const{ 00093 double q1, q2; 00094 00095 // compute the value for comparison for both jets 00096 // This depends on the choice of variable (mt is the default) 00097 q1 = jet1.sm_var2; 00098 q2 = jet2.sm_var2; 00099 00100 bool res = q1 > q2; 00101 00102 // if we enable the refined version of the comparison (see defines.h), 00103 // we compute the difference more precisely when the two jets are very 00104 // close in the ordering variable. 00105 #ifdef EPSILON_SPLITMERGE 00106 if ( (fabs(q1-q2) < EPSILON_SPLITMERGE*max(q1,q2)) && 00107 (jet1.v.ref != jet2.v.ref) ) { 00108 // get the momentum of the difference 00109 Cmomentum difference; 00110 double pt_tilde_difference; 00111 get_difference(jet1,jet2,&difference,&pt_tilde_difference); 00112 00113 // use the following relation: pt1^2 - pt2^2 = (pt1+pt2)*(pt1-pt2) 00114 double qdiff; 00115 Cmomentum sum = jet1.v ; 00116 sum += jet2.v; 00117 double pt_tilde_sum = jet1.pt_tilde + jet2.pt_tilde; 00118 00119 // depending on the choice of ordering variable, set the result 00120 switch (split_merge_scale){ 00121 case SM_mt: 00122 qdiff = sum.E*difference.E - sum.pz*difference.pz; 00123 break; 00124 case SM_pt: 00125 qdiff = sum.px*difference.px + sum.py*difference.py; 00126 break; 00127 case SM_pttilde: 00128 qdiff = pt_tilde_sum*pt_tilde_difference; 00129 break; 00130 case SM_Et: 00131 // diff = E^2 (dpt^2 pz^2- pt^2 dpz^2) 00132 // + dE^2 (pt^2+pz^2) pt2^2 00133 // where, unless explicitely specified the variables 00134 // refer to the first jet or differences jet1-jet2. 00135 qdiff = jet1.v.E*jet1.v.E* 00136 ((sum.px*difference.px + sum.py*difference.py)*jet1.v.pz*jet1.v.pz 00137 -jet1.v.perp2()*sum.pz*difference.pz) 00138 +sum.E*difference.E*(jet1.v.perp2()+jet1.v.pz*jet1.v.pz)*jet2.v.perp2(); 00139 break; 00140 default: 00141 throw Csiscone_error("Unsupported split-merge scale choice: " 00142 + SM_scale_name()); 00143 } 00144 res = qdiff > 0; 00145 } 00146 #endif // EPSILON_SPLITMERGE 00147 00148 return res; 00149 } 00150 00151 00154 std::string split_merge_scale_name(Esplit_merge_scale sms) { 00155 switch(sms) { 00156 case SM_pt: 00157 return "pt (IR unsafe)"; 00158 case SM_Et: 00159 return "Et (boost dep.)"; 00160 case SM_mt: 00161 return "mt (IR safe except for pairs of identical decayed heavy particles)"; 00162 case SM_pttilde: 00163 return "pttilde (scalar sum of pt's)"; 00164 default: 00165 return "[SM scale without a name]"; 00166 } 00167 } 00168 00169 00170 // get the difference between 2 jets 00171 // - j1 first jet 00172 // - j2 second jet 00173 // - v jet1-jet2 00174 // - pt_tilde jet1-jet2 pt_tilde 00175 // return true if overlapping, false if disjoint 00176 //----------------------------------------------- 00177 void Csplit_merge_ptcomparison::get_difference(const Cjet &j1, const Cjet &j2, Cmomentum *v, double *pt_tilde) const { 00178 int i1,i2; 00179 00180 // initialise 00181 i1=i2=0; 00182 *v = Cmomentum(); 00183 *pt_tilde = 0.0; 00184 00185 // compute overlap 00186 // at the same time, we store union in indices 00187 do{ 00188 if (j1.contents[i1]==j2.contents[i2]) { 00189 i1++; 00190 i2++; 00191 } else if (j1.contents[i1]<j2.contents[i2]){ 00192 (*v) += (*particles)[j1.contents[i1]]; 00193 (*pt_tilde) += (*pt)[j1.contents[i1]]; 00194 i1++; 00195 } else if (j1.contents[i1]>j2.contents[i2]){ 00196 (*v) -= (*particles)[j2.contents[i2]]; 00197 (*pt_tilde) -= (*pt)[j2.contents[i2]]; 00198 i2++; 00199 } else { 00200 throw Csiscone_error("get_non_overlap reached part it should never have seen..."); 00201 } 00202 } while ((i1<j1.n) && (i2<j2.n)); 00203 00204 // deal with particles at the end of the list... 00205 while (i1 < j1.n) { 00206 (*v) += (*particles)[j1.contents[i1]]; 00207 (*pt_tilde) += (*pt)[j1.contents[i1]]; 00208 i1++; 00209 } 00210 while (i2 < j2.n) { 00211 (*v) -= (*particles)[j2.contents[i2]]; 00212 (*pt_tilde) -= (*pt)[j2.contents[i2]]; 00213 i2++; 00214 } 00215 } 00216 00217 00218 /******************************************************** 00219 * class Csplit_merge implementation * 00220 * Class used to split and merge jets. * 00221 ********************************************************/ 00222 // default ctor 00223 //-------------- 00224 Csplit_merge::Csplit_merge(){ 00225 merge_identical_protocones = false; 00226 #ifdef ALLOW_MERGE_IDENTICAL_PROTOCONES 00227 #ifdef MERGE_IDENTICAL_PROTOCONES_DEFAULT_TRUE 00228 merge_identical_protocones = true; 00229 #endif 00230 #endif 00231 indices = NULL; 00232 00233 // ensure that ptcomparison points to our set of particles (though params not correct) 00234 ptcomparison.particles = &particles; 00235 ptcomparison.pt = &pt; 00236 candidates.reset(new multiset<Cjet,Csplit_merge_ptcomparison>(ptcomparison)); 00237 00238 // no hardest cut (col-unsafe) 00239 SM_var2_hardest_cut_off = -1.0; 00240 00241 // no pt cutoff for the particles to put in p_uncol_hard 00242 stable_cone_soft_pt2_cutoff = -1.0; 00243 00244 // no pt-weighted splitting 00245 use_pt_weighted_splitting = false; 00246 } 00247 00248 00249 // default dtor 00250 //-------------- 00251 Csplit_merge::~Csplit_merge(){ 00252 full_clear(); 00253 } 00254 00255 00256 // initialisation function 00257 // - _particles list of particles 00258 // - protocones list of protocones (initial jet candidates) 00259 // - R2 cone radius (squared) 00260 // - ptmin minimal pT allowed for jets 00261 //------------------------------------------------------------- 00262 int Csplit_merge::init(vector<Cmomentum> & /*_particles*/, vector<Cmomentum> *protocones, double R2, double ptmin){ 00263 // browse protocones 00264 return add_protocones(protocones, R2, ptmin); 00265 } 00266 00267 00268 // initialisation function for particle list 00269 // - _particles list of particles 00270 //------------------------------------------------------------- 00271 int Csplit_merge::init_particles(vector<Cmomentum> &_particles){ 00272 full_clear(); 00273 00274 // compute the list of particles 00275 // here, we do not need to throw away particles 00276 // with infinite rapidity (colinear with the beam) 00277 particles = _particles; 00278 n = particles.size(); 00279 00280 // build the vector of particles' pt 00281 pt.resize(n); 00282 for (int i=0;i<n;i++) 00283 pt[i] = particles[i].perp(); 00284 00285 // ensure that ptcomparison points to our set of particles (though params not correct) 00286 ptcomparison.particles = &particles; 00287 ptcomparison.pt = &pt; 00288 00289 // set up the list of particles left. 00290 init_pleft(); 00291 00292 indices = new int[n]; 00293 00294 return 0; 00295 } 00296 00297 00298 // build initial list of remaining particles 00299 //------------------------------------------ 00300 int Csplit_merge::init_pleft(){ 00301 // at this level, we only rule out particles with 00302 // infinite rapidity 00303 // in the parent particle list, index contain the run 00304 // at which particles are puts in jets: 00305 // - -1 means infinity rapidity 00306 // - 0 means not included 00307 // - i mean included at run i 00308 int i,j; 00309 00310 // copy particles removing the ones with infinite rapidity 00311 // determine min,max eta 00312 j=0; 00313 double eta_min=0.0; 00314 double eta_max=0.0; 00315 p_remain.clear(); 00316 for (i=0;i<n;i++){ 00317 // set ref for checkxor 00318 particles[i].ref.randomize(); 00319 00320 // check if rapidity is not infinite or ill-defined 00321 if (fabs(particles[i].pz) < (particles[i].E)){ 00322 p_remain.push_back(particles[i]); 00323 // set up parent index for tracability 00324 p_remain[j].parent_index = i; 00325 // left particles are marked with a 1 00326 // IMPORTANT NOTE: the meaning of index in p_remain is 00327 // somehow different as in the initial particle list. 00328 // here, within one pass, we use the index to track whether 00329 // a particle is included in the current pass (index set to 0 00330 // in add_protocones) or still remain (index still 1) 00331 p_remain[j].index = 1; 00332 00333 j++; 00334 // set up parent-particle index 00335 particles[i].index = 0; 00336 00337 eta_min = min(eta_min, particles[i].eta); 00338 eta_max = max(eta_max, particles[i].eta); 00339 } else { 00340 particles[i].index = -1; 00341 } 00342 } 00343 n_left = p_remain.size(); 00344 n_pass = 0; 00345 00346 Ceta_phi_range epr; 00347 epr.eta_min = eta_min-0.01; 00348 epr.eta_max = eta_max+0.01; 00349 00350 merge_collinear_and_remove_soft(); 00351 00352 return 0; 00353 } 00354 00355 00356 // partial clearance 00357 // we want to keep particle list and indices 00358 // for future usage, so do not clear it ! 00359 // this is done in full_clear 00360 //---------------------------------------- 00361 int Csplit_merge::partial_clear(){ 00362 // release jets 00363 00364 // set up the auto_ptr for the multiset with the _current_ state of 00365 // ptcomparison (which may have changed since we constructed the 00366 // class) 00367 candidates.reset(new multiset<Cjet,Csplit_merge_ptcomparison>(ptcomparison)); 00368 00369 // start off with huge number 00370 most_ambiguous_split = numeric_limits<double>::max(); 00371 00372 jets.clear(); 00373 #ifdef ALLOW_MERGE_IDENTICAL_PROTOCONES 00374 if (merge_identical_protocones) 00375 cand_refs.clear(); 00376 #endif 00377 00378 p_remain.clear(); 00379 00380 return 0; 00381 } 00382 00383 00384 // full clearance 00385 //---------------- 00386 int Csplit_merge::full_clear(){ 00387 partial_clear(); 00388 00389 // clear previously allocated memory 00390 if (indices != NULL){ 00391 delete[] indices; 00392 } 00393 particles.clear(); 00394 00395 return 0; 00396 } 00397 00398 00399 // build the list 'p_uncol_hard' from p_remain by clustering collinear particles 00400 // note that thins in only used for stable-cone detection 00401 // so the parent_index field is unnecessary 00402 //------------------------------------------------------------------------- 00403 int Csplit_merge::merge_collinear_and_remove_soft(){ 00404 int i,j; 00405 vector<Cmomentum> p_sorted; 00406 bool collinear; 00407 double dphi; 00408 00409 p_uncol_hard.clear(); 00410 00411 // we first sort the particles according to their rapidity 00412 for (i=0;i<n_left;i++) 00413 p_sorted.push_back(p_remain[i]); 00414 sort(p_sorted.begin(), p_sorted.end(), momentum_eta_less); 00415 00416 // then we cluster particles looping over the particles in the following way 00417 // if (a particle i has same eta-phi a one after (j)) 00418 // then add momentum i to j 00419 // else add i to the p_uncol_hard list 00420 i = 0; 00421 while (i<n_left){ 00422 // check if the particle passes the stable_cone_soft_pt2_cutoff 00423 if (p_sorted[i].perp2()<stable_cone_soft_pt2_cutoff) { 00424 i++; 00425 continue; 00426 } 00427 00428 // check if there is(are) particle(s) with the 'same' eta 00429 collinear = false; 00430 j=i+1; 00431 while ((j<n_left) && (fabs(p_sorted[j].eta-p_sorted[i].eta)<EPSILON_COLLINEAR) && (!collinear)){ 00432 dphi = fabs(p_sorted[j].phi-p_sorted[i].phi); 00433 if (dphi>M_PI) dphi = twopi-dphi; 00434 if (dphi<EPSILON_COLLINEAR){ 00435 // i is collinear with j; add the momentum (i) to the momentum (j) 00436 p_sorted[j] += p_sorted[i]; 00437 // set collinearity test to true 00438 collinear = true; 00439 } 00440 j++; 00441 } 00442 // if no collinearity detected, add the particle to our list 00443 if (!collinear) 00444 p_uncol_hard.push_back(p_sorted[i]); 00445 i++; 00446 } 00447 00448 return 0; 00449 } 00450 00451 00452 // add a list of protocones 00453 // - protocones list of protocones (initial jet candidates) 00454 // - R2 cone radius (squared) 00455 // - ptmin minimal pT allowed for jets 00456 //------------------------------------------------------------- 00457 int Csplit_merge::add_protocones(vector<Cmomentum> *protocones, double R2, double ptmin){ 00458 int i; 00459 Cmomentum *c; 00460 Cmomentum *v; 00461 double eta, phi; 00462 double dx, dy; 00463 double R; 00464 Cjet jet; 00465 00466 if (protocones->size()==0) 00467 return 1; 00468 00469 pt_min2 = ptmin*ptmin; 00470 R = sqrt(R2); 00471 00472 // browse protocones 00473 // for each of them, build the list of particles in them 00474 for (vector<Cmomentum>::iterator p_it = protocones->begin();p_it != protocones->end();p_it++){ 00475 // initialise variables 00476 c = &(*p_it); 00477 00478 // note: cones have been tested => their (eta,phi) coordinates are computed 00479 eta = c->eta; 00480 phi = c->phi; 00481 00482 // browse particles to create cone contents 00483 // note that jet is always initialised with default values at this level 00484 jet.v = Cmomentum(); 00485 jet.pt_tilde=0; 00486 jet.contents.clear(); 00487 for (i=0;i<n_left;i++){ 00488 v = &(p_remain[i]); 00489 // for security, avoid including particles with infinite rapidity) 00490 // NO NEEDED ANYMORE SINCE REMOVED FROM p_remain FROM THE BEGINNING 00491 //if (fabs(v->pz)!=v->E){ 00492 dx = eta - v->eta; 00493 dy = fabs(phi - v->phi); 00494 if (dy>M_PI) 00495 dy -= twopi; 00496 if (dx*dx+dy*dy<R2){ 00497 jet.contents.push_back(v->parent_index); 00498 jet.v+= *v; 00499 jet.pt_tilde+= pt[v->parent_index]; 00500 v->index=0; 00501 } 00502 } 00503 jet.n=jet.contents.size(); 00504 00505 // set the momentum in protocones 00506 // (it was only known through eta and phi up to now) 00507 *c = jet.v; 00508 c->eta = eta; // restore exact original coords 00509 c->phi = phi; // to avoid rounding error inconsistencies 00510 00511 // set the jet range 00512 jet.range=Ceta_phi_range(eta,phi,R); 00513 00514 #ifdef DEBUG_SPLIT_MERGE 00515 cout << "adding jet: "; 00516 for (int i2=0;i2<jet.n;i2++) 00517 cout << jet.contents[i2] << " "; 00518 cout << endl; 00519 #endif 00520 00521 // add it to the list of jets 00522 insert(jet); 00523 } 00524 00525 // update list of included particles 00526 n_pass++; 00527 00528 #ifdef DEBUG_SPLIT_MERGE 00529 cout << "remaining particles: "; 00530 #endif 00531 int j=0; 00532 for (i=0;i<n_left;i++){ 00533 if (p_remain[i].index){ 00534 // copy particle 00535 p_remain[j]=p_remain[i]; 00536 p_remain[j].parent_index = p_remain[i].parent_index; 00537 p_remain[j].index=1; 00538 // set run in initial list 00539 particles[p_remain[j].parent_index].index = n_pass; 00540 #ifdef DEBUG_SPLIT_MERGE 00541 cout << p_remain[j].parent_index << " "; 00542 #endif 00543 j++; 00544 } 00545 } 00546 #ifdef DEBUG_SPLIT_MERGE 00547 cout << endl; 00548 #endif 00549 n_left = j; 00550 p_remain.resize(j); 00551 00552 merge_collinear_and_remove_soft(); 00553 00554 return 0; 00555 } 00556 00557 00558 /* 00559 * really do the splitting and merging 00560 * At the end, the vector jets is filled with the jets found. 00561 * the 'contents' field of each jets contains the indices 00562 * of the particles included in that jet. 00563 * - overlap_tshold threshold for splitting/merging transition 00564 * - ptmin minimal pT allowed for jets 00565 * return the number of jets is returned 00566 ******************************************************************/ 00567 int Csplit_merge::perform(double overlap_tshold, double ptmin){ 00568 // iterators for the 2 jets 00569 cjet_iterator j1; 00570 cjet_iterator j2; 00571 00572 pt_min2 = ptmin*ptmin; 00573 00574 if (candidates->size()==0) 00575 return 0; 00576 00577 if (overlap_tshold>=1.0 || overlap_tshold <= 0) { 00578 ostringstream message; 00579 message << "Illegal value for overlap_tshold, f = " << overlap_tshold; 00580 message << " (legal values are 0<f<1)"; 00581 throw Csiscone_error(message.str()); 00582 } 00583 00584 // overlap (the contents of this variable depends on the choice for 00585 // the split--merge variable.) 00586 // Note that the square of the ovelap is used 00587 double overlap2; 00588 00589 // avoid to compute tshold*tshold at each overlap 00590 double overlap_tshold2 = overlap_tshold*overlap_tshold; 00591 00592 do{ 00593 if (candidates->size()>0){ 00594 // browse for the first jet 00595 j1 = candidates->begin(); 00596 00597 // if hardest jet does not pass threshold then nothing else will 00598 // either so one stops the split merge. 00599 if (j1->sm_var2<SM_var2_hardest_cut_off) {break;} 00600 00601 // browse for the second jet 00602 j2 = j1; 00603 j2++; 00604 int j2_relindex = 1; // used only in ifdef, but costs little so keep it outside 00605 00606 while (j2 != candidates->end()){ 00607 #ifdef DEBUG_SPLIT_MERGE 00608 show(); 00609 #endif 00610 // check overlapping 00611 if (get_overlap(*j1, *j2, &overlap2)){ 00612 // check if overlapping energy passes threshold 00613 // Note that this depends on the ordering variable 00614 #ifdef DEBUG_SPLIT_MERGE 00615 cout << "overlap between cdt 1 and cdt " << j2_relindex+1 << " with overlap " 00616 << sqrt(overlap2/j2->sm_var2) << endl<<endl; 00617 #endif 00618 if (overlap2<overlap_tshold2*j2->sm_var2){ 00619 // split jets 00620 split(j1, j2); 00621 00622 // update iterators 00623 j2 = j1 = candidates->begin(); 00624 j2_relindex = 0; 00625 } else { 00626 // merge jets 00627 merge(j1, j2); 00628 00629 // update iterators 00630 j2 = j1 = candidates->begin(); 00631 j2_relindex = 0; 00632 } 00633 } 00634 // watch out: split/merge might have caused new jets with pt < 00635 // ptmin to disappear, so the total number of jets may 00636 // have changed by more than expected and j2 might already by 00637 // the end of the candidates list... 00638 j2_relindex++; 00639 if (j2 != candidates->end()) j2++; 00640 } // end of loop on the second jet 00641 00642 if (j1 != candidates->end()) { 00643 // all "second jet" passed without overlapping 00644 // (otherwise we won't leave the j2 loop) 00645 // => store jet 1 as real jet 00646 jets.push_back(*j1); 00647 jets[jets.size()-1].v.build_etaphi(); 00648 // a bug where the contents has non-zero size has been cropping 00649 // up in many contexts -- so catch it! 00650 assert(j1->contents.size() > 0); 00651 jets[jets.size()-1].pass = particles[j1->contents[0]].index; 00652 #ifdef ALLOW_MERGE_IDENTICAL_PROTOCONES 00653 cand_refs.erase(j1->v.ref); 00654 #endif 00655 candidates->erase(j1); 00656 00658 //if ((candidates->size()!=0) && 00659 // (candidates->begin()->sm_var2<SM_var2_hardest_cut_off)){ 00660 // candidates->clear(); 00661 //} 00662 } 00663 } 00664 } while (candidates->size()>0); 00665 00666 // sort jets by pT 00667 sort(jets.begin(), jets.end(), jets_pt_less); 00668 #ifdef DEBUG_SPLIT_MERGE 00669 show(); 00670 #endif 00671 00672 return jets.size(); 00673 } 00674 00675 00676 00677 // save the event on disk 00678 // - flux stream used to save jet contents 00679 //-------------------------------------------- 00680 int Csplit_merge::save_contents(FILE *flux){ 00681 jet_iterator it_j; 00682 Cjet *j1; 00683 int i1, i2; 00684 00685 fprintf(flux, "# %d jets found\n", (int) jets.size()); 00686 fprintf(flux, "# columns are: eta, phi, pt and number of particles for each jet\n"); 00687 for (it_j = jets.begin(), i1=0 ; it_j != jets.end() ; it_j++, i1++){ 00688 j1 = &(*it_j); 00689 j1->v.build_etaphi(); 00690 fprintf(flux, "%f\t%f\t%e\t%d\n", 00691 j1->v.eta, j1->v.phi, j1->v.perp(), j1->n); 00692 } 00693 00694 fprintf(flux, "# jet contents\n"); 00695 fprintf(flux, "# columns are: eta, phi, pt, particle index and jet number\n"); 00696 for (it_j = jets.begin(), i1=0 ; it_j != jets.end() ; it_j++, i1++){ 00697 j1 = &(*it_j); 00698 for (i2=0;i2<j1->n;i2++) 00699 fprintf(flux, "%f\t%f\t%e\t%d\t%d\n", 00700 particles[j1->contents[i2]].eta, particles[j1->contents[i2]].phi, 00701 particles[j1->contents[i2]].perp(), j1->contents[i2], i1); 00702 } 00703 00704 return 0; 00705 } 00706 00707 00708 // show current jets/candidate status 00709 //------------------------------------ 00710 int Csplit_merge::show(){ 00711 jet_iterator it_j; 00712 cjet_iterator it_c; 00713 Cjet *j; 00714 const Cjet *c; 00715 int i1, i2; 00716 00717 for (it_j = jets.begin(), i1=0 ; it_j != jets.end() ; it_j++, i1++){ 00718 j = &(*it_j); 00719 fprintf(stdout, "jet %2d: %e\t%e\t%e\t%e\t", i1+1, 00720 j->v.px, j->v.py, j->v.pz, j->v.E); 00721 for (i2=0;i2<j->n;i2++) 00722 fprintf(stdout, "%d ", j->contents[i2]); 00723 fprintf(stdout, "\n"); 00724 } 00725 00726 for (it_c = candidates->begin(), i1=0 ; it_c != candidates->end() ; it_c++, i1++){ 00727 c = &(*it_c); 00728 fprintf(stdout, "cdt %2d: %e\t%e\t%e\t%e\t%e\t", i1+1, 00729 c->v.px, c->v.py, c->v.pz, c->v.E, sqrt(c->sm_var2)); 00730 for (i2=0;i2<c->n;i2++) 00731 fprintf(stdout, "%d ", c->contents[i2]); 00732 fprintf(stdout, "\n"); 00733 } 00734 00735 fprintf(stdout, "\n"); 00736 return 0; 00737 } 00738 00739 00740 // get the overlap between 2 jets 00741 // - j1 first jet 00742 // - j2 second jet 00743 // - overlap2 returned overlap^2 (determined by the choice of SM variable) 00744 // return true if overlapping, false if disjoint 00745 //--------------------------------------------------------------------- 00746 bool Csplit_merge::get_overlap(const Cjet &j1, const Cjet &j2, double *overlap2){ 00747 // check if ranges overlap 00748 if (!is_range_overlap(j1.range,j2.range)) 00749 return false; 00750 00751 int i1,i2; 00752 bool is_overlap; 00753 00754 // initialise 00755 i1=i2=idx_size=0; 00756 is_overlap = false; 00757 Cmomentum v; 00758 double pt_tilde=0.0; 00759 00760 // compute overlap 00761 // at the same time, we store union in indices 00762 do{ 00763 if (j1.contents[i1]<j2.contents[i2]){ 00764 indices[idx_size] = j1.contents[i1]; 00765 i1++; 00766 } else if (j1.contents[i1]>j2.contents[i2]){ 00767 indices[idx_size] = j2.contents[i2]; 00768 i2++; 00769 } else { // (j1.contents[i1]==j2.contents[i2]) 00770 v += particles[j1.contents[i1]]; 00771 pt_tilde += pt[j1.contents[i1]]; 00772 indices[idx_size] = j1.contents[i1]; 00773 i1++; 00774 i2++; 00775 is_overlap = true; 00776 } 00777 idx_size++; 00778 } while ((i1<j1.n) && (i2<j2.n)); 00779 00780 // finish computing union 00781 // (only needed if overlap !) 00782 if (is_overlap){ 00783 while (i1<j1.n){ 00784 indices[idx_size] = j1.contents[i1]; 00785 i1++; 00786 idx_size++; 00787 } 00788 while (i2<j2.n){ 00789 indices[idx_size] = j2.contents[i2]; 00790 i2++; 00791 idx_size++; 00792 } 00793 } 00794 00795 // assign the overlapping var as return variable 00796 (*overlap2) = get_sm_var2(v, pt_tilde); 00797 00798 return is_overlap; 00799 } 00800 00801 00802 00803 // split the two given jet. 00804 // during this procedure, the jets j1 & j2 are replaced 00805 // by 2 new jets. Common particles are associted to the 00806 // closest initial jet. 00807 // - it_j1 iterator of the first jet in 'candidates' 00808 // - it_j2 iterator of the second jet in 'candidates' 00809 // - j1 first jet (Cjet instance) 00810 // - j2 second jet (Cjet instance) 00811 // return true on success, false on error 00813 bool Csplit_merge::split(cjet_iterator &it_j1, cjet_iterator &it_j2){ 00814 int i1, i2; 00815 Cjet jet1, jet2; 00816 double eta1, phi1, pt1_weight, eta2, phi2, pt2_weight; 00817 double dx1, dy1, dx2, dy2; 00818 Cmomentum tmp; 00819 Cmomentum *v; 00820 00821 // shorthand to avoid having "->" all over the place 00822 const Cjet & j1 = * it_j1; 00823 const Cjet & j2 = * it_j2; 00824 00825 i1=i2=0; 00826 jet2.v = jet1.v = Cmomentum(); 00827 jet2.pt_tilde = jet1.pt_tilde = 0.0; 00828 00829 // compute centroids 00830 // When use_pt_weighted_splitting is activated, the 00831 // "geometrical" distance is weighted by the inverse 00832 // of the pt of the protojet 00833 // This is stored in pt{1,2}_weight 00834 tmp = j1.v; 00835 tmp.build_etaphi(); 00836 eta1 = tmp.eta; 00837 phi1 = tmp.phi; 00838 pt1_weight = (use_pt_weighted_splitting) ? 1.0/tmp.perp2() : 1.0; 00839 00840 tmp = j2.v; 00841 tmp.build_etaphi(); 00842 eta2 = tmp.eta; 00843 phi2 = tmp.phi; 00844 pt2_weight = (use_pt_weighted_splitting) ? 1.0/tmp.perp2() : 1.0; 00845 00846 jet1.v = jet2.v = Cmomentum(); 00847 00848 // compute jet splitting 00849 do{ 00850 if (j1.contents[i1]<j2.contents[i2]){ 00851 // particle i1 belong only to jet 1 00852 v = &(particles[j1.contents[i1]]); 00853 jet1.contents.push_back(j1.contents[i1]); 00854 jet1.v += *v; 00855 jet1.pt_tilde += pt[j1.contents[i1]]; 00856 i1++; 00857 jet1.range.add_particle(v->eta,v->phi); 00858 } else if (j1.contents[i1]>j2.contents[i2]){ 00859 // particle i2 belong only to jet 2 00860 v = &(particles[j2.contents[i2]]); 00861 jet2.contents.push_back(j2.contents[i2]); 00862 jet2.v += *v; 00863 jet2.pt_tilde += pt[j2.contents[i2]]; 00864 i2++; 00865 jet2.range.add_particle(v->eta,v->phi); 00866 } else { // (j1.contents[i1]==j2.contents[i2]) 00867 // common particle, decide which is the closest centre 00868 v = &(particles[j1.contents[i1]]); 00869 00870 // distance w.r.t. centroid 1 00871 dx1 = eta1 - v->eta; 00872 dy1 = fabs(phi1 - v->phi); 00873 if (dy1>M_PI) 00874 dy1 -= twopi; 00875 00876 // distance w.r.t. centroid 2 00877 dx2 = eta2 - v->eta; 00878 dy2 = fabs(phi2 - v->phi); 00879 if (dy2>M_PI) 00880 dy2 -= twopi; 00881 00882 //? what when == ? 00883 // When use_pt_weighted_splitting is activated, the 00884 // "geometrical" distance is weighted by the inverse 00885 // of the pt of the protojet 00886 double d1sq = (dx1*dx1+dy1*dy1)*pt1_weight; 00887 double d2sq = (dx2*dx2+dy2*dy2)*pt2_weight; 00888 // do bookkeeping on most ambiguous split 00889 if (fabs(d1sq-d2sq) < most_ambiguous_split) 00890 most_ambiguous_split = fabs(d1sq-d2sq); 00891 00892 if (d1sq<d2sq){ 00893 // particle i1 belong only to jet 1 00894 jet1.contents.push_back(j1.contents[i1]); 00895 jet1.v += *v; 00896 jet1.pt_tilde += pt[j1.contents[i1]]; 00897 jet1.range.add_particle(v->eta,v->phi); 00898 } else { 00899 // particle i2 belong only to jet 2 00900 jet2.contents.push_back(j2.contents[i2]); 00901 jet2.v += *v; 00902 jet2.pt_tilde += pt[j2.contents[i2]]; 00903 jet2.range.add_particle(v->eta,v->phi); 00904 } 00905 00906 i1++; 00907 i2++; 00908 } 00909 } while ((i1<j1.n) && (i2<j2.n)); 00910 00911 while (i1<j1.n){ 00912 v = &(particles[j1.contents[i1]]); 00913 jet1.contents.push_back(j1.contents[i1]); 00914 jet1.v += *v; 00915 jet1.pt_tilde += pt[j1.contents[i1]]; 00916 i1++; 00917 jet1.range.add_particle(v->eta,v->phi); 00918 } 00919 while (i2<j2.n){ 00920 v = &(particles[j2.contents[i2]]); 00921 jet2.contents.push_back(j2.contents[i2]); 00922 jet2.v += *v; 00923 jet2.pt_tilde += pt[j2.contents[i2]]; 00924 i2++; 00925 jet2.range.add_particle(v->eta,v->phi); 00926 } 00927 00928 // finalise jets 00929 jet1.n = jet1.contents.size(); 00930 jet2.n = jet2.contents.size(); 00931 00932 //jet1.range = j1.range; 00933 //jet2.range = j2.range; 00934 00935 // remove previous jets 00936 #ifdef ALLOW_MERGE_IDENTICAL_PROTOCONES 00937 cand_refs.erase(j1.v.ref); 00938 cand_refs.erase(j2.v.ref); 00939 #endif 00940 candidates->erase(it_j1); 00941 candidates->erase(it_j2); 00942 00943 // reinsert new ones 00944 insert(jet1); 00945 insert(jet2); 00946 00947 return true; 00948 } 00949 00950 // merge the two given jet. 00951 // during this procedure, the jets j1 & j2 are replaced 00952 // by 1 single jets containing both of them. 00953 // - it_j1 iterator of the first jet in 'candidates' 00954 // - it_j2 iterator of the second jet in 'candidates' 00955 // return true on success, false on error 00957 bool Csplit_merge::merge(cjet_iterator &it_j1, cjet_iterator &it_j2){ 00958 Cjet jet; 00959 int i; 00960 00961 // build new jet 00962 // note: particles within j1 & j2 have already been stored in indices 00963 for (i=0;i<idx_size;i++){ 00964 jet.contents.push_back(indices[i]); 00965 jet.v += particles[indices[i]]; 00966 jet.pt_tilde += pt[indices[i]]; 00967 } 00968 jet.n = jet.contents.size(); 00969 00970 // deal with ranges 00971 jet.range = range_union(it_j1->range, it_j2->range); 00972 00973 // remove old candidates 00974 #ifdef ALLOW_MERGE_IDENTICAL_PROTOCONES 00975 if (merge_identical_protocones){ 00976 cand_refs.erase(it_j1->v.ref); 00977 cand_refs.erase(it_j2->v.ref); 00978 } 00979 #endif 00980 candidates->erase(it_j1); 00981 candidates->erase(it_j2); 00982 00983 // reinsert new candidate 00984 insert(jet); 00985 00986 return true; 00987 } 00988 00994 bool Csplit_merge::insert(Cjet &jet){ 00995 00996 // eventually check that no other candidate are present with the 00997 // same cone contents. We recall that this automatic merging of 00998 // identical protocones can lead to infrared-unsafe situations. 00999 #ifdef ALLOW_MERGE_IDENTICAL_PROTOCONES 01000 if ((merge_identical_protocones) && (!cand_refs.insert(jet.v.ref).second)) 01001 return false; 01002 #endif 01003 01004 // check that the protojet has large enough pt 01005 if (jet.v.perp2()<pt_min2) 01006 return false; 01007 01008 // assign SM variable 01009 jet.sm_var2 = get_sm_var2(jet.v, jet.pt_tilde); 01010 01011 // insert the jet. 01012 candidates->insert(jet); 01013 01014 return true; 01015 } 01016 01023 double Csplit_merge::get_sm_var2(Cmomentum &v, double &pt_tilde){ 01024 switch(ptcomparison.split_merge_scale) { 01025 case SM_pt: return v.perp2(); 01026 case SM_mt: return v.perpmass2(); 01027 case SM_pttilde: return pt_tilde*pt_tilde; 01028 case SM_Et: return v.Et2(); 01029 default: 01030 throw Csiscone_error("Unsupported split-merge scale choice: " 01031 + ptcomparison.SM_scale_name()); 01032 } 01033 01034 return 0.0; 01035 } 01036 01037 }