//STARTHEADER // $Id$ // // Copyright (c) 2005-2011, Matteo Cacciari, Gavin P. Salam and Gregory Soyez // //---------------------------------------------------------------------- // This file is part of FastJet. // // FastJet is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // The algorithms that underlie FastJet have required considerable // development and are described in hep-ph/0512210. If you use // FastJet as part of work towards a scientific publication, please // include a citation to the FastJet paper. // // FastJet is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with FastJet. If not, see . //---------------------------------------------------------------------- //ENDHEADER #include "fastjet/tools/JetMedianBackgroundEstimator.hh" #include #include #include FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh using namespace std; double BackgroundJetScalarPtDensity::result(const PseudoJet & jet) const { std::vector constituents = jet.constituents(); double scalar_pt = 0; for (unsigned i = 0; i < constituents.size(); i++) { scalar_pt += pow(constituents[i].perp(), _pt_power); } return scalar_pt / jet.area(); } //---------------------------------------------------------------------- double BackgroundRescalingYPolynomial::result(const PseudoJet & jet) const { double y = jet.rap(); double y2 = y*y; double rescaling = _a0 + _a1*y + _a2*y2 + _a3*y2*y + _a4*y2*y2; return rescaling; } /// allow for warnings LimitedWarning JetMedianBackgroundEstimator::_warnings; LimitedWarning JetMedianBackgroundEstimator::_warnings_zero_area; LimitedWarning JetMedianBackgroundEstimator::_warnings_preliminary; //--------------------------------------------------------------------- // class JetMedianBackgroundEstimator // Class to estimate the density of the background per unit area //--------------------------------------------------------------------- //---------------------------------------------------------------------- // ctors and dtors //---------------------------------------------------------------------- // ctor that allows to set only the particles later on JetMedianBackgroundEstimator::JetMedianBackgroundEstimator(const Selector &rho_range, const JetDefinition &jet_def, const AreaDefinition &area_def) : _rho_range(rho_range), _jet_def(jet_def), _area_def(area_def) { // initialise things decently reset(); // make a few checks _check_jet_alg_good_for_median(); } //---------------------------------------------------------------------- // ctor from a cluster sequence // - csa the ClusterSequenceArea to use // - rho_range the Selector specifying which jets will be considered JetMedianBackgroundEstimator::JetMedianBackgroundEstimator( const Selector &rho_range, const ClusterSequenceAreaBase &csa) : _rho_range(rho_range), _jet_def(JetDefinition()){ // initialise things properly reset(); // tell the BGE about the cluster sequence set_cluster_sequence(csa); } //---------------------------------------------------------------------- // setting a new event //---------------------------------------------------------------------- // tell the background estimator that it has a new event, composed // of the specified particles. void JetMedianBackgroundEstimator::set_particles(const vector & particles) { // make sure that we have been provided a genuine jet definition if (_jet_def.jet_algorithm() == undefined_jet_algorithm) throw Error("JetMedianBackgroundEstimator::set_particles can only be called if you set the jet (and area) definition explicitly through the class constructor"); // initialise things decently (including setting uptodate to false!) //reset(); _uptodate=false; // cluster the particles // // One may argue that it is better to cache the particles and only // do the clustering later but clustering the particles now has 2 // practical advantages: // - it allows to une only '_included_jets' in all that follows // - it avoids adding another flag to ensure particles are // clustered only once ClusterSequenceArea *csa = new ClusterSequenceArea(particles, _jet_def, _area_def); _included_jets = csa->inclusive_jets(); // store the CS for later on _csi = csa->structure_shared_ptr(); csa->delete_self_when_unused(); } //---------------------------------------------------------------------- // (re)set the cluster sequence (with area support) to be used by // future calls to rho() etc. // // \param csa the cluster sequence area // // Pre-conditions: // - one should be able to estimate the "empty area" (i.e. the area // not occupied by jets). This is feasible if at least one of the following // conditions is satisfied: // ( i) the ClusterSequence has explicit ghosts // (ii) the range selected has a computable area. // - the jet algorithm must be suited for median computation // (otherwise a warning will be issues) // // Note that selectors with e.g. hardest-jets exclusion do not have // a well-defined area. For this reasons, it is STRONGLY advised to // use an area with explicit ghosts. void JetMedianBackgroundEstimator::set_cluster_sequence(const ClusterSequenceAreaBase & csa) { _csi = csa.structure_shared_ptr(); // sanity checks //--------------- // (i) check the alg is appropriate _check_jet_alg_good_for_median(); // (ii) check that, if there are no explicit ghosts, the selector has a finite area if ((!csa.has_explicit_ghosts()) && (!_rho_range.has_finite_area())){ throw Error("JetMedianBackgroundEstimator: either an area with explicit ghosts (recommended) or a Selector with finite area is needed (to allow for the computation of the empty area)"); } // get the initial list of jets _included_jets = csa.inclusive_jets(); _uptodate = false; } //---------------------------------------------------------------------- // (re)set the jets (which must have area support) to be used by future // calls to rho() etc.; for the conditions that must be satisfied // by the jets, see the Constructor that takes jets. void JetMedianBackgroundEstimator::set_jets(const vector &jets) { if (! jets.size()) throw Error("JetMedianBackgroundEstimator::JetMedianBackgroundEstimator: At least one jet is needed to compute the background properties"); // sanity checks //--------------- // (o) check that there is an underlying CS shared by all the jets if (! (jets[0].has_associated_cluster_sequence()) && (jets[0].has_area())) throw Error("JetMedianBackgroundEstimator::JetMedianBackgroundEstimator: the jets used to estimate the background properties must be associated with a valid ClusterSequenceAreaBase"); _csi = jets[0].structure_shared_ptr(); ClusterSequenceStructure * csi = dynamic_cast(_csi()); const ClusterSequenceAreaBase * csab = csi->validated_csab(); for (unsigned int i=1;ihas_explicit_ghosts()) && (!_rho_range.has_finite_area())){ throw Error("JetMedianBackgroundEstimator: either an area with explicit ghosts (recommended) or a Selector with finite area is needed (to allow for the computation of the empty area)"); } // get the initial list of jets _included_jets = jets; // ensure recalculation of quantities that need it _uptodate = false; } //---------------------------------------------------------------------- // retrieving fundamental information //---------------------------------------------------------------- // get rho, the median background density per unit area double JetMedianBackgroundEstimator::rho() const { if (_rho_range.takes_reference()) throw Error("The background estimation is obtained from a selector that takes a reference jet. rho(PseudoJet) should be used in that case"); _recompute_if_needed(); return _rho; } // get sigma, the background fluctuations per unit area double JetMedianBackgroundEstimator::sigma() const { if (_rho_range.takes_reference()) throw Error("The background estimation is obtained from a selector that takes a reference jet. rho(PseudoJet) should be used in that case"); _recompute_if_needed(); return _sigma; } // get rho, the median background density per unit area, locally at // the position of a given jet. // // If the Selector associated with the range takes a reference jet // (i.e. is relocatable), then for subsequent operations the // Selector has that jet set as its reference. double JetMedianBackgroundEstimator::rho(const PseudoJet & jet) { _recompute_if_needed(jet); double our_rho = _rho; if (_rescaling_class != 0) { our_rho *= (*_rescaling_class)(jet); } return our_rho; } // get sigma, the background fluctuations per unit area, // locally at the position of a given jet. // // If the Selector associated with the range takes a reference jet // (i.e. is relocatable), then for subsequent operations the // Selector has that jet set as its reference. double JetMedianBackgroundEstimator::sigma(const PseudoJet &jet) { _recompute_if_needed(jet); double our_sigma = _sigma; if (_rescaling_class != 0) { our_sigma *= (*_rescaling_class)(jet); } return our_sigma; } //---------------------------------------------------------------------- // configuring behaviour //---------------------------------------------------------------------- // reset to default values // // set the variou options to their default values void JetMedianBackgroundEstimator::reset(){ // set the remaining default parameters set_use_area_4vector(); // true by default set_provide_fj2_sigma(false); // reset the computed values _rho = _sigma = 0.0; _n_jets_used = _n_empty_jets = 0; _empty_area = _mean_area = 0.0; _included_jets.clear(); _jet_density_class = 0; // null pointer _rescaling_class = 0; // null pointer _uptodate = false; } // Set a pointer to a class that calculates the quantity whose // median will be calculated; if the pointer is null then pt/area // is used (as occurs also if this function is not called). void JetMedianBackgroundEstimator::set_jet_density_class(const FunctionOfPseudoJet * jet_density_class_in) { _warnings_preliminary.warn("JetMedianBackgroundEstimator::set_jet_density_class: density classes are still preliminary in FastJet 3.0. Their interface may differ in future releases (without guaranteeing backward compatibility)."); _jet_density_class = jet_density_class_in; _uptodate = false; } //---------------------------------------------------------------------- // description //---------------------------------------------------------------------- string JetMedianBackgroundEstimator::description() const { ostringstream desc; desc << "JetMedianBackgroundEstimator, using " << _jet_def.description() << " with " << _area_def.description() << " and selecting jets with " << _rho_range.description(); return desc.str(); } //---------------------------------------------------------------------- // computation of the background properties //---------------------------------------------------------------------- // for estimation using a relocatable selector (i.e. local range) // this allows to set its position. Note that this HAS to be called // before any attempt to compute the background properties void JetMedianBackgroundEstimator::_recompute_if_needed(const PseudoJet &jet){ // if the range is relocatable, handles its relocation if (_rho_range.takes_reference()){ // check that the reference is not the same as the previous one // (would avoid an unnecessary recomputation) if (jet == _current_reference) return; // relocate the range and make sure things get recomputed the next // time one tries to get some information _rho_range.set_reference(jet); _uptodate=false; } _recompute_if_needed(); } // do the actual job void JetMedianBackgroundEstimator::_compute() const { // check if the clustersequence is still valid _check_csa_alive(); // fill the vector of pt/area (or the quantity from the jet density class) // - in the range vector vector_for_median; double total_area = 0.0; _n_jets_used = 0; // apply the selector to the included jets vector selected_jets = _rho_range(_included_jets); // compute the pt/area for the selected jets for (unsigned i = 0; i < selected_jets.size(); i++) { const PseudoJet & current_jet = selected_jets[i]; double this_area = (_use_area_4vector) ? current_jet.area_4vector().perp() : current_jet.area(); if (this_area>0){ double median_input; if (_jet_density_class == 0) { median_input = current_jet.perp()/this_area; } else { median_input = (*_jet_density_class)(current_jet); } if (_rescaling_class != 0) { median_input /= (*_rescaling_class)(current_jet); } vector_for_median.push_back(median_input); total_area += this_area; _n_jets_used++; } else { _warnings_zero_area.warn("JetMedianBackgroundEstimator::_compute(...): 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)."); } } // there is nothing inside our region, so answer will always be zero if (vector_for_median.size() == 0) { _rho = 0.0; _sigma = 0.0; _mean_area = 0.0; return; } // determine the number of empty jets const ClusterSequenceAreaBase * csab = (dynamic_cast(_csi()))->validated_csab(); if (csab->has_explicit_ghosts()) { _empty_area = 0.0; _n_empty_jets = 0; } else { _empty_area = csab->empty_area(_rho_range); _n_empty_jets = csab->n_empty_jets(_rho_range); } double total_njets = _n_jets_used + _n_empty_jets; total_area += _empty_area; double stand_dev; _median_and_stddev(vector_for_median, _n_empty_jets, _rho, stand_dev, _provide_fj2_sigma); // process and store the results (_rho was already stored above) _mean_area = total_area / total_njets; _sigma = stand_dev * sqrt(_mean_area); // record that the computation has been performed _uptodate = true; } // check that the underlying structure is still alive; // throw an error otherwise void JetMedianBackgroundEstimator::_check_csa_alive() const{ ClusterSequenceStructure* csa = dynamic_cast(_csi()); if (csa == 0) { throw Error("JetMedianBackgroundEstimator: there is no cluster sequence associated with the JetMedianBackgroundEstimator"); } if (! dynamic_cast(_csi())->has_associated_cluster_sequence()) throw Error("JetMedianBackgroundEstimator: modifications are no longer possible as the underlying ClusterSequence has gone out of scope"); } // check that the algorithm used for the clustering is suitable for // background estimation (i.e. either kt or C/A). // Issue a warning otherwise void JetMedianBackgroundEstimator::_check_jet_alg_good_for_median() const{ const JetDefinition * jet_def = &_jet_def; // if no explicit jet def has been provided, fall back on the // cluster sequence if (_jet_def.jet_algorithm() == undefined_jet_algorithm){ const ClusterSequence * cs = dynamic_cast(_csi())->validated_cs(); jet_def = &(cs->jet_def()); } if (jet_def->jet_algorithm() != kt_algorithm && jet_def->jet_algorithm() != cambridge_algorithm && jet_def->jet_algorithm() != cambridge_for_passive_algorithm) { _warnings.warn("JetMedianBackgroundEstimator: jet_def being used may not be suitable for estimating diffuse backgrounds (good alternatives are kt, cam)"); } } FASTJET_END_NAMESPACE