Filter.cc

00001 //STARTHEADER
00002 // $Id: Filter.cc 1737 2010-07-08 16:19:56Z soyez $
00003 //
00004 // Copyright (c) 2009-2010, Matteo Cacciari, Gavin Salam and Gregory Soyez
00005 //
00006 //----------------------------------------------------------------------
00007 // This file is part of FastJet.
00008 //
00009 //  FastJet 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 //  The algorithms that underlie FastJet have required considerable
00015 //  development and are described in hep-ph/0512210. If you use
00016 //  FastJet as part of work towards a scientific publication, please
00017 //  include a citation to the FastJet paper.
00018 //
00019 //  FastJet is distributed in the hope that it will be useful,
00020 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00021 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00022 //  GNU General Public License for more details.
00023 //
00024 //  You should have received a copy of the GNU General Public License
00025 //  along with FastJet; if not, write to the Free Software
00026 //  Foundation, Inc.:
00027 //      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00028 //----------------------------------------------------------------------
00029 //ENDHEADER
00030 
00031 #include "fastjet/tools/Filter.hh"
00032 #include <fastjet/ClusterSequenceActiveAreaExplicitGhosts.hh>
00033 #include <cassert>
00034 #include <algorithm>
00035 #include <sstream>
00036 
00037 using namespace std;
00038 
00039 
00040 FASTJET_BEGIN_NAMESPACE      // defined in fastjet/internal/base.hh
00041 
00042 //----------------------------------------------------------------------
00043 // Filter class implementation
00044 //----------------------------------------------------------------------
00045 
00046 
00047 // class description
00048 string Filter::description() const {
00049   ostringstream ostr;
00050   ostr << "Filter with subjet_def = ";
00051   ostr << _subjet_def.description();
00052   ostr<< ", and selection " << _selector.description();
00053   return ostr.str();
00054 }
00055 
00056 
00057 // return a vector of subjets, which are the ones that would be kept by the filtering
00058 PseudoJet Filter::operator()(const PseudoJet &jet) const {
00059   ClusterSequence *internal_cs;
00060 
00061   // start by getting the list of subjets (including a sanity check
00062   // that all the jets in the argument vector share the same
00063   // underlying ClusterSequence)
00064   vector<PseudoJet> subjets; // NB: empty to begin with (see the comment for _set_filtered_elements_cafilt)
00065   _set_filtered_elements(jet, subjets, internal_cs);
00066 
00067   // decide what to keep and what to reject
00068   // we first make a copy of the pointers then apply the selector
00069   vector<const PseudoJet*> subjet_pointers;
00070   for (unsigned int i=0;i<subjets.size(); i++)
00071     subjet_pointers.push_back(&(subjets[i]));
00072 
00073   // Note that the following line is the one requiring that _seelector be declared as mutable
00074   if (_selector.takes_reference()) _selector.set_reference(jet);
00075   _selector.nullify_non_selected(subjet_pointers);
00076 
00077   // now build the vector of kept and rejected subjets
00078   vector<PseudoJet> kept, rejected;
00079   for (unsigned int i=0;i<subjets.size(); i++){
00080     if (subjet_pointers[i]==0)
00081       rejected.push_back(subjets[i]);
00082     else
00083       kept.push_back(subjets[i]);
00084   }
00085 
00086   // gather the info under the form of a PseudoJet
00087   return _finalise(jet, kept, rejected, internal_cs);
00088 }
00089 
00090 
00091 // sets filtered_elements to be all the subjets on which filtering will work
00092 void Filter::_set_filtered_elements(const PseudoJet & jet,
00093                                     vector<PseudoJet> & filtered_elements,
00094                                     ClusterSequence * &internal_cs) const {
00095   // sanity checks
00096   //-------------------------------------------------------------------
00097   // make sure that the jet has constituents
00098   if (! jet.has_constituents())
00099     throw Error("Filter can only be applied on jets having constituents");
00100   
00101   // if rho!=0, make sure we have a CS that supports area and has explicit ghosts
00102   // watch out: that will fail for a MergedJet!!x
00103   if (_rho != 0.0){
00104     if (!jet.has_area())   
00105       throw Error("Attempt to filter and subtract (non-zero rho) without area info for the original jet");
00106 
00107     if (!jet.has_associated_cluster_sequence())
00108       throw Error("Attempt to filter and subtract (non-zero rho) without a cluster sequence associated with the jet");
00109 
00110     // note theat the validated_csab() used in the next line will
00111     // automatically throw an error if there is no valis CSAB so we
00112     // just have to check for the explicit ghosts
00113     if (!jet.validated_csab()->has_explicit_ghosts())
00114       throw Error("Attempt to filter and subtract (non-zero rho) without explicit ghosts");
00115   }
00116 
00117 
00118   // get the jet definition to be use and whether we can apply our simplified C/A+C/A filter
00119   //
00120   // we apply C/A clustering iff
00121   //  - the request subjet_def is C/A
00122   //  - the jet is either directly coming from C/A or if it is a superposition of C/A jets
00123   //  - the pieces agree with the recombination scheme of subjet_def
00124   //-------------------------------------------------------------------
00125   bool simple_cafilt = (_subjet_def.jet_algorithm() == cambridge_algorithm) && (_recursively_check_ca(jet));
00126  
00127   // extract the subjets
00128   //-------------------------------------------------------------------
00129   if (simple_cafilt){
00130     _set_filtered_elements_cafilt(jet, filtered_elements, _subjet_def.R());
00131     internal_cs = NULL;
00132   } else {
00133     if (_rho != 0.0){
00134       internal_cs = _set_filtered_elements_generic_subtracted(jet, filtered_elements);
00135     } else {
00136       internal_cs = _set_filtered_elements_generic_unsubtracted(jet, filtered_elements);
00137     }
00138   }
00139 
00140   // order the filtered elements in pt
00141   filtered_elements = sorted_by_pt(filtered_elements);
00142 }
00143 
00144 
00145 // gather the information about what is kept and rejected under the
00146 // form of a PseudoJet with a special ClusterSequenceInfo
00147 PseudoJet Filter::_finalise(const PseudoJet & jet, vector<PseudoJet> & kept, vector<PseudoJet> & rejected, ClusterSequence * &internal_cs) const {
00148   PseudoJet filtered_jet(0.0,0.0,0.0,0.0);
00149 
00150   // create an appropriate structure and transfer the info to it
00151   FilteredJetStructure *fi = new FilteredJetStructure();
00152 
00153   fi->_original_jet = jet;
00154   fi->_pieces = kept;   // in the base interface
00155   fi->_rejected = rejected;
00156 
00157   if (internal_cs)
00158     fi->_internal_cs.reset(internal_cs);
00159 
00160   // to sum the kept pieces, extract the recombiner used for the sub-clustering
00161   const JetDefinition::Recombiner &rec = *(_subjet_def.recombiner());
00162   for (unsigned i = 0; i < kept.size(); i++)
00163     rec.plus_equal(filtered_jet, kept[i]);
00164 
00165   // make sure the filtered jet has the same index (cluster and user)
00166   // (i.e. "looks like") the original jet
00167   // what about extra info? 
00168   // TODO: do we want this: in principle, this could interfere with teh recombiner??
00169   filtered_jet.set_cluster_hist_index(jet.cluster_hist_index());
00170   filtered_jet.set_user_index(jet.user_index());
00171 
00172   // finally attach the clustering info to the PJ
00173   filtered_jet.set_structure_shared_ptr(SharedPtr<PseudoJetStructureBase>(fi));
00174 
00175   return filtered_jet;
00176 }
00177 
00178 
00179 // check if the jet is obtained from C/A or a superposition of C/A pieces
00180 bool Filter::_recursively_check_ca(const PseudoJet & jet) const{
00181   if (jet.has_associated_cluster_sequence()) 
00182     return jet.associated_cluster_sequence()->jet_def().jet_algorithm() == cambridge_algorithm;
00183 
00184   if (jet.has_pieces()){
00185     const vector<PseudoJet> pieces = jet.pieces();
00186     for (vector<PseudoJet>::const_iterator it=pieces.begin(); it!=pieces.end(); it++)
00187       if (!_recursively_check_ca(*it)) return false;
00188     return true;
00189   }
00190 
00191   return false;
00192 }
00193 
00194 
00195 
00196 // set the filtered elements in the simple case of C/A+C/A
00197 //
00198 // WATCH OUT: this could be recursively called, so filtered elements
00199 // of 'jet' are APPENDED to 'filtered_elements'
00200 void Filter::_set_filtered_elements_cafilt(const PseudoJet & jet, vector<PseudoJet> & filtered_elements, double Rfilt) const{
00201   // we know that the jet is either a C/A jet or a superposition of such pieces
00202   if (jet.has_associated_cluster_sequence()){
00203     // just extract the exclusive subjets of 'jet'
00204     const ClusterSequence *cs = jet.associated_cluster_sequence(); 
00205     vector<PseudoJet> local_fe;
00206 
00207     double dcut = Rfilt / cs->jet_def().R();
00208     if (dcut>=1.0){
00209       local_fe.push_back(jet);
00210     } else {
00211       dcut *= dcut;
00212       local_fe = jet.exclusive_subjets(dcut);
00213     }
00214 
00215     // subtract the jets if needed
00216     // Note that this one would work on pieces!!
00217     //-----------------------------------------------------------------
00218     if (_rho != 0.0){
00219       const ClusterSequenceAreaBase * csab = jet.validated_csab();
00220       for (unsigned int i=0;i<local_fe.size();i++)
00221         local_fe[i] = csab->subtracted_jet(local_fe[i], _rho);
00222     }
00223 
00224     copy(local_fe.begin(), local_fe.end(), back_inserter(filtered_elements));
00225     return;
00226   }
00227 
00228   // just recurse into the pieces
00229   const vector<PseudoJet> & pieces = jet.pieces();
00230   for (vector<PseudoJet>::const_iterator it = pieces.begin(); it!=pieces.end(); it++)
00231     _set_filtered_elements_cafilt(*it, filtered_elements, Rfilt);
00232 }
00233 
00234 
00235 // set the filtered elements in the generic re-clustering case (wo subtraction)
00236 ClusterSequence * Filter::_set_filtered_elements_generic_unsubtracted(const PseudoJet & jet, 
00237                                                                       vector<PseudoJet> & filtered_elements) const{
00238   // create a new, internal, ClusterSequence from the jet constituents
00239   // get the subjets directly from there
00240   //---------------------------------------------------------------
00241   ClusterSequence * cs = new ClusterSequence(jet.constituents(), _subjet_def);
00242   filtered_elements = cs->inclusive_jets();
00243 
00244   return cs;
00245 }
00246 
00247 // set the filtered elements in the generic re-clustering case (with subtraction)
00248 ClusterSequence * Filter::_set_filtered_elements_generic_subtracted(const PseudoJet & jet, 
00249                                                                     vector<PseudoJet> & filtered_elements) const{
00250   // create a new, internal, ClusterSequence from jet constituents
00251   // 
00252   // the difference is that we need to separate the ghosts to get a
00253   // reliable area computation
00254   // ---------------------------------------------------------------
00255   vector<PseudoJet> all_constituents = jet.constituents();
00256   vector<PseudoJet> regular_constituents, ghosts;  
00257 
00258   for (vector<PseudoJet>::iterator it = all_constituents.begin(); 
00259        it != all_constituents.end(); it++){
00260     if (it->is_pure_ghost())
00261       ghosts.push_back(*it);
00262     else
00263       regular_constituents.push_back(*it);
00264   }
00265 
00266   // figure the ghost area from the 1st ghost (if none, any value
00267   // would probably do as the area will be 0 and subtraction will have
00268   // no effect!)
00269   double ghost_area = (ghosts.size()) ? ghosts[0].area() : 0.01;
00270   ClusterSequenceActiveAreaExplicitGhosts * csa
00271     = new ClusterSequenceActiveAreaExplicitGhosts(regular_constituents, _subjet_def, 
00272                                                   ghosts, ghost_area);
00273       
00274   // get the subjets
00275   filtered_elements = csa->subtracted_jets(_rho);
00276 
00277   return csa;
00278 }
00279 
00280 
00281 
00282 //----------------------------------------------------------------------
00283 // FilterInterface implementation 
00284 //----------------------------------------------------------------------
00285 
00286 
00287 FASTJET_END_NAMESPACE      // defined in fastjet/internal/base.hh