// $Id: Recluster.cc 699 2014-07-07 09:58:12Z gsoyez $ // // Copyright (c) 2014-, Matteo Cacciari, Gavin P. Salam and Gregory Soyez // //---------------------------------------------------------------------- // This file is part of FastJet contrib. // // It 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. // // It 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 this code. If not, see . //---------------------------------------------------------------------- #include "Recluster.hh" #include #include #include using namespace std; // Comments: // // - If the jet comes from a C/A clustering (or is a composite jet // made of C/A clusterings) and we recluster it with a C/A // algorithm, we just use exclusive jets instead of doing the // clustering explicitly. In this specific case, we need to make // sure that all the pieces share the same cluster sequence. // // - If the recombiner has to be taken from the original jet and that // jet is composite, we need to check that all the pieces were // obtained with the same recombiner. // // TODO: // // - check this actually works!!! FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh namespace contrib{ LimitedWarning Recluster::_explicit_ghost_warning; // class description string Recluster::description() const { ostringstream ostr; ostr << "Recluster with subjet_def = "; if (_use_full_def) { ostr << _subjet_def.description(); } else { if (_subjet_alg == kt_algorithm) { ostr << "Longitudinally invariant kt algorithm with R = " << _subjet_radius; } else if (_subjet_alg == cambridge_algorithm) { ostr << "Longitudinally invariant Cambridge/Aachen algorithm with R = " << _subjet_radius; } else if (_subjet_alg == antikt_algorithm) { ostr << "Longitudinally invariant anti-kt algorithm with R = " << _subjet_radius; } else if (_subjet_alg == genkt_algorithm) { ostr << "Longitudinally invariant generalised kt algorithm with R = " << _subjet_radius << ", p = " << _subjet_extra; } else if (_subjet_alg == cambridge_for_passive_algorithm) { ostr << "Longitudinally invariant Cambridge/Aachen algorithm with R = " << _subjet_radius << " and a special hack whereby particles with kt < " << _subjet_extra << "are treated as passive ghosts"; } else if (_subjet_alg == ee_kt_algorithm) { ostr << "e+e- kt (Durham) algorithm"; } else if (_subjet_alg == ee_genkt_algorithm) { ostr << "e+e- generalised kt algorithm with R = " << _subjet_radius << ", p = " << _subjet_extra; } else if (_subjet_alg == undefined_jet_algorithm) { ostr << "uninitialised JetDefinition (jet_algorithm=undefined_jet_algorithm)" ; } else { ostr << "unrecognized jet_algorithm"; } ostr << ", a recombiner obtained from the jet being reclustered"; } if (_single) ostr << " and keeping the hardest subjet"; else ostr << " and joining all subjets in a composite jet"; return ostr.str(); } // return a vector of subjets, which are the ones that would be kept // by the filtering PseudoJet Recluster::result(const PseudoJet &jet) const { // generic sanity checks //------------------------------------------------------------------- // make sure that the jet has constituents if (! jet.has_constituents()) throw Error("Filter can only be applied on jets having constituents"); // tests particular to certain configurations //------------------------------------------------------------------- // for a whole variety of checks, we shall need the "recursive" // pieces of the jet (the jet itself or recursing down to its most // fundamental pieces). So we do compute these once and for all. // // Note that the pieces are always needed (either for C/A or for the // area checks) vector all_pieces; //.clear(); if ((!_get_all_pieces(jet, all_pieces)) || (all_pieces.size()==0)){ throw Error("Recluster: failed to retrieve all the pieces composing the jet."); } // decide which jet definition to use //------------------------------------------------------------------- JetDefinition subjet_def; if (_use_full_def){ subjet_def = _subjet_def; } else { _build_jet_def_with_recombiner(all_pieces, subjet_def); } // the vector that will ultimately hold the subjets vector subjets; // check if we can apply the simplification for C/A jets reclustered // with C/A // // we apply C/A clustering iff // - the request subjet_def is C/A // - the jet is either directly coming from C/A or if it is a // superposition of C/A jets from the same cluster sequence // - the pieces agree with the recombination scheme of subjet_def // // Note that in this case area support will be automatically // inherted so we can only worry about this later //------------------------------------------------------------------- if (_check_ca(all_pieces, subjet_def)){ _recluster_cafilt(all_pieces, subjets, subjet_def.R()); subjets = sorted_by_pt(subjets); return _single ? subjets[0] : join(subjets, *(subjet_def.recombiner())); } // decide if area support has to be kept //------------------------------------------------------------------- bool include_area_support = jet.has_area(); if ((include_area_support) && (!_check_explicit_ghosts(all_pieces))){ _explicit_ghost_warning.warn("Recluster: the original cluster sequence is lacking explicit ghosts; area support will no longer be available after re-clustering"); include_area_support = false; } // extract the subjets //------------------------------------------------------------------- _recluster_generic(jet, subjets, subjet_def, include_area_support); subjets = sorted_by_pt(subjets); return _single ? subjets[0] : join(subjets, *(subjet_def.recombiner())); } //---------------------------------------------------------------------- // the parts that really do the reclustering //---------------------------------------------------------------------- // get the subjets in the simple case of C/A+C/A void Recluster::_recluster_cafilt(const vector & all_pieces, vector & subjets, double Rfilt) const{ subjets.clear(); // each individual piece should have a C/A cluster sequence // associated to it. So a simple loop would do the job for (vector::const_iterator piece_it = all_pieces.begin(); piece_it!=all_pieces.end(); piece_it++){ // just extract the exclusive subjets of 'jet' const ClusterSequence *cs = piece_it->associated_cluster_sequence(); vector local_subjets; double dcut = Rfilt / cs->jet_def().R(); if (dcut>=1.0){ local_subjets.push_back(*piece_it); } else { local_subjets = piece_it->exclusive_subjets(dcut*dcut); } copy(local_subjets.begin(), local_subjets.end(), back_inserter(subjets)); } } // set the filtered elements in the generic re-clustering case (w/o // subtraction) void Recluster::_recluster_generic(const PseudoJet & jet, vector & subjets, const JetDefinition & subjet_def, bool do_areas) const{ // create a new, internal, ClusterSequence from the jet constituents // get the subjets directly from there // // If the jet has area support then we separate the ghosts from the // "regular" particles so the subjets will also have area // support. Note that we do this regardless of whether rho is zero // or not. // // Note that to be able to separate the ghosts, one needs explicit // ghosts!! // --------------------------------------------------------------- if (do_areas){ vector all_constituents = jet.constituents(); vector regular_constituents, ghosts; for (vector::iterator it = all_constituents.begin(); it != all_constituents.end(); it++){ if (it->is_pure_ghost()) ghosts.push_back(*it); else regular_constituents.push_back(*it); } // figure out the ghost area from the 1st ghost (if none, any value // would probably do as the area will be 0 and subtraction will have // no effect!) double ghost_area = (ghosts.size()) ? ghosts[0].area() : 0.01; ClusterSequenceActiveAreaExplicitGhosts * csa = new ClusterSequenceActiveAreaExplicitGhosts(regular_constituents, subjet_def, ghosts, ghost_area); subjets = csa->inclusive_jets(); // allow the cs to be deleted when it's no longer used // // Note that there is at least one constituent in the jet so there // is in principle at least one subjet But one may have used a // nasty recombiner that left an empty set of subjets, so we'd // rather play it safe if (subjets.size()) csa->delete_self_when_unused(); else delete csa; } else { ClusterSequence * cs = new ClusterSequence(jet.constituents(), subjet_def); subjets = cs->inclusive_jets(); // allow the cs to be deleted when it's no longer used (again, we // add an extra safety check) if (subjets.size()) cs->delete_self_when_unused(); else delete cs; } } //---------------------------------------------------------------------- // various checks and internal constructs //---------------------------------------------------------------------- // fundamental info for CompositeJets //---------------------------------------------------------------------- // get the pieces down to the fundamental pieces // // Note that this just checks that there is an associated CS to the // fundamental pieces, not that it is still valid bool Recluster::_get_all_pieces(const PseudoJet &jet, vector &all_pieces) const{ if (jet.has_associated_cluster_sequence()){ all_pieces.push_back(jet); return true; } if (jet.has_pieces()){ const vector pieces = jet.pieces(); for (vector::const_iterator it=pieces.begin(); it!=pieces.end(); it++) if (!_get_all_pieces(*it, all_pieces)) return false; return true; } return false; } // treatment of recombiners //---------------------------------------------------------------------- // get the common recombiner to all pieces (NULL if none) // // Note that if the jet has an associated cluster sequence that is no // longer valid, an error will be thrown (needed since it could be the // 1st check called after the enumeration of the pieces) const JetDefinition::Recombiner* Recluster::_get_common_recombiner(const vector &all_pieces) const{ const JetDefinition & jd_ref = all_pieces[0].validated_cs()->jet_def(); for (unsigned int i=1; ijet_def().has_same_recombiner(jd_ref)) return NULL; return jd_ref.recombiner(); } void Recluster::_build_jet_def_with_recombiner(const vector &all_pieces, JetDefinition &subjet_def) const{ // the recombiner has to be guessed from the pieces const JetDefinition::Recombiner * common_recombiner = _get_common_recombiner(all_pieces); if (common_recombiner) { if (typeid(*common_recombiner) == typeid(JetDefinition::DefaultRecombiner)) { RecombinationScheme scheme = static_cast(common_recombiner)->scheme(); if (_has_subjet_extra) subjet_def = JetDefinition(_subjet_alg, _subjet_radius, _subjet_extra, scheme); else if (_has_subjet_radius) subjet_def = JetDefinition(_subjet_alg, _subjet_radius, scheme); else subjet_def = JetDefinition(_subjet_alg, scheme); } else { if (_has_subjet_extra) subjet_def = JetDefinition(_subjet_alg, _subjet_radius, _subjet_extra, common_recombiner); else if (_has_subjet_radius) subjet_def = JetDefinition(_subjet_alg, _subjet_radius, common_recombiner); else subjet_def = JetDefinition(_subjet_alg, common_recombiner); } } else { throw Error("Recluster: requested to guess the recombination scheme (or recombiner) from the original jet but an inconsistency was found between the pieces constituing that jet."); } } // area support //---------------------------------------------------------------------- // check if the jet (or all its pieces) have explicit ghosts // (assuming the jet has area support). // // Note that if the jet has an associated cluster sequence that is no // longer valid, an error will be thrown (needed since it could be the // 1st check called after the enumeration of the pieces) bool Recluster::_check_explicit_ghosts(const vector &all_pieces) const{ for (vector::const_iterator it=all_pieces.begin(); it!=all_pieces.end(); it++) if (! it->validated_csab()->has_explicit_ghosts()) return false; return true; } // C/A specific tests //---------------------------------------------------------------------- // check if one can apply the simplification for C/A subjets // // This includes: // - the subjet definition asks for C/A subjets // - all the pieces share the same CS // - that CS is C/A with the same recombiner as the subjet def // - the filtering radius is not larger than any of the pairwise // distance between the pieces // // Note that if the jet has an associated cluster sequence that is no // longer valid, an error will be thrown (needed since it could be the // 1st check called after the enumeration of the pieces) bool Recluster::_check_ca(const vector &all_pieces, const JetDefinition &subjet_def) const{ if (subjet_def.jet_algorithm() != cambridge_algorithm) return false; // check that the 1st of all the pieces (we're sure there is at // least one) is coming from a C/A clustering. Then check that all // the following pieces share the same ClusterSequence const ClusterSequence * cs_ref = all_pieces[0].validated_cs(); if (cs_ref->jet_def().jet_algorithm() != cambridge_algorithm) return false; for (unsigned int i=1; ijet_def().has_same_recombiner(subjet_def)) return false; // we also have to make sure that the reclustering radius is not larger // than any of the inter-pieces distance double Rsub2 = subjet_def.R(); Rsub2 *= Rsub2; for (unsigned int i=0; i