BackgroundEstimator.cc

00001 // design questions?
00002 //
00003 //  - keep the option to define things with just a Selector and a list of particles
00004 //    HOWTO:
00005 //     . ClusterSequenceArea:
00006 //        o impose an area for the Selector
00007 //        o if not relocatable, take ghosts maxrap from there
00008 //          otherwise, get it from the maxrap of the particles
00009 //          warn in the doc that one should set a decent maxrap
00010 //        o use kt, R=0.6
00011 //        o active_area_explicit_ghosts
00012 //        o have functions that allow to control the maxrap, the ghost area, the alg, R
00013 //
00014 //     . have a SharedPtr<ClusterSequenceArea> initialised only in that case
00015 //       remove the const CSA & _csa;
00016 //     . access the cs using 
00017 //         dynamic_cast<ClusterSequenceArea*>(_csw()->cs());
00018 //       
00019 //
00020 //  - use a default Selector: SelectorStrip(2.0) * !SelectorNHardest(2)
00021 
00022 #include "fastjet/tools/BackgroundEstimator.hh"
00023 #include <fastjet/ClusterSequenceAreaBase.hh>
00024 #include <fastjet/ClusterSequenceStructure.hh>
00025 #include <iostream>
00026 
00027 FASTJET_BEGIN_NAMESPACE     // defined in fastjet/internal/base.hh
00028 
00029 using namespace std;
00030 
00031 
00032 /// allow for warnings
00033 LimitedWarning BackgroundEstimator::_warnings;
00034 LimitedWarning BackgroundEstimator::_warnings_zero_area;
00035 
00036 
00037 //---------------------------------------------------------------------
00038 // class BackgroundEstimator
00039 // Class to estimate the density of the background per unit area
00040 //---------------------------------------------------------------------
00041 
00042 // default ctor
00043 //  - csa        the ClusterSequenceArea to use
00044 //  - rho_range  the range over which jets will be considered
00045 BackgroundEstimator::BackgroundEstimator(const ClusterSequenceAreaBase &csa, const Selector &rho_range)
00046   : _rho_range(rho_range){
00047 
00048   _csi = csa.structure_shared_ptr();
00049 
00050   // sanity checks
00051   //---------------
00052   //  (i) check the alg is appropriate
00053   _check_jet_alg_good_for_median();
00054 
00055   //  (ii) check that, if there is no explicit ghosts, the selector has an area
00056   if ((!csa.has_explicit_ghosts()) && (!_rho_range.has_area())){
00057     throw Error("BackgroundEstimator: either an area with explicit ghosts (recommended) or a Selector with finite area is needed (to allow for the computation of the empty area)");
00058   }
00059 
00060   // get the initial list of jets
00061   _included_jets = csa.inclusive_jets();
00062 
00063   // initialise things properly
00064   reset();
00065 }
00066 
00067 
00068 // ctor from a list of jets
00069 //  - jets        the set of jets to use for the computation
00070 //  - rho_range   the range over which jets will be considered
00071 BackgroundEstimator::BackgroundEstimator(const vector<PseudoJet> &jets, const Selector &rho_range)
00072   : _rho_range(rho_range){
00073 
00074   if (! jets.size())
00075     throw Error("BackgroundEstimator::BackgroundEstimator: At least one jet is needed to compute the background properties");
00076 
00077   // sanity checks
00078   //---------------
00079   //  (o) check that there is an underlying CS shared by all the jets
00080   if (! (jets[0].has_associated_cluster_sequence()) && (jets[0].has_area()))
00081     throw Error("BackgroundEstimator::BackgroundEstimator: the jets used to estimate the background properties must be associated with a valid ClusterSequenceAreaBase");
00082 
00083   _csi = jets[0].structure_shared_ptr();
00084   ClusterSequenceStructure * csi = dynamic_cast<ClusterSequenceStructure*>(_csi());
00085   const ClusterSequenceAreaBase * csab = csi->validated_csab();
00086 
00087   for (unsigned int i=1;i<jets.size(); i++){
00088     if (! jets[i].has_associated_cluster_sequence()) // area automatic if the next test succeeds
00089       throw Error("BackgroundEstimator::BackgroundEstimator: the jets used to estimate the background properties must be associated with a valid ClusterSequenceAreaBase");
00090 
00091     if (jets[i].structure_shared_ptr().get() != _csi.get())
00092       throw Error("BackgroundEstimator::BackgroundEstimator: all the jets used to estimate the background properties must share the same ClusterSequence");
00093   }
00094 
00095   //  (i) check the alg is appropriate
00096   _check_jet_alg_good_for_median();
00097 
00098   //  (ii) check that, if there is no explicit ghosts, the selector has an area
00099   if ((!csab->has_explicit_ghosts()) && (!_rho_range.has_area())){
00100     throw Error("BackgroundEstimator: either an area with explicit ghosts (recommended) or a Selector with finite area is needed (to allow for the computation of the empty area)");
00101   }
00102 
00103 
00104   // get the initial list of jets
00105   _included_jets = jets;
00106 
00107   // initialise things properly
00108   reset();
00109 }
00110 
00111 
00112 // default dtor
00113 BackgroundEstimator::~BackgroundEstimator(){
00114 
00115 }
00116 
00117 
00118 // for estimation using a relocatable selector (i.e. local range)
00119 // this allows to set its position. Note that this HAS to be called
00120 // before any attempt to compute the background properties
00121 BackgroundEstimator & BackgroundEstimator::set_reference(const PseudoJet &jet){
00122   // if the range is norrelocatable, do nothing
00123   if (_rho_range.takes_reference()){
00124     // relocate the range and make sure things get recomputed the next
00125     // time one tries to get some information
00126     _rho_range.set_reference(jet);
00127     _uptodate=false;
00128   }
00129 
00130   return *this;
00131 }
00132 
00133 // reset to default values
00134 // 
00135 // set the variou options to their default values
00136 void BackgroundEstimator::reset(){
00137   // set the remaining default parameters
00138   set_use_area_4vector();  // true by default
00139 
00140   // reset the computed values
00141   _rho = _sigma = 0.0;
00142   _n_jets_used = _n_empty_jets = 0;
00143   _empty_area = _mean_area = 0.0;
00144 
00145   _uptodate = false;
00146 }
00147 
00148 
00149 // do the actual job
00150 void BackgroundEstimator::_compute(){
00151   // check if the clustersequence is still valid
00152   _check_csa_alive();
00153 
00154   // fill the vector of pt/area with the jets 
00155   //  - in included_jets
00156   //  - not in excluded_jets
00157   //  - in the range
00158   vector<double> pt_over_areas;
00159   double total_area  = 0.0;
00160   
00161   _n_jets_used = 0;
00162 
00163   // apply the selector to the included jets
00164   _selected_jets = _rho_range(_included_jets);
00165 
00166   // compute the pt/area for the selected jets
00167   for (unsigned i = 0; i < _selected_jets.size(); i++) {
00168     const PseudoJet & current_jet = _selected_jets[i];
00169 
00170     double this_area = (_use_area_4vector) ? current_jet.area_4vector().perp() : current_jet.area(); 
00171 
00172     if (this_area>0){
00173       pt_over_areas.push_back(current_jet.perp()/this_area);
00174       total_area  += this_area;
00175       _n_jets_used++;
00176     } else {
00177       _warnings_zero_area.warn("BackgroundEstimator::get_median_rho_and_sigma(...): discarded jet with zero area. Zero-area jets may be due to (i) too large a ghost area (ii) a jet being outside the ghost range (iii) the computation not being done using an appropriate algorithm (kt;C/A).");
00178     }
00179       
00180   }
00181   
00182   // there is nothing inside our region, so answer will always be zero
00183   if (pt_over_areas.size() == 0) {
00184     _rho        = 0.0;
00185     _sigma      = 0.0;
00186     _mean_area  = 0.0;
00187     return;
00188   }
00189 
00190   // get median (pt/area) [this is the "old" median definition. It considers
00191   // only the "real" jets in calculating the median, i.e. excluding the
00192   // only-ghost ones; it will be supplemented with more info below]
00193   sort(pt_over_areas.begin(), pt_over_areas.end());
00194 
00195   // determine the number of empty jets
00196   _empty_area = 0.0;
00197   _n_empty_jets = 0.0;
00198   const ClusterSequenceAreaBase * csab = dynamic_cast<ClusterSequenceStructure*>(_csi())->validated_csab();
00199 
00200   if (csab->has_explicit_ghosts()) {
00201     _empty_area = 0.0;
00202     _n_empty_jets = 0;
00203   } else {
00204     // note that we are sure that the selector has an area
00205     _empty_area = _rho_range.area() - total_area;
00206     if (_empty_area<0) _empty_area = 0;
00207     _n_empty_jets = _empty_area / (0.55*pi*csab->jet_def().R());
00208   }
00209 
00210   double total_njets = _n_jets_used + _n_empty_jets;
00211   total_area  += _empty_area;
00212 
00213   // now get the median & error, accounting for empty jets
00214   // define the fractions of distribution at median, median-1sigma
00215   double posn[2] = {0.5, (1.0-0.6827)/2.0};
00216   double res[2];
00217 
00218   for (int i = 0; i < 2; i++) {
00219     double nj_median_pos = (total_njets-1)*posn[i] - _n_empty_jets;
00220     double nj_median_ratio;
00221     if (nj_median_pos >= 0 && pt_over_areas.size() > 1) {
00222       int int_nj_median = int(nj_median_pos);
00223       nj_median_ratio =
00224         pt_over_areas[int_nj_median] * (int_nj_median+1-nj_median_pos)
00225         + pt_over_areas[int_nj_median+1] * (nj_median_pos - int_nj_median);
00226     } else {
00227       nj_median_ratio = 0.0;
00228     }
00229     res[i] = nj_median_ratio;
00230   }
00231 
00232   // store the results
00233   double error  = res[0] - res[1];
00234   _rho        = res[0];
00235   _mean_area  = total_area / total_njets;
00236   _sigma      = error * sqrt(_mean_area);
00237 
00238   // record that the computation has been performed  
00239   _uptodate = true;
00240 }
00241 
00242 
00243 // check that the underlying structure is still alive
00244 // throw an error otherwise
00245 void BackgroundEstimator::_check_csa_alive(){
00246   if (! dynamic_cast<ClusterSequenceStructure*>(_csi())->has_associated_cluster_sequence())
00247     throw Error("BackgroundEstimator: modifications are no longer possible as the underlying ClusterSequence has gone out of scope");
00248 }
00249 
00250 
00251 // check that the algorithm used for the clustering is adapted for
00252 // background estimation (i.e. either kt or C/A)
00253 // Issue a warning otherwise
00254 void BackgroundEstimator::_check_jet_alg_good_for_median(){
00255   const ClusterSequence * cs = dynamic_cast<ClusterSequenceStructure*>(_csi())->validated_cs();
00256 
00257   if (cs->jet_def().jet_algorithm() != kt_algorithm
00258       && cs->jet_def().jet_algorithm() != cambridge_algorithm
00259       && cs->jet_def().jet_algorithm() != cambridge_for_passive_algorithm) {
00260     _warnings.warn("BackgroundEstimator: jet_def being used may not be suitable for estimating diffuse backgrounds (good options are kt, cam)");
00261   }
00262 }
00263 
00264 FASTJET_END_NAMESPACE
00265 
00266