#ifndef __FASTJET_NNFJN2PLAIN_HH__ #define __FASTJET_NNFJN2PLAIN_HH__ //FJSTARTHEADER // $Id: NNFJN2Plain.hh 4355 2018-04-22 15:38:54Z salam $ // // Copyright (c) 2005-2018, 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. They are described in the original FastJet paper, // hep-ph/0512210 and in the manual, arXiv:1111.6097. If you use // FastJet as part of work towards a scientific publication, please // quote the version you use and include a citation to the manual and // optionally also to hep-ph/0512210. // // 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 . //---------------------------------------------------------------------- //FJENDHEADER #include FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh //---------------------------------------------------------------------- /// @ingroup advanced_usage /// \class NNFJN2Plain /// /// Helps solve closest pair problems with factorised interparticle /// and beam distances (ie satisfying the FastJet lemma) /// /// (see NNBase.hh for an introductory description) /// /// This variant provides an implementation based on the N2Plain /// clustering strategy in FastJet. The interparticle and beam /// distances should be of the form /// /// \code /// dij = min(mom_factor(i), mom_factor(j)) * geometrical_distance(i,j) /// diB = mom_factor(i) * geometrical_beam_distance(i) /// \endcode /// /// The class is templated with a BJ (brief jet) class and can be used /// with or without an extra "Information" template, /// i.e. NNFJN2Plain or NNFJN2Plain /// /// For the NNH_N2Plain version of the class to function, BJ must /// provide four member functions /// /// \code /// void BJ::init(const PseudoJet & jet); // initialise with a PseudoJet /// double BJ::geometrical_distance(const BJ * other_bj_jet); // distance between this and other_bj_jet (geometrical part) /// double BJ::geometrical_beam_distance(); // distance to the beam (geometrical part) /// double BJ::momentum_factor(); // extra momentum factor /// \endcode /// /// For the NNH_N2Plain version to function, the BJ::init(...) /// member must accept an extra argument /// /// \code /// void BJ::init(const PseudoJet & jet, I * info); // initialise with a PseudoJet + info /// \endcode /// /// NOTE: THE DISTANCE MUST BE SYMMETRIC I.E. SATISFY /// \code /// a.geometrical_distance(b) == b.geometrical_distance(a) /// \endcode /// /// Note that you are strongly advised to add the following lines to /// your BJ class to allow it to be used also with NNH: /// /// \code /// /// make this BJ class compatible with the use of NNH /// double BJ::distance(const BJ * other_bj_jet){ /// double mom1 = momentum_factor(); /// double mom2 = other_bj_jet->momentum_factor(); /// return (mom1 class NNFJN2Plain : public NNBase { public: /// constructor with an initial set of jets (which will be assigned indices /// `0...jets.size()-1`) NNFJN2Plain(const std::vector & jets) : NNBase() {start(jets);} NNFJN2Plain(const std::vector & jets, I * info) : NNBase(info) {start(jets);} /// initialisation from a given list of particles virtual void start(const std::vector & jets); /// returns the dij_min and indices iA, iB, for the corresponding jets. /// If iB < 0 then iA recombines with the beam double dij_min(int & iA, int & iB); /// removes the jet pointed to by index iA void remove_jet(int iA); /// merges the jets pointed to by indices A and B and replace them with /// jet, assigning it an index jet_index. void merge_jets(int iA, int iB, const PseudoJet & jet, int jet_index); /// a destructor ~NNFJN2Plain() { delete[] briefjets; delete[] diJ; } private: class NNBJ; // forward declaration // return the full distance of a particle to its NN inline double compute_diJ(const NNBJ * const jet) const { double mom_fact = jet->momentum_factor(); if (jet->NN != NULL) { double other_mom_fact = jet->NN->momentum_factor(); if (other_mom_fact < mom_fact) {mom_fact = other_mom_fact;} } return jet->NN_dist * mom_fact; } /// establish the nearest neighbour for jet, and cross check consistency /// of distances for the other jets that are encountered. Assumes /// jet not contained within begin...end void set_NN_crosscheck(NNBJ * jet, NNBJ * begin, NNBJ * end); /// establish the nearest neighbour for jet; don't cross check other jets' /// distances and allow jet to be contained within begin...end void set_NN_nocross (NNBJ * jet, NNBJ * begin, NNBJ * end); /// contains the briefjets NNBJ * briefjets; /// semaphores for the current extent of our structure NNBJ * head, * tail; /// currently active number of jets int n; /// where_is[i] contains a pointer to the jet with index i std::vector where_is; /// a table containing all the (full) distances to each object's NN double * diJ; /// a class that wraps around the BJ, supplementing it with extra information /// such as pointers to neighbours, etc. class NNBJ : public BJ { public: void init(const PseudoJet & jet, int index_in) { BJ::init(jet); other_init(index_in); } void init(const PseudoJet & jet, int index_in, I * info) { BJ::init(jet, info); other_init(index_in); } void other_init(int index_in) { _index = index_in; NN_dist = BJ::geometrical_beam_distance(); NN = NULL; } int index() const {return _index;} double NN_dist; NNBJ * NN; private: int _index; }; }; //---------------------------------------------------------------------- template void NNFJN2Plain::start(const std::vector & jets) { n = jets.size(); briefjets = new NNBJ[n]; where_is.resize(2*n); NNBJ * jetA = briefjets; // initialise the basic jet info for (int i = 0; i< n; i++) { // the this-> in the next line is required by standard compiler // see e.g. http://stackoverflow.com/questions/10639053/name-lookups-in-c-templates this->init_jet(jetA, jets[i], i); where_is[i] = jetA; jetA++; // move on to next entry of briefjets } tail = jetA; // a semaphore for the end of briefjets head = briefjets; // a nicer way of naming start // now initialise the NN distances: jetA will run from 1..n-1; and // jetB from 0..jetA-1 for (jetA = head + 1; jetA != tail; jetA++) { // set NN info for jetA based on jets running from head..jetA-1, // checking in the process whether jetA itself is an undiscovered // NN of one of those jets. set_NN_crosscheck(jetA, head, jetA); } // now create the diJ (where J is i's NN) table -- remember that // we differ from standard normalisation here by a factor of R2 diJ = new double[n]; jetA = head; for (int i = 0; i < n; i++) { diJ[i] = compute_diJ(jetA); jetA++; // have jetA follow i } } //---------------------------------------------------------------------- template double NNFJN2Plain::dij_min(int & iA, int & iB) { // find the minimum of the diJ on this round double diJ_min = diJ[0]; int diJ_min_jet = 0; for (int i = 1; i < n; i++) { if (diJ[i] < diJ_min) { diJ_min_jet = i; diJ_min = diJ[i]; } } // return information to user about recombination NNBJ * jetA = & briefjets[diJ_min_jet]; //std::cout << jetA - briefjets << std::endl; iA = jetA->index(); iB = jetA->NN ? jetA->NN->index() : -1; return diJ_min; } //---------------------------------------------------------------------- // remove jetA from the list template void NNFJN2Plain::remove_jet(int iA) { NNBJ * jetA = where_is[iA]; // now update our nearest neighbour info and diJ table // first reduce size of table tail--; n--; // Copy last jet contents and diJ info into position of jetA *jetA = *tail; // update the info on where the given index is stored where_is[jetA->index()] = jetA; diJ[jetA - head] = diJ[tail-head]; // updating NN infos. 2 cases to watch for (see below) for (NNBJ * jetI = head; jetI != tail; jetI++) { // see if jetI had jetA as a NN -- if so recalculate the NN if (jetI->NN == jetA) { set_NN_nocross(jetI, head, tail); diJ[jetI-head] = compute_diJ(jetI); // update diJ } // if jetI's NN is the new tail then relabel it so that it becomes jetA if (jetI->NN == tail) {jetI->NN = jetA;} } } //---------------------------------------------------------------------- template void NNFJN2Plain::merge_jets(int iA, int iB, const PseudoJet & jet, int index) { NNBJ * jetA = where_is[iA]; NNBJ * jetB = where_is[iB]; // If necessary relabel A & B to ensure jetB < jetA, that way if // the larger of them == newtail then that ends up being jetA and // the new jet that is added as jetB is inserted in a position that // has a future! if (jetA < jetB) std::swap(jetA,jetB); // initialise jetB based on the new jet this->init_jet(jetB, jet, index); // and record its position (making sure we have the space) if (index >= int(where_is.size())) where_is.resize(2*index); where_is[jetB->index()] = jetB; // now update our nearest neighbour info // first reduce size of table tail--; n--; // Copy last jet contents into position of jetA *jetA = *tail; // update the info on where the tail's index is stored where_is[jetA->index()] = jetA; diJ[jetA - head] = diJ[tail-head]; // initialise jetB NN's distance and update NN infos for (NNBJ * jetI = head; jetI != tail; jetI++) { // see if jetI had jetA or jetB as a NN -- if so recalculate the NN if (jetI->NN == jetA || jetI->NN == jetB) { set_NN_nocross(jetI, head, tail); diJ[jetI-head] = compute_diJ(jetI); // update diJ } // check whether new jetB is closer than jetI's current NN and // if need be update things double dist = jetI->geometrical_distance(jetB); if (dist < jetI->NN_dist) { // we need to update I if (jetI != jetB) { jetI->NN_dist = dist; jetI->NN = jetB; diJ[jetI-head] = compute_diJ(jetI); // update diJ... } } if (dist < jetB->NN_dist) { // we need to update B if (jetI != jetB) { jetB->NN_dist = dist; jetB->NN = jetI; } } // if jetI's NN is the new tail then relabel it so that it becomes jetA if (jetI->NN == tail) {jetI->NN = jetA;} } // update info for jetB diJ[jetB-head] = compute_diJ(jetB); } //---------------------------------------------------------------------- // this function assumes that jet is not contained within begin...end template void NNFJN2Plain::set_NN_crosscheck(NNBJ * jet, NNBJ * begin, NNBJ * end) { double NN_dist = jet->geometrical_beam_distance(); NNBJ * NN = NULL; for (NNBJ * jetB = begin; jetB != end; jetB++) { double dist = jet->geometrical_distance(jetB); if (dist < NN_dist) { NN_dist = dist; NN = jetB; } if (dist < jetB->NN_dist) { jetB->NN_dist = dist; jetB->NN = jet; } } jet->NN = NN; jet->NN_dist = NN_dist; } //---------------------------------------------------------------------- // set the NN for jet without checking whether in the process you might // have discovered a new nearest neighbour for another jet template void NNFJN2Plain::set_NN_nocross( NNBJ * jet, NNBJ * begin, NNBJ * end) { double NN_dist = jet->geometrical_beam_distance(); NNBJ * NN = NULL; // if (head < jet) { // for (NNBJ * jetB = head; jetB != jet; jetB++) { if (begin < jet) { for (NNBJ * jetB = begin; jetB != jet; jetB++) { double dist = jet->geometrical_distance(jetB); if (dist < NN_dist) { NN_dist = dist; NN = jetB; } } } // if (tail > jet) { // for (NNBJ * jetB = jet+1; jetB != tail; jetB++) { if (end > jet) { for (NNBJ * jetB = jet+1; jetB != end; jetB++) { double dist = jet->geometrical_distance(jetB); if (dist < NN_dist) { NN_dist = dist; NN = jetB; } } } jet->NN = NN; jet->NN_dist = NN_dist; } FASTJET_END_NAMESPACE // defined in fastjet/internal/base.hh #endif // __FASTJET_NNFJN2PLAIN_HH__