SISCone  2.0.5
siscone/spherical/split_merge.cpp
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 }
The SISCone project has been developed by Gavin Salam and Gregory Soyez
Documentation generated on Mon Jun 4 2012 18:23:38 for SISCone by  Doxygen 1.7.6.1