// $Id$
//
// Copyright (c) 2013,
// Andrew Larkoski, Simone Marzani, Gregory Soyez, and 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 "SoftDrop.hh"
#include
#include
#include
FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
namespace contrib{
using namespace std;
LimitedWarning SoftDropTagger::_warnings_recluster;
LimitedWarning SoftDropTagger::_warnings_deprecated;
//----------------------------------------------------------------------
// SoftDropTagger class implementation
//----------------------------------------------------------------------
//------------------------------------------------------------------------
// description of the tagger
string SoftDropTagger::description() const{
ostringstream oss;
oss << "SoftDropTagger with beta=" << _beta
<< ", zcut=" << _zcut
<< "and mu=" << _mu;
return oss.str();
}
//------------------------------------------------------------------------
// returns the tagged PseudoJet if successful, 0 otherwise
// - jet the PseudoJet to tag
PseudoJet SoftDropTagger::result(const PseudoJet & jet) const{
//--------------------------------------------------------------
// check that the jet has constituents
if (!jet.has_constituents()){
throw Error("SoftDropTagger can only be applied to jet with constituents");
return PseudoJet();
}
//--------------------------------------------------------------
// get the original cliustering radius R0
if (!jet.has_associated_cluster_sequence()){
throw Error("SoftDropTagger can only be applied on jets resulting from a previous clustering [i.e. jets with an associated cluster sequence].");
return PseudoJet();
}
double R0 = jet.associated_cs()->jet_def().R();
//--------------------------------------------------------------
// Recluster the jet with the requested jet alg (CA by default)
PseudoJet j = _recluster(jet);
//--------------------------------------------------------------
// now do the declustering until the 2 conditions (soft-drop and
// mass-drop) are met.
PseudoJet j1, j2;
bool had_parents;
double pt1=0.0, pt2=0.0, theta12_squared=0.0;
std::vector z_drop_values;
// store dummy z_drop value so that we can always access this information
z_drop_values.push_back(0.0);
while ((had_parents = j.has_parents(j1,j2))) {
// pre-compute the transverse momenta
pt1 = j1.pt();
pt2 = j2.pt();
// and the angle (including the normalisation)
theta12_squared = j1.squared_distance(j2)/(R0*R0);
// Check mass if desired
bool passMassDrop = (_mu == 1.0 || (max(j1.m2(), j2.m2()) < _mu*_mu*j.m2()));
// check the 2 conditions
if ( (min(pt1,pt2) > (pt1+pt2)*_zcut*pow(theta12_squared,0.5*_beta)) && passMassDrop) {
// conditions satisfied: exit the loop
break;
} else {
// store z_drop value
z_drop_values.push_back(min(pt1,pt2)/(pt1+pt2));
// recurse into the subjet with the largest transverse momentum
j = (j1.pt2() < j2.pt2()) ? j2 : j1;
}
}
// create the result and its structure
PseudoJet result_local = j;
SoftDropTaggerStructure * s = new SoftDropTaggerStructure(result_local);
if (!had_parents){
// no substructure found, return the "leading" particle
// [Note that this behaviours differs from the MassDropTagger. In
// this regard, the SoftDropTagger behaves more like a groomer
// than a tagger]
s->_mu = 0.0;
s->_soft_drop = 0.0;
s->_z = 0.0;
s->_Rg = 0.0;
s->_z_drop_values = z_drop_values; // still want to know how much we dropped
} else {
s->_mu = (j.m2()!=0.0) ? sqrt(j1.m2()/j.m2()) : 0.0;
s->_soft_drop = min(pt1,pt2)/(pt1+pt2)*pow(theta12_squared,-0.5*_beta);
s->_z = min(pt1,pt2)/(pt1+pt2);
s->_Rg = j1.delta_R(j2);
s->_z_drop_values = z_drop_values;
}
result_local.set_structure_shared_ptr(SharedPtr(s));
return result_local;
}
//------------------------------------------------------------------------
// rcluster the jet with the Cambridge/Aachen algorithm (or any
// generalized kt algorithm)
// Note that we assume that the jet has constituents and an associated
// cluster sequence
PseudoJet SoftDropTagger::_recluster(const PseudoJet & jet) const{
// Before everything else, if the jet results from a C/A clustering,
// (and we have the exponent p = 0.0) we do not need to do anything!
const JetDefinition & original_jet_def = jet.associated_cs()->jet_def();
if (original_jet_def.jet_algorithm() == cambridge_algorithm && _p == 0.0)
return jet;
// in all other cases, recluster the constituents.
//
// Prepare the cluster sequence and the jet definition.
// If the input jet has been obtained with a user-defined
// recombiner, use it also for the re-clustering.
ClusterSequence *cs;
JetDefinition jet_def(genkt_algorithm,
JetDefinition::max_allowable_R,
_p);
jet_def.set_recombiner(original_jet_def.recombiner());
// get the jet constituents
vector constituents = jet.constituents();
if (constituents.size() == 0) return PseudoJet();
// if we have explicit ghosts, do the clustering keeping area info
if ((jet.has_area()) &&
(jet.validated_csab()->has_explicit_ghosts())){
// we have area readily available from explicit ghosts; split the
// constituents into ghosts and regular particles and user the
// former to keep the area information in the new clustering
std::vector particles, ghosts;
SelectorIsPureGhost().sift(constituents, ghosts, particles);
// get the ghost area (if there is no ghost in the jet, any number
// would do here!)
double ghost_area = ghosts.size() ? ghosts[0].area() : 0.01;
// do the clustering using the particles and ghosts
cs = new ClusterSequenceActiveAreaExplicitGhosts(particles, jet_def,
ghosts, ghost_area);
} else { // no area info or area without explicit ghosts
// use a regular clustering without area info
cs = new ClusterSequence(constituents, jet_def);
}
// keep clustering info available
vector new_jets = cs->inclusive_jets();
// print a warning if we get more than one jet
if (new_jets.size()!=1){
_warnings_recluster.warn("SoftDropTagger::_recluster: more than one jet obtained after re-clustering. Keeping the hardest");
new_jets = sorted_by_pt(new_jets);
}
// make sure we keep the re-clustering sequence alive
cs->delete_self_when_unused();
return new_jets[0];
}
void SoftDropTagger::_warn_deprecated() const{
_warnings_deprecated.warn("SoftDropTagger is deprecated. Use the SoftDrop class in the RecursiveTools contrib instead.");
}
} // namespace contrib
FASTJET_END_NAMESPACE