// JetsWithoutJets Package // Questions/Comments? danbert@mit.edu jthaler@mit.edu // // Copyright (c) 2013 // Daniele Bertolini and Jesse Thaler // // $Id: $ //---------------------------------------------------------------------- // 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 . //---------------------------------------------------------------------- #ifndef __FASTJET_CONTRIB_JETSWITHOUTJETS_HH__ #define __FASTJET_CONTRIB_JETSWITHOUTJETS_HH__ #include #include "fastjet/FunctionOfPseudoJet.hh" #include "fastjet/tools/Transformer.hh" #include "fastjet/JetDefinition.hh" #include "fastjet/PseudoJet.hh" #include "fastjet/ClusterSequence.hh" #include "fastjet/SharedPtr.hh" #include #include #include FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh namespace contrib { ////// // // Defining a function of a vector of PseudoJets // ////// //---------------------------------------------------------------------- // MyFunctionOfVectorOfPseudoJets // In current version of FastJet there is no standard interface for a function // that takes a vector of PseudoJets as argument. This class serves as a base class // and it is the analog of FunctionOfPseudoJet, it only differs in taking a vector // of PseudoJets as argument. Should a standard interface become available, this // class could be removed and the classes below (like JetLikeEventShape, // FunctorJetCount, etc.) could derive from the standard base class. template class MyFunctionOfVectorOfPseudoJets { public: MyFunctionOfVectorOfPseudoJets(){} MyFunctionOfVectorOfPseudoJets(const TOut &constant_value); virtual ~MyFunctionOfVectorOfPseudoJets(){} virtual std::string description() const{ return "";} virtual TOut result(const std::vector &pjs) const = 0; TOut operator()(const std::vector &pjs) const { return result(pjs);} }; //---------------------------------------------------------------------- // The following are example Functors to Use // //-GPS: is there any reason to call these Functors? A Functor has a // quite specific mathematical meaning, and not all aspects are // relevant here, e.g. "associates to each morphism // f:X\rightarrow Y \in C a morphism F(f):F(X) \rightarrow F(Y) // \in D " //---------------------------------------------------------------------- ////// // // Example Functors to Use // ////// //---------------------------------------------------------------------- // FunctorJetCount // Counts the number of jets (i.e. 1 per jet) //-GPS: how does this count particles? It always returns 1. This seems a bit too trivial... class FunctorJetCount : public MyFunctionOfVectorOfPseudoJets { double result(const std::vector & particles) const { return 1.0; } std::string description() const { return "Jet Count";} }; //---------------------------------------------------------------------- // FunctorJetScalarPt // Finds the summed pt of the jet constituents (NOT the pT of the jet) //-GPS: should this be called ScalarPtSum? It doesn't necessarily involve // any jets at all, but it does do a sum. class FunctorJetScalarPt : public MyFunctionOfVectorOfPseudoJets { double result(const std::vector & particles) const { double myPt = 0.0; for (unsigned int i = 0; i { private: int _n; public: FunctorJetScalarPtToN(int n) : _n(n) {} double result(const std::vector & particles) const { FunctorJetScalarPt sumPt = FunctorJetScalarPt(); return pow(sumPt(particles),_n); } std::string description() const { std::stringstream myStream; myStream << "Jet Scalar Pt^" << _n; return myStream.str(); } }; //---------------------------------------------------------------------- // FunctorJetMass // Finds the mass of the jet //-GPS: similar comment as for FunctorJetScalarPt class FunctorJetMass : public MyFunctionOfVectorOfPseudoJets { double result(const std::vector & particles) const { PseudoJet myJet(0,0,0,0); for (unsigned int i = 0; i { double result(const std::vector & particles) const { PseudoJet myJet(0,0,0,0); for (unsigned int i = 0; i > > _regionStorage; // bool for whether region needs to be considered. std::vector > _aboveCutBool; double _rapmax; int _maxRapIndex; double _rapSpread; int _maxPhiIndex; double _phiSpread; // helper functions to determine which region a pseudo jet is in int getRapIndex(const fastjet::PseudoJet & currentJet) const; int getPhiIndex(const fastjet::PseudoJet & currentJet) const; public: JWJStorageArray() : _Rjet(0.0) { // at the moment, this value is hard coded _rapmax = 10.0; } // make the storage array void establishStorage(const std::vector & my_jets, double Rjet, double ptcut); // give the 2R x 2R region relevant for a given particle std::vector & getStorageFor(const fastjet::PseudoJet & currentJet); // give whether the 2R x 2R region has enough pT to merit further examination bool aboveCutFor(const fastjet::PseudoJet & currentJet); }; ////// // // Extendable Jet-like Event Shapes // (works on any function of vector that returns double) // ////// //---------------------------------------------------------------------- // JetLikeEventShape // Generic JetLikeEventShape which takes a Functor (derived from // MyFunctionOfVectorOfPseudoJets) as an argument and then sums over all // "jets" (i.e. regions of radius R around each particle) in an event. // Optional trimming built in. Note that trimming first, then applying the event // shape gives a slightly different answer. // Only works with quantities that are doubles for each jet // (could templatize, but easier to deal with case by case) //-GPS: why is this "JetLikeEventShape"? The result isn't a jet. You // mean an object that acts like an event shape as applied to jets? class JetLikeEventShape : public MyFunctionOfVectorOfPseudoJets { protected: MyFunctionOfVectorOfPseudoJets * _measurement; //jet measurement to use double _Rjet, _ptcut, _Rsub, _fcut; bool _trim; //-GPS if at all possible, it's good to avoid mutables for temporary storage like //-GPS this, because it makes it impossible to run the code in parallel on //-GPS two separate threads. You may not think you need to worry about that now, //-GPS but it can create debugging nightmares later. // used for temporary storage of information when establishing weights mutable bool _includeParticle; mutable double _weightParticle, _pt_in_Rjet, _pt_in_Rsub; mutable std::vector _nearbyParticles; // establishes the weights for each jet region to avoid double-counting. void establishWeights(const PseudoJet myParticle, const std::vector & particles) const; // optional storage array to cache the 2R x 2R partitions. bool _useStorageArray; mutable JWJStorageArray _myStorageArray; void establishStorage(const std::vector & particles) const; public: JetLikeEventShape(): _measurement(NULL), _useStorageArray(true), _myStorageArray() {} // constructor without trimming //-GPS: comments here are a bit thin! In particular, it's useful to mention that measurement // will be automatically deleted when the class is destroyed. JetLikeEventShape(MyFunctionOfVectorOfPseudoJets * measurement, double Rjet, double ptcut) : _measurement(measurement), _Rjet(Rjet), _ptcut(ptcut), _Rsub(Rjet), _fcut(1.0), _trim(false), _useStorageArray(true), _myStorageArray() {} // constructor with trimming JetLikeEventShape(MyFunctionOfVectorOfPseudoJets * measurement, double Rjet, double ptcut, double Rsub, double fcut) : _measurement(measurement), _Rjet(Rjet), _ptcut(ptcut), _Rsub(Rsub), _fcut(fcut), _trim(true), _useStorageArray(true), _myStorageArray() {} virtual ~JetLikeEventShape(){ if(_measurement) delete _measurement;} // the event shape value virtual double result(const std::vector & particles) const; // Default is to use storage array. Can use this to turn off. void setUseStorageArray(bool useStorageArray) {_useStorageArray = useStorageArray;} std::string jetParameterString() const { std::stringstream stream; stream << "R_jet=" << _Rjet << ", pT_cut=" << _ptcut; if (_trim) stream << ", trimming with R_sub=" << _Rsub <<", fcut=" << _fcut; return stream.str(); } virtual std::string description() const { return "Summed " + _measurement->description() + " as event shape, " + jetParameterString(); } }; ////// // // Built-In Jet-like Event Shapes // ////// //---------------------------------------------------------------------- // ShapeJetMultiplicity // Defines event shape jet counting. class ShapeJetMultiplicity: public JetLikeEventShape { public: ShapeJetMultiplicity(double Rjet, double ptcut): JetLikeEventShape(new FunctorJetCount(),Rjet,ptcut) {} ShapeJetMultiplicity(double Rjet, double ptcut, double Rsub, double fcut) : JetLikeEventShape(new FunctorJetCount(),Rjet,ptcut,Rsub,fcut) {} virtual ~ShapeJetMultiplicity(){} }; //---------------------------------------------------------------------- // ShapeScalarPt // Defines event shape scalar pT sum. class ShapeScalarPt: public JetLikeEventShape { public: ShapeScalarPt(double Rjet, double ptcut): JetLikeEventShape(new FunctorJetScalarPt(),Rjet,ptcut) {} ShapeScalarPt(double Rjet, double ptcut, double Rsub, double fcut) : JetLikeEventShape(new FunctorJetScalarPt(),Rjet,ptcut,Rsub,fcut) {} virtual ~ShapeScalarPt(){} }; //---------------------------------------------------------------------- // ShapeScalarPtToN // Sum of pT^n of jets class ShapeScalarPtToN: public JetLikeEventShape { public: ShapeScalarPtToN(double n, double Rjet, double ptcut): JetLikeEventShape(new FunctorJetScalarPtToN(n),Rjet,ptcut) {} ShapeScalarPtToN(double n, double Rjet, double ptcut, double Rsub, double fcut) : JetLikeEventShape(new FunctorJetScalarPtToN(n),Rjet,ptcut,Rsub,fcut) {} virtual ~ShapeScalarPtToN(){} }; //---------------------------------------------------------------------- // ShapeSummedMass // Sum over jet masses class ShapeSummedMass: public JetLikeEventShape { public: ShapeSummedMass(double Rjet, double ptcut): JetLikeEventShape(new FunctorJetMass(),Rjet,ptcut) {} ShapeSummedMass(double Rjet, double ptcut, double Rsub, double fcut) : JetLikeEventShape(new FunctorJetMass(),Rjet,ptcut,Rsub,fcut) {} virtual ~ShapeSummedMass(){} }; //---------------------------------------------------------------------- // ShapeSummedMassSquared // Sum over jet mass^2 class ShapeSummedMassSquared: public JetLikeEventShape { public: ShapeSummedMassSquared(double Rjet, double ptcut): JetLikeEventShape(new FunctorJetMassSquared(),Rjet,ptcut) {} ShapeSummedMassSquared(double Rjet, double ptcut, double Rsub, double fcut) : JetLikeEventShape(new FunctorJetMassSquared(),Rjet,ptcut,Rsub,fcut) {} virtual ~ShapeSummedMassSquared(){} }; ////// // // More complicated Jet-like Event Shapes that need special coding // ////// //---------------------------------------------------------------------- // ShapeMissingPt // ShapeMissingPt is pT of the vector sum of jets momenta. // Needs special coding because it requires measuring the magnitude of the // transverse momentum vector class ShapeMissingPt : public JetLikeEventShape { public: ShapeMissingPt(double Rjet, double ptcut): JetLikeEventShape(NULL,Rjet,ptcut) {} ShapeMissingPt(double Rjet, double ptcut, double Rsub, double fcut) : JetLikeEventShape(NULL,Rjet,ptcut,Rsub,fcut) {} virtual ~ShapeMissingPt(){} double result(const std::vector & particles) const; std::string description() const; }; //---------------------------------------------------------------------- // ShapeTrimmedSubjetMultiplicity // A counter for subjets within trimmed fat jets. Requires special coding // to avoid too many repeated calculations class ShapeTrimmedSubjetMultiplicity: public JetLikeEventShape { private: double _ptsubcut; // additional subjet pT cut, if desired public: ShapeTrimmedSubjetMultiplicity(double Rjet, double ptcut, double Rsub, double fcut, double ptsubcut = 0.0) : JetLikeEventShape(NULL,Rjet,ptcut,Rsub,fcut), _ptsubcut(ptsubcut) {} virtual ~ShapeTrimmedSubjetMultiplicity(){} double result(const std::vector & particles) const; std::string description() const; }; ////// // // Shape Trimming // ////// //---------------------------------------------------------------------- // SelectorShapeTrimming // Event-wide trimming is implemented as a selector. Selector SelectorShapeTrimming(double Rjet, double ptcut, double Rsub, double fcut); //---------------------------------------------------------------------- // SelectorJetShapeTrimming // Jet-based trimming, implimented as a selector. // It takes the jet pT as the sum of the given four-vectors Selector SelectorJetShapeTrimming(double Rsub, double fcut); //---------------------------------------------------------------------- // JetShapeTrimmer // For convenience, a Transformer version of jet-shape trimming which // acts on individual jets. Simply a wrapper for SelectorJetShapeTrimming class JetShapeTrimmer : public Transformer { private: double _Rsub, _fcut; Selector _selector; public: JetShapeTrimmer(double Rsub, double fcut) : _Rsub(Rsub), _fcut(fcut){ _selector = SelectorJetShapeTrimming(_Rsub,_fcut); } virtual PseudoJet result(const PseudoJet & original) const { return join(_selector(original.constituents())); } std::string jetParameterString() const { std::stringstream stream; stream << "R_sub=" << _Rsub <<", fcut=" << _fcut; return stream.str(); } virtual std::string description() const { return "Jet shape trimmer, " + jetParameterString(); } }; ////// // // Variable pT_cut // ////// //---------------------------------------------------------------------- // JetLikeEventShape_VariablePtCut // Generic class to get the whole range of event shape values as ptcut // is changed. Currently, this does not use the JWJStorageArray for // speed up, but that can be added upon request. class JetLikeEventShape_VariablePtCut { protected: // The jet measurement of interest MyFunctionOfVectorOfPseudoJets * _measurement; double _Rjet, _Rsub, _fcut, _offset; bool _trim; // Build up the event shape as a function of ptcut. void _buildStepFunction(const std::vector particles) const; // temporary storage of information mutable std::vector _ptR_values, _eventShape_values; mutable std::vector _nearbyParticles; mutable double _pt_in_Rjet, _pt_in_Rsub; public: JetLikeEventShape_VariablePtCut(): _measurement(NULL) {} // constructor without trimming JetLikeEventShape_VariablePtCut(MyFunctionOfVectorOfPseudoJets * measurement, double Rjet, double offset = 0.0): _measurement(measurement), _Rjet(Rjet), _Rsub(Rjet), _fcut(1.0), _offset(offset), _trim(false) {} // constructor with trimming JetLikeEventShape_VariablePtCut(MyFunctionOfVectorOfPseudoJets * measurement, double Rjet, double Rsub, double fcut, double offset = 0.0): _measurement(measurement), _Rjet(Rjet), _Rsub(Rsub), _fcut(fcut), _offset(offset), _trim(true) {} virtual ~JetLikeEventShape_VariablePtCut() {if(_measurement) delete _measurement;} // Initialization: input particles and build step function. void set_input(const std::vector & particles) const {_buildStepFunction(particles);} // Get the event shape for any value of ptcut. virtual double eventShapeFor(const double ptcut_0) const; // Pseudo-inverse: get ptcut for a given event shape value. If initialized it uses an offset, default offset is zero. virtual double ptCutFor(const double eventShape_0) const; // Get the full arrays. virtual std::vector ptCutArray() const {return _ptR_values;} virtual std::vector eventShapeArray() const {return _eventShape_values;} std::string ParameterString() const { std::stringstream stream; stream << "R_jet=" << _Rjet; if (_trim) stream << ", trimming with R_sub=" << _Rsub <<", fcut=" << _fcut; stream << ", offset for inverse function=" << _offset; return stream.str(); } virtual std::string description() const { return _measurement->description() + "as function of pT_cut, " + ParameterString(); } }; //---------------------------------------------------------------------- // ShapeJetMultiplicity_VariablePtCut // Example of an event shape with a variable ptcut. Here, we are just doing // jet multiplicity, but the same could be done for any of the other event shapes class ShapeJetMultiplicity_VariablePtCut: public JetLikeEventShape_VariablePtCut { public: // As per the physics paper, the default offset is 0.5 ShapeJetMultiplicity_VariablePtCut(double Rjet, double offset = 0.5): JetLikeEventShape_VariablePtCut(new FunctorJetCount(), Rjet, offset) {}; ShapeJetMultiplicity_VariablePtCut(double Rjet, double Rsub, double fcut, double offset = 0.5): JetLikeEventShape_VariablePtCut(new FunctorJetCount(), Rjet, Rsub, fcut, offset) {}; virtual ~ShapeJetMultiplicity_VariablePtCut(){} }; ////// // // Njet with variable R_jet // ////// //---------------------------------------------------------------------- // ShapeJetMultiplicity_VariableR // This is the only event shape we have coded up with the ability to do // a variable R. Currently, this does not use the JWJStorageArray for // speed up, but that can be added upon request. // //-GPS it's not clear from this what the class really does - is it //-Variable R in the sense of the Variable R paper from a couple of //-years ago, or is it variable R in the sense of being able to handle //-multiple R values, any R value? // //-You should also signal that this uses N^2 memory (16 N^2 bytes as //-far as I can tell), because that can become problematic - e.g. if //-someone in HI tries this, with 30k particles, they'll get 14GB usage. class ShapeJetMultiplicity_VariableR { protected: double _ptcut, _Rsub, _fcut; bool _trim; void _buildStepFunction(const std::vector particles) const; mutable std::vector _dR_values, _eventShape_values; mutable std::vector _nearbyParticles; mutable double _pt_in_Rjet; public: ShapeJetMultiplicity_VariableR() {} // constructor without trimming ShapeJetMultiplicity_VariableR(double ptcut): _ptcut(ptcut), _Rsub(0.0), _fcut(1.0), _trim(false) {} // constructor with trimming ShapeJetMultiplicity_VariableR(double ptcut, double Rsub, double fcut): _ptcut(ptcut), _Rsub(Rsub), _fcut(fcut), _trim(true) {} virtual ~ShapeJetMultiplicity_VariableR() {} // Initialization: input particles and build step function. void set_input(const std::vector & particles) const {_buildStepFunction(particles);} // Get the event shape for any value of R. virtual double eventShapeFor(const double Rjet_0) const; // Get the full arrays. // //-GPS: these are both huge arrays (N^2); it's much better to return //-a constant reference to the internal array, so that time isn't //-wasted copying them. Also, do they really need to be virtual? virtual std::vector RArray() const {return _dR_values;} virtual std::vector eventShapeArray() const {return _eventShape_values;} std::string ParameterString() const { std::stringstream stream; stream << "pT_cut=" << _ptcut; if (_trim) stream << ", trimming with R_sub=" << _Rsub <<", fcut=" << _fcut; return stream.str(); } virtual std::string description() const { return "Jet multiplicity as function of Rjet, " + ParameterString(); } }; ////// // // Finding Jet Axes with the Event Shape Density // ////// //-------------------------------------------------------- // FunctorJetAxis // Functor to find jet axis according to a given jet definition. Needed // for the event shape densities class FunctorJetAxis : public MyFunctionOfVectorOfPseudoJets< PseudoJet > { public: FunctorJetAxis(){} FunctorJetAxis(fastjet::JetDefinition &jetDef) : _jetDef(jetDef) {} ~FunctorJetAxis(){} PseudoJet result(const std::vector & particles) const { fastjet::ClusterSequence clustSeq(particles,_jetDef); fastjet::PseudoJet myAxis = clustSeq.inclusive_jets(0.0)[0]; // should cluster everything return(myAxis); } std::string description() const { return "Jet axis with " + _jetDef.description();} private: fastjet::JetDefinition _jetDef; }; //-------------------------------------------------------- // FunctorLightLikeVersion // Takes a pseudojet and returns a light-like version with no mass // energy set to 1, but maintaining the input rapidity and azimuth //-GPS: above name doesn't correspond to the actual class name! //-GPS: it's not entirely clear what this class does from the name. //-GPS: It's actually doing two things: making it light-like and giving it // a unit pt. Should it really be LightLikeUnitPtVersion? class FunctorLightLikeVersion : public FunctionOfPseudoJet< PseudoJet > { public: FunctorLightLikeVersion() {} // Convert to light-like object PseudoJet result(const PseudoJet & jet) const { PseudoJet p; p.reset_PtYPhiM(1.0, jet.rap(), jet.phi(),0.0); //-GPS: why take only user index? It might be better to // say p = jet and then use reset_momentum_PtYPhiM(...) p.set_user_index(jet.user_index()); // carry user index infomation return p; } //-GPS: this might also be more explicit about the unit pt std::string description() const { return "Light-like version";} }; //-------------------------------------------------------- // WinnerTakeAllRecombiner // Winner-take-all recombiner. Returns a recombined pseudojet whose pt // is the sum of the input pts, but direction is the harder jet direction. class WinnerTakeAllRecombiner : public fastjet::JetDefinition::Recombiner { public: WinnerTakeAllRecombiner() {} virtual std::string description() const { return "WinnerTakeAll Recombination Scheme"; } /// recombine pa and pb and put result into pab virtual void recombine(const PseudoJet & pa, const PseudoJet & pb, PseudoJet & pab) const { PseudoJet harder = pa; PseudoJet softer = pb; //-GPS: pt costs a sqrt in FJ. Better to cache the pt's, since they'll be reused below if (harder.pt() < softer.pt()) std::swap(harder,softer); PseudoJet direction = lightLikeVersion(harder); double newpT = harder.pt() + softer.pt(); pab = newpT * direction; } protected: FunctorLightLikeVersion lightLikeVersion; }; //-------------------------------------------------------- // EventShapeDensity_JetAxes // This is the hybrid probability density shape for jet axes position // It calculates axis directions using the winner-take-all recombination, // and also returns weights. class EventShapeDensity_JetAxes { public: EventShapeDensity_JetAxes(){} // using default cluster measure (anti-kT with winner-take-all recombination) EventShapeDensity_JetAxes(double Rjet, double ptcut, bool applyGlobalConsistency = false): _Rjet(Rjet), _ptcut(ptcut), _jetDef(fastjet::JetDefinition(fastjet::antikt_algorithm, 2.0*Rjet, new WinnerTakeAllRecombiner(), fastjet::Best)), _applyGlobalConsistency(applyGlobalConsistency), _useStorageArray(true) { _jetDef.delete_recombiner_when_unused(); } // Using arbitrary jet clustering procedure EventShapeDensity_JetAxes(double Rjet, double ptcut, const fastjet::JetAlgorithm &jetAlgo, bool applyGlobalConsistency = false): _Rjet(Rjet), _ptcut(ptcut), _jetDef(fastjet::JetDefinition(jetAlgo, 2.0*Rjet, new WinnerTakeAllRecombiner(), fastjet::Best)), _applyGlobalConsistency(applyGlobalConsistency), _useStorageArray(true) { _jetDef.delete_recombiner_when_unused();} ~EventShapeDensity_JetAxes(){} // Set input and find local axes void set_input(const std::vector & particles) const { _find_local_axes(particles); find_axes_and_weights(); } //Turn on/off global consistency requirement void setGlobalConsistencyCheck(bool applyGlobalConsistency) {_applyGlobalConsistency = applyGlobalConsistency;} //(re)find axes and weights void find_axes_and_weights() const; //Return a vector of PseudoJets sorted by pT. pT of each axis is its corresponding pT weight. std::vector axes() const { return _distinctAxes; } //Return the corresponding vector of Njet weights std::vector Njet_weights() const { return _tot_Njet_weights; } void setUseStorageArray(bool useStorageArray) {_useStorageArray = useStorageArray;} //Description std::string ParameterString() const { std::stringstream stream; stream << "R_jet=" << _Rjet << ", pT_cut=" << _ptcut; stream << ". Local clustering with " << _jetDef.description()<<"."; if (_applyGlobalConsistency) stream << " Global consistency condition on."; return stream.str(); } std::string description() const { return "Hybrid event shape density for finding jet axes." + ParameterString();} private: double _Rjet, _ptcut; mutable fastjet::JetDefinition _jetDef; bool _applyGlobalConsistency; void _find_local_axes(const std::vector & particles) const; bool _isStable(const int thisAxis) const; mutable unsigned int _N; mutable std::vector _tot_Njet_weights,_Njet_weights, _pt_weights; mutable std::vector _axes; mutable std::vector _myParticles, _distinctAxes; FunctorLightLikeVersion _lightLikeVersion; bool _useStorageArray; mutable JWJStorageArray _myStorageArray; }; } // namespace contrib FASTJET_END_NAMESPACE #endif // __FASTJET_CONTRIB_JETSWITHOUTJETS_HH__