SISCone  2.0.5
siscone/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 // 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 }
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