// $Id: RecursiveSymmetryCutBase.cc 700 2014-07-07 12:50:05Z gsoyez $ // // Copyright (c) 2014-, Gavin P. Salam, Gregory Soyez, Jesse Thaler // //---------------------------------------------------------------------- // 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 "RecursiveSymmetryCutBase.hh" #include "fastjet/JetDefinition.hh" #include "fastjet/ClusterSequenceAreaBase.hh" #include #include using namespace std; FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh namespace contrib{ LimitedWarning RecursiveSymmetryCutBase::_negative_mass_warning; LimitedWarning RecursiveSymmetryCutBase::_mu2_gt1_warning; //LimitedWarning RecursiveSymmetryCutBase::_nonca_warning; LimitedWarning RecursiveSymmetryCutBase::_explicit_ghost_warning; bool RecursiveSymmetryCutBase::_verbose = false; //---------------------------------------------------------------------- PseudoJet RecursiveSymmetryCutBase::result(const PseudoJet & jet) const { // construct the input jet (by default, recluster with C/A) if (! jet.has_constituents()){ throw Error("RecursiveSymmetryCutBase can only be applied to jets with constituents"); } PseudoJet j = _do_reclustering ? _recluster ? (*_recluster)(jet) : Recluster(cambridge_algorithm, JetDefinition::max_allowable_R)(jet) : jet; // issue a warning if the jet is not obtained through a C/A // clustering // if ((! j.has_associated_cluster_sequence()) || // (j.validated_cs()->jet_def().jet_algorithm() != cambridge_algorithm)) // _nonca_warning.warn("RecursiveSymmetryCutBase is designed to be applied on jets from a Cambridge/Aachen clustering; use it with other algorithms at your own risk."); if (! j.has_valid_cluster_sequence()){ throw Error("RecursiveSymmetryCutBase can only be applied to jets associated to a (valid) cluster sequence"); } if (_subtractor) { const ClusterSequenceAreaBase * csab = dynamic_cast(j.associated_cs()); if (csab == 0 || (!csab->has_explicit_ghosts())) _explicit_ghost_warning.warn("RecursiveSymmetryCutBase: there is no clustering sequence, or it lacks explicit ghosts: subtraction is not guaranteed to function properly"); } // establish the first subjet and optionally subtract it PseudoJet subjet = j; if (_subtractor && (!_input_jet_is_subtracted)) { subjet = (*_subtractor)(subjet); } bool use_mu_cut = (_mu_cut != numeric_limits::infinity()); // variables for tracking what will happen PseudoJet piece1, piece2; // vectors for storing optional verbose structure // these hold the deltaR, symmetry, and mu values of dropped branches std::vector dropped_delta_R; std::vector dropped_symmetry; std::vector dropped_mu; // now recurse into the jet's structure while (subjet.has_parents(piece1, piece2)) { // first sanity check: // - zero or negative pts are not allowed for the input subjet // - zero or negative masses are not allowed for configurations // in which the mass will effectively appear in a denominator // (The masses will be checked later) if (subjet.pt2() <= 0) return PseudoJet(); if (_subtractor) { piece1 = (*_subtractor)(piece1); piece2 = (*_subtractor)(piece2); } // determine the symmetry parameter double sym; if (_symmetry_measure == y) { // the original d_{ij}/m^2 choice from MDT // first make sure the mass is sensible if (subjet.m2() <= 0) { _negative_mass_warning.warn("RecursiveSymmetryCutBase: cannot calculate y, because (sub)jet mass is negative; bailing out"); return _result_no_substructure(subjet); //TBC: do we return the hardest parent? A NULL PseudoJet? } sym = piece1.kt_distance(piece2) / subjet.m2(); } else if (_symmetry_measure == vector_z) { // min(pt1, pt2)/(pt), where the denominator is a vector sum // of the two subjets sym = min(piece1.pt(), piece2.pt()) / subjet.pt(); } else if (_symmetry_measure == scalar_z) { // min(pt1, pt2)/(pt1+pt2), where the denominator is a scalar sum // of the two subjets double pt1 = piece1.pt(); double pt2 = piece2.pt(); // make sure denominator is non-zero sym = pt1 + pt2; if (sym == 0) return PseudoJet(); sym = min(pt1, pt2) / sym; } else { throw Error ("Unrecognized choice of symmetry_measure"); } // determine the symmetry cut // (This function is specified in the derived classes) double this_symmetry_cut = symmetry_cut_fn(piece1, piece2); // and make a first tagging decision based on symmetry cut bool tagged = (sym > this_symmetry_cut); // if tagged based on symmetry cut, then check the mu cut (if relevant) // and update the tagging decision. Calculate mu^2 regardless, for cases // of users not cutting on mu2, but still interested in its value. double mu2 = max(piece1.m2(), piece2.m2())/subjet.m2(); if (tagged && use_mu_cut) { // first a sanity check -- mu2 won't be sensible if the subjet mass // is negative, so we can't then trust the mu cut - bail out if (subjet.m2() <= 0) { _negative_mass_warning.warn("RecursiveSymmetryCutBase: cannot trust mu, because (sub)jet mass is negative; bailing out"); return PseudoJet(); } if (mu2 > 1) _mu2_gt1_warning.warn("RecursiveSymmetryCutBase encountered mu^2 value > 1"); if (mu2 > pow(_mu_cut,2)) tagged = false; } // if we've tagged the splitting, return the jet with its substructure if (tagged) { // record relevant information StructureType * structure = new StructureType(subjet); structure->_symmetry = sym; structure->_mu = (mu2 >= 0) ? sqrt(mu2) : -sqrt(-mu2); structure->_delta_R = piece1.delta_R(piece2); if (_verbose_structure) { structure->_has_verbose = true; structure->_dropped_symmetry = dropped_symmetry; structure->_dropped_mu = dropped_mu; structure->_dropped_delta_R = dropped_delta_R; } subjet.set_structure_shared_ptr(SharedPtr(structure)); return subjet; } // if desired, store information about dropped branches before recursing if (_verbose_structure) { dropped_delta_R.push_back(piece1.delta_R(piece2)); dropped_symmetry.push_back(sym); dropped_mu.push_back((mu2 >= 0) ? sqrt(mu2) : -sqrt(-mu2)); } // otherwise continue unclustering, allowing for the different // ways of choosing which parent to look into int choice; if (_recursion_choice == larger_mt) { choice = piece1.mt2() > piece2.mt2() ? 1 : 2; } else if (_recursion_choice == larger_pt) { choice = piece1.pt2() > piece2.pt2() ? 1 : 2; } else if (_recursion_choice == larger_m) { choice = piece1.m2() > piece2.m2() ? 1 : 2; } else { throw Error ("Unrecognized value for recursion_choice"); } if (_verbose) cout << "choice is " << choice << endl;; subjet = (choice == 1) ? piece1 : piece2; } // (subjet.has_parents(...)) if (_verbose) cout << "reached end; returning null jet " << endl; // decide on tagging versus grooming mode here PseudoJet result = _result_no_substructure(subjet); if (result != 0) { // if in grooming mode, add dummy structure information StructureType * structure = new StructureType(result); structure->_symmetry = 0.0; structure->_mu = 0.0; structure->_delta_R = 0.0; if (_verbose_structure) { // still want to store verbose information about dropped branches structure->_has_verbose = true; structure->_dropped_symmetry = dropped_symmetry; structure->_dropped_mu = dropped_mu; structure->_dropped_delta_R = dropped_delta_R; } result.set_structure_shared_ptr(SharedPtr(structure)); } return result; } //---------------------------------------------------------------------- string RecursiveSymmetryCutBase::description() const { ostringstream ostr; ostr << "Recursive " << (_grooming_mode ? "Groomer" : "Tagger") << " with a symmetry cut "; switch(_symmetry_measure) { case y: ostr << "y"; break; case scalar_z: ostr << "scalar_z"; break; case vector_z: ostr << "vector_z"; break; default: cerr << "failed to interpret symmetry_measure" << endl; exit(-1); } ostr << " > " << symmetry_cut_description(); if (_mu_cut != numeric_limits::infinity()) { ostr << ", mass-drop cut mu=max(m1,m2)/m < " << _mu_cut; } else { ostr << ", no mass-drop requirement"; } ostr << ", recursion into the subjet with larger "; switch(_recursion_choice) { case larger_pt: ostr << "pt"; break; case larger_mt: ostr << "mt(=sqrt(m^2+pt^2))"; break; case larger_m: ostr << "mass"; break; default: cerr << "failed to interpret recursion_choice" << endl; exit(-1); } if (_subtractor) { ostr << " and subtractor: " << _subtractor->description(); if (_input_jet_is_subtracted) {ostr << " (input jet is assumed already subtracted)";} } return ostr.str(); } // decide what to return when no substructure has been found PseudoJet RecursiveSymmetryCutBase::_result_no_substructure(const PseudoJet &last_parent) const{ if (_grooming_mode){ // in grooming mode, return the last parent return last_parent; } else { // in tagging mode, return an empty PseudoJet return PseudoJet(); } } } // namespace contrib FASTJET_END_NAMESPACE