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