Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:57:18

0001 #ifndef __FASTJET_NNFJN2TILED_HH__
0002 #define __FASTJET_NNFJN2TILED_HH__
0003 
0004 //FJSTARTHEADER
0005 // $Id$
0006 //
0007 // Copyright (c) 2016-2021, Matteo Cacciari, Gavin P. Salam and Gregory Soyez
0008 //
0009 //----------------------------------------------------------------------
0010 // This file is part of FastJet.
0011 //
0012 //  FastJet is free software; you can redistribute it and/or modify
0013 //  it under the terms of the GNU General Public License as published by
0014 //  the Free Software Foundation; either version 2 of the License, or
0015 //  (at your option) any later version.
0016 //
0017 //  The algorithms that underlie FastJet have required considerable
0018 //  development. They are described in the original FastJet paper,
0019 //  hep-ph/0512210 and in the manual, arXiv:1111.6097. If you use
0020 //  FastJet as part of work towards a scientific publication, please
0021 //  quote the version you use and include a citation to the manual and
0022 //  optionally also to hep-ph/0512210.
0023 //
0024 //  FastJet is distributed in the hope that it will be useful,
0025 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
0026 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0027 //  GNU General Public License for more details.
0028 //
0029 //  You should have received a copy of the GNU General Public License
0030 //  along with FastJet. If not, see <http://www.gnu.org/licenses/>.
0031 //----------------------------------------------------------------------
0032 //FJENDHEADER
0033 
0034 #include "fastjet/NNBase.hh"
0035 #include "fastjet/internal/TilingExtent.hh"
0036 
0037 FASTJET_BEGIN_NAMESPACE      // defined in fastjet/internal/base.hh
0038 
0039 //----------------------------------------------------------------------
0040 /// @ingroup advanced_usage
0041 /// \class NNFJN2Tiled
0042 ///
0043 /// Helps solve closest pair problems with factorised interparticle
0044 /// and beam distances (ie satisfying the FastJet lemma) that are on
0045 /// a cylindrical geometry and allow tiling.
0046 ///
0047 /// (see NNBase.hh for an introductory description)
0048 ///
0049 /// This variant provides an implementation based on the N2Tiled
0050 /// clustering strategy in FastJet. As for the NNFJN2Plain case, the
0051 /// interparticle and beam distances should be of the form
0052 ///
0053 /// \code
0054 ///   dij = min(mom_factor(i), mom_factor(j)) * geometrical_distance(i,j)
0055 ///   diB = mom_factor(i) * geometrical_beam_distance(i)
0056 /// \endcode
0057 ///
0058 /// Additionally, the NNFJN2Tiled class takes a tile_size parameter
0059 /// that controls the size of the tiles. It must be such that, for any
0060 /// two points in non-neighbouring (and non-identical) tiles, the
0061 /// geometrical distance between the 2 points is larger than the
0062 /// geometrical beam distance of each of the 2 points.
0063 ///
0064 /// It is templated with a BJ (brief jet) class and can be used with or
0065 /// without an extra "Information" template, i.e. NNFJN2Tiled<BJ> or
0066 /// NNFJN2Tiled<BJ,I>
0067 ///
0068 /// For the NNFJN2Tiled<BJ> version of the class to function, BJ must provide 
0069 /// three member functions
0070 ///  
0071 /// \code
0072 ///   void   BJ::init(const PseudoJet & jet);                   // initialise with a PseudoJet
0073 ///   double BJ::geometrical_distance(const BJ * other_bj_jet); // distance between this and other_bj_jet (geometrical part)
0074 ///   double BJ::geometrical_beam_distance();                   // distance to the beam (geometrical part)
0075 ///   double BJ::momentum_factor();                             // extra momentum factor
0076 /// \endcode
0077 ///
0078 /// For the NNFJN2Tiled<BJ,I> version to function, the BJ::init(...) member
0079 /// must accept an extra argument
0080 ///
0081 /// \code
0082 ///   void BJ::init(const PseudoJet & jet, I * info);   // initialise with a PseudoJet + info
0083 /// \endcode
0084 ///
0085 /// NOTE: THE DISTANCE MUST BE SYMMETRIC I.E. SATISFY
0086 /// \code
0087 ///     a.geometrical_distance(b) == b.geometrical_distance(a)
0088 /// \endcode
0089 ///
0090 /// Finally, the BJ class needs to provide access to the variables used
0091 /// for the rectangular tiling:
0092 ///
0093 /// \code
0094 ///   double BJ::rap(); // rapidity-like variable
0095 ///   double BJ::phi(); // azimutal-angle-like variable (should be > -2pi)
0096 /// \endcode
0097 ///
0098 /// Note that you are strongly advised to add the following lines
0099 /// to your BJ class to allow it to be used also with NNH:
0100 ///
0101 /// \code
0102 ///   /// make this BJ class compatible with the use of NNH
0103 ///   double BJ::distance(const BJ * other_bj_jet){
0104 ///     double mom1 = momentum_factor();
0105 ///     double mom2 = other_bj_jet->momentum_factor();
0106 ///     return (mom1<mom2 ? mom1 : mom2) * geometrical_distance(other_bj_jet);
0107 ///   }
0108 ///   double BJ::beam_distance(){
0109 ///     return momentum_factor() * geometrical_beam_distance();
0110 ///   }
0111 /// \endcode
0112 ///
0113 template<class BJ, class I = _NoInfo> class NNFJN2Tiled : public NNBase<I> {
0114 public:
0115 
0116   /// constructor with an initial set of jets (which will be assigned indices
0117   /// `0...jets.size()-1`)
0118   NNFJN2Tiled(const std::vector<PseudoJet> & jets, double requested_tile_size)
0119     : NNBase<I>(),     _requested_tile_size(requested_tile_size) {start(jets);}
0120   NNFJN2Tiled(const std::vector<PseudoJet> & jets, double requested_tile_size, I * info)
0121     : NNBase<I>(info), _requested_tile_size(requested_tile_size) {start(jets);}
0122   
0123   void start(const std::vector<PseudoJet> & jets);
0124 
0125   /// return the dij_min and indices iA, iB, for the corresponding jets.
0126   /// If iB < 0 then iA recombines with the beam
0127   double dij_min(int & iA, int & iB);
0128 
0129   /// remove the jet pointed to by index iA
0130   void remove_jet(int iA);
0131 
0132   /// merge the jets pointed to by indices A and B and replace them with
0133   /// jet, assigning it an index jet_index.
0134   void merge_jets(int iA, int iB, const PseudoJet & jet, int jet_index);
0135 
0136   /// a destructor
0137   ~NNFJN2Tiled() {
0138     delete[] briefjets;
0139     delete[] diJ;
0140   }
0141     
0142 private:
0143   class TiledJet; // forward declaration
0144   class Tile;
0145   class diJ_plus_link;
0146 
0147   // Set up the tiles:
0148   void _initialise_tiles(const std::vector<PseudoJet> & particles);
0149 
0150   // return the full distance of a particle to its NN
0151   inline double _compute_diJ(const TiledJet * const jet) const {
0152     double mom_fact = jet->momentum_factor();
0153     if (jet->NN != NULL) {
0154       double other_mom_fact = jet->NN->momentum_factor();
0155       if (other_mom_fact < mom_fact) {mom_fact = other_mom_fact;}
0156     }
0157     return jet->NN_dist * mom_fact;
0158   }
0159 
0160   // reasonably robust return of tile index given irap and iphi, in particular
0161   // it works even if iphi is negative
0162   inline int _tile_index (int irap, int iphi) const {
0163     // note that (-1)%n = -1 so that we have to add _n_tiles_phi
0164     // before performing modulo operation
0165     return (irap-_tiles_irap_min)*_n_tiles_phi
0166                   + (iphi+_n_tiles_phi) % _n_tiles_phi;
0167   }
0168 
0169   int  _tile_index(const double rap, const double phi) const;
0170   void _tiledjet_set_jetinfo ( TiledJet * const tiled_jet, const PseudoJet &jet, int index);
0171   void _bj_remove_from_tiles(TiledJet * const jet);
0172   void _initialise_tiles();
0173   void _print_tiles(TiledJet * briefjets ) const;
0174   void _add_neighbours_to_tile_union(const int tile_index, int & n_near_tiles) const;
0175   void _add_untagged_neighbours_to_tile_union(const int tile_index, int & n_near_tiles);
0176 
0177 
0178   /// contains the briefjets
0179   TiledJet * briefjets;
0180 
0181   /// semaphores for the current extent of our structure
0182   TiledJet * head;
0183 
0184   /// currently active number of jets
0185   int n;
0186 
0187   /// where_is[i] contains a pointer to the jet with index i
0188   std::vector<TiledJet *> where_is;
0189 
0190   /// helper to keep tracks of tiles to be checked for updates
0191   std::vector<int> tile_union;
0192 
0193   /// a table containing all the (full) distances to each object's NN
0194   diJ_plus_link * diJ;
0195 
0196   /// tiling information
0197   std::vector<Tile> _tiles;
0198   double _requested_tile_size;
0199   double _tiles_rap_min, _tiles_rap_max;
0200   double _tile_size_rap, _tile_size_phi;
0201   int    _n_tiles_phi,_tiles_irap_min,_tiles_irap_max;
0202   
0203   /// a class that wraps around the BJ, supplementing it with extra information
0204   /// such as pointers to neighbours, etc.
0205   class TiledJet : public BJ {
0206   public:
0207     void init(const PseudoJet & jet, int index_in) {
0208       BJ::init(jet);
0209       other_init(index_in);
0210     }
0211     void init(const PseudoJet & jet, int index_in, I * info) {
0212       BJ::init(jet, info);
0213       other_init(index_in);
0214     }
0215     void other_init(int index_in) {
0216       _index = index_in;
0217       NN_dist = BJ::geometrical_beam_distance();
0218       NN = NULL;
0219     }
0220     int jet_index() const {return _index;}
0221     
0222     double NN_dist;
0223     TiledJet * NN, *previous, * next;
0224     int tile_index, diJ_posn;
0225     // routines that are useful in the minheap version of tiled
0226     // clustering ("misuse" the otherwise unused diJ_posn, so as
0227     // to indicate whether jets need to have their minheap entries
0228     // updated).
0229     inline void label_minheap_update_needed() {diJ_posn = 1;}
0230     inline void label_minheap_update_done()   {diJ_posn = 0;}
0231     inline bool minheap_update_needed() const {return diJ_posn==1;}
0232     
0233   private:
0234     int _index;
0235   };
0236 
0237   /// number of neighbours that a tile will have (rectangular geometry
0238   /// gives 9 neighbours).
0239   static const int n_tile_neighbours = 9;
0240   //----------------------------------------------------------------------
0241   /// The fundamental structures to be used for the tiled N^2 algorithm
0242   /// (see CCN27-44 for some discussion of pattern of tiling)
0243   class Tile {
0244   public:
0245     /// pointers to neighbouring tiles, including self
0246     Tile *   begin_tiles[n_tile_neighbours]; 
0247     /// neighbouring tiles, excluding self
0248     Tile **  surrounding_tiles; 
0249     /// half of neighbouring tiles, no self
0250     Tile **  RH_tiles;  
0251     /// just beyond end of tiles
0252     Tile **  end_tiles; 
0253     /// start of list of BriefJets contained in this tile
0254     TiledJet * head;    
0255     /// sometimes useful to be able to tag a tile
0256     bool     tagged;    
0257   };
0258 
0259   // structure that holds the real, full, distance (as well as a pointer to the corresponding TiledJet)
0260   class diJ_plus_link {
0261   public:
0262     double     diJ; // the distance
0263     TiledJet * jet; // the jet (i) for which we've found this distance
0264                     // (whose NN will the J).
0265   };
0266 
0267 };
0268 
0269 
0270 
0271 //----------------------------------------------------------------------
0272 template<class BJ, class I> void NNFJN2Tiled<BJ,I>::start(const std::vector<PseudoJet> & jets) {
0273 
0274   _initialise_tiles(jets);
0275 
0276   n = jets.size();
0277 
0278   briefjets = new TiledJet[n];
0279   where_is.resize(2*n);
0280 
0281   TiledJet * jetA = briefjets, * jetB;
0282 
0283   // will be used quite deep inside loops, but declare it here so that
0284   // memory (de)allocation gets done only once
0285   tile_union.resize(3*n_tile_neighbours);
0286   
0287   // initialise the basic jet info 
0288   for (int i = 0; i< n; i++) {
0289     _tiledjet_set_jetinfo(jetA, jets[i], i);
0290     where_is[i] = jetA;
0291     jetA++; // move on to next entry of briefjets
0292   }
0293 
0294   head = briefjets; // a nicer way of naming start
0295 
0296   // set up the initial nearest neighbour information
0297   typename std::vector<Tile>::const_iterator tile;
0298   for (tile = _tiles.begin(); tile != _tiles.end(); tile++) {
0299     // first do it on this tile
0300     for (jetA = tile->head; jetA != NULL; jetA = jetA->next) {
0301       for (jetB = tile->head; jetB != jetA; jetB = jetB->next) {
0302     double dist = jetA->geometrical_distance(jetB);
0303     if (dist < jetA->NN_dist) {jetA->NN_dist = dist; jetA->NN = jetB;}
0304     if (dist < jetB->NN_dist) {jetB->NN_dist = dist; jetB->NN = jetA;}
0305       }
0306     }
0307     // then do it for RH tiles
0308     for (Tile ** RTile = tile->RH_tiles; RTile != tile->end_tiles; RTile++) {
0309       for (jetA = tile->head; jetA != NULL; jetA = jetA->next) {
0310     for (jetB = (*RTile)->head; jetB != NULL; jetB = jetB->next) {
0311       double dist = jetA->geometrical_distance(jetB);
0312       if (dist < jetA->NN_dist) {jetA->NN_dist = dist; jetA->NN = jetB;}
0313       if (dist < jetB->NN_dist) {jetB->NN_dist = dist; jetB->NN = jetA;}
0314     }
0315       }
0316     }
0317     // no need to do it for LH tiles, since they are implicitly done
0318     // when we set NN for both jetA and jetB on the RH tiles.
0319   }
0320   
0321   diJ = new diJ_plus_link[n];
0322   jetA = head;
0323   for (int i = 0; i < n; i++) {
0324     diJ[i].diJ = _compute_diJ(jetA); // kt distance * R^2
0325     diJ[i].jet = jetA;  // our compact diJ table will not be in      
0326     jetA->diJ_posn = i; // one-to-one corresp. with non-compact jets,
0327                         // so set up bi-directional correspondence here.
0328     jetA++; // have jetA follow i 
0329   }
0330 }
0331 
0332 
0333 //----------------------------------------------------------------------
0334 template<class BJ, class I> double NNFJN2Tiled<BJ,I>::dij_min(int & iA, int & iB) {
0335   // find the minimum of the diJ on this round
0336   diJ_plus_link * best, *stop; // pointers a bit faster than indices
0337                                // could use best to keep track of diJ
0338                                // min, but it turns out to be
0339                                // marginally faster to have a separate
0340                                // variable (avoids n dereferences at
0341                                // the expense of n/2 assignments).
0342   double diJ_min = diJ[0].diJ; // initialise the best one here.
0343   best = diJ;                  // and here
0344   stop = diJ+n;
0345   for (diJ_plus_link * here = diJ+1; here != stop; here++) {
0346     if (here->diJ < diJ_min) {best = here; diJ_min  = here->diJ;}
0347   }
0348 
0349   // return information to user about recombination
0350   TiledJet * jetA = best->jet;
0351   iA = jetA->jet_index();
0352   iB = jetA->NN ? jetA->NN->jet_index() : -1;
0353   return diJ_min;
0354 }
0355 
0356 
0357 //----------------------------------------------------------------------
0358 // remove jetA from the list
0359 template<class BJ, class I> void NNFJN2Tiled<BJ,I>::remove_jet(int iA) {
0360   TiledJet * jetA = where_is[iA];
0361 
0362   _bj_remove_from_tiles(jetA);
0363 
0364   // first establish the set of tiles over which we are going to
0365   // have to run searches for updated and new nearest-neighbours --
0366   // basically a combination of vicinity of the tiles of the two old
0367   // and one new jet.
0368   int n_near_tiles = 0;
0369   _add_untagged_neighbours_to_tile_union(jetA->tile_index, n_near_tiles);
0370 
0371   // now update our nearest neighbour info and diJ table
0372   // first reduce size of table
0373   n--;
0374   // then compactify the diJ by taking the last of the diJ and copying
0375   // it to the position occupied by the diJ for jetA
0376   diJ[n].jet->diJ_posn = jetA->diJ_posn;
0377   diJ[jetA->diJ_posn] = diJ[n];
0378 
0379   // updating other particles' NN.
0380   // Run over all tiles in our union 
0381   for (int itile = 0; itile < n_near_tiles; itile++) {
0382     Tile * tile_ptr = &_tiles[tile_union[itile]];
0383     tile_ptr->tagged = false; // reset tag, since we're done with unions
0384     // run over all jets in the current tile
0385     for (TiledJet * jetI = tile_ptr->head; jetI != NULL; jetI = jetI->next) {
0386       // see if jetI had jetA or jetB as a NN -- if so recalculate the NN
0387       if (jetI->NN == jetA) {
0388         jetI->NN_dist = jetI->geometrical_beam_distance();
0389         jetI->NN      = NULL;
0390         // now go over tiles that are neighbours of I (include own tile)
0391         for (Tile ** near_tile  = tile_ptr->begin_tiles; 
0392              near_tile != tile_ptr->end_tiles; near_tile++) {
0393           // and then over the contents of that tile
0394           for (TiledJet * jetJ  = (*near_tile)->head; jetJ != NULL; jetJ = jetJ->next) {
0395             double dist = jetI->geometrical_distance(jetJ);
0396             if (dist < jetI->NN_dist && jetJ != jetI) {
0397               jetI->NN_dist = dist; jetI->NN = jetJ;
0398             }
0399           }
0400         }
0401         diJ[jetI->diJ_posn].diJ = _compute_diJ(jetI); // update diJ kt-dist
0402       }
0403     }
0404   }
0405 
0406 }
0407 
0408 
0409 //----------------------------------------------------------------------
0410 template<class BJ, class I> void NNFJN2Tiled<BJ,I>::merge_jets(int iA, int iB, 
0411                     const PseudoJet & jet, int index) {
0412 
0413   TiledJet * jetA = where_is[iA];
0414   TiledJet * jetB = where_is[iB];
0415 
0416   // jet-jet recombination
0417   // If necessary relabel A & B to ensure jetB < jetA, that way if
0418   // the larger of them == newtail then that ends up being jetA and 
0419   // the new jet that is added as jetB is inserted in a position that
0420   // has a future!
0421   if (jetA < jetB) {std::swap(jetA,jetB);}
0422 
0423   // what was jetB will now become the new jet
0424   _bj_remove_from_tiles(jetA);
0425   TiledJet oldB = * jetB;  // take a copy because we will need it...
0426   _bj_remove_from_tiles(jetB);
0427   _tiledjet_set_jetinfo(jetB, jet, index); // cause jetB to become _jets[nn]
0428                                            // (also registers the jet in the tiling)
0429   where_is[index] = jetB;
0430   
0431   // first establish the set of tiles over which we are going to
0432   // have to run searches for updated and new nearest-neighbours --
0433   // basically a combination of vicinity of the tiles of the two old
0434   // and one new jet.
0435   int n_near_tiles = 0;
0436   _add_untagged_neighbours_to_tile_union(jetA->tile_index, n_near_tiles);
0437   if (jetB->tile_index != jetA->tile_index) {
0438     _add_untagged_neighbours_to_tile_union(jetB->tile_index, n_near_tiles);
0439   }
0440   if (oldB.tile_index != jetA->tile_index && 
0441       oldB.tile_index != jetB->tile_index) {
0442     _add_untagged_neighbours_to_tile_union(oldB.tile_index, n_near_tiles);
0443   }
0444 
0445   // now update our nearest neighbour info and diJ table
0446   // first reduce size of table
0447   n--;
0448   // then compactify the diJ by taking the last of the diJ and copying
0449   // it to the position occupied by the diJ for jetA
0450   diJ[n].jet->diJ_posn = jetA->diJ_posn;
0451   diJ[jetA->diJ_posn] = diJ[n];
0452 
0453   // Initialise jetB's NN distance as well as updating it for 
0454   // other particles.
0455   // Run over all tiles in our union 
0456   for (int itile = 0; itile < n_near_tiles; itile++) {
0457     Tile * tile_ptr = &_tiles[tile_union[itile]];
0458     tile_ptr->tagged = false; // reset tag, since we're done with unions
0459     // run over all jets in the current tile
0460     for (TiledJet * jetI = tile_ptr->head; jetI != NULL; jetI = jetI->next) {
0461       // see if jetI had jetA or jetB as a NN -- if so recalculate the NN
0462       if ((jetI->NN == jetA) || (jetI->NN == jetB)) {
0463         jetI->NN_dist = jetI->geometrical_beam_distance();
0464         jetI->NN      = NULL;
0465         // now go over tiles that are neighbours of I (include own tile)
0466         for (Tile ** near_tile  = tile_ptr->begin_tiles; near_tile != tile_ptr->end_tiles; near_tile++) {
0467           // and then over the contents of that tile
0468           for (TiledJet * jetJ  = (*near_tile)->head; jetJ != NULL; jetJ = jetJ->next) {
0469             double dist = jetI->geometrical_distance(jetJ);
0470             if (dist < jetI->NN_dist && jetJ != jetI) {
0471               jetI->NN_dist = dist; jetI->NN = jetJ;
0472             }
0473           }
0474         }
0475         diJ[jetI->diJ_posn].diJ = _compute_diJ(jetI); // update diJ kt-dist
0476       }
0477       // check whether new jetB is closer than jetI's current NN and
0478       // if jetI is closer than jetB's current (evolving) nearest
0479       // neighbour. Where relevant update things
0480       double dist = jetI->geometrical_distance(jetB);
0481       if (dist < jetI->NN_dist) {
0482         if (jetI != jetB) {
0483           jetI->NN_dist = dist;
0484           jetI->NN = jetB;
0485           diJ[jetI->diJ_posn].diJ = _compute_diJ(jetI); // update diJ...
0486         }
0487       }
0488       if (dist < jetB->NN_dist) {
0489         if (jetI != jetB) {
0490           jetB->NN_dist = dist;
0491           jetB->NN      = jetI;}
0492       }
0493     }
0494   }
0495 
0496   // finally, register the updated kt distance for B
0497   diJ[jetB->diJ_posn].diJ = _compute_diJ(jetB);
0498 }
0499 
0500 
0501 //----------------------------------------------------------------------
0502 /// Set up the tiles:
0503 ///  - decide the range in eta
0504 ///  - allocate the tiles
0505 ///  - set up the cross-referencing info between tiles
0506 ///
0507 /// The neighbourhood of a tile is set up as follows
0508 ///
0509 ///           LRR
0510 ///           LXR
0511 ///           LLR
0512 ///
0513 /// such that tiles is an array containing XLLLLRRRR with pointers
0514 ///                                         |   \ RH_tiles
0515 ///                                         \ surrounding_tiles
0516 ///
0517 /// with appropriate precautions when close to the edge of the tiled
0518 /// region.
0519 ///
0520 template <class BJ, class I>
0521 void NNFJN2Tiled<BJ,I>::_initialise_tiles(const std::vector<PseudoJet> &particles) {
0522 
0523   // first decide tile sizes (with a lower bound to avoid huge memory use with
0524   // very small R)
0525   double default_size = _requested_tile_size>0.1 ? _requested_tile_size : 0.1;
0526   _tile_size_rap = default_size;
0527   // it makes no sense to go below 3 tiles in phi -- 3 tiles is
0528   // sufficient to make sure all pair-wise combinations up to pi in
0529   // phi are possible
0530   _n_tiles_phi   = int(floor(twopi/default_size));
0531   if (_n_tiles_phi<3) _n_tiles_phi = 3;
0532   _tile_size_phi = twopi / _n_tiles_phi; // >= _Rparam and fits in 2pi
0533 
0534   TilingExtent tiling_analysis(particles);
0535   _tiles_rap_min = tiling_analysis.minrap();
0536   _tiles_rap_max = tiling_analysis.maxrap();
0537 
0538   // now adjust the values
0539   _tiles_irap_min = int(floor(_tiles_rap_min/_tile_size_rap));
0540   _tiles_irap_max = int(floor( _tiles_rap_max/_tile_size_rap));
0541   _tiles_rap_min = _tiles_irap_min * _tile_size_rap;
0542   _tiles_rap_max = _tiles_irap_max * _tile_size_rap;
0543 
0544   // allocate the tiles
0545   _tiles.resize((_tiles_irap_max-_tiles_irap_min+1)*_n_tiles_phi);
0546 
0547   // now set up the cross-referencing between tiles
0548   for (int irap = _tiles_irap_min; irap <= _tiles_irap_max; irap++) {
0549     for (int iphi = 0; iphi < _n_tiles_phi; iphi++) {
0550       Tile * tile = & _tiles[_tile_index(irap,iphi)];
0551       // no jets in this tile yet
0552       tile->head = NULL; // first element of tiles points to itself
0553       tile->begin_tiles[0] =  tile;
0554       Tile ** pptile = & (tile->begin_tiles[0]);
0555       pptile++;
0556       //
0557       // set up L's in column to the left of X
0558       tile->surrounding_tiles = pptile;
0559       if (irap > _tiles_irap_min) {
0560     // with the itile subroutine, we can safely run tiles from
0561     // idphi=-1 to idphi=+1, because it takes care of
0562     // negative and positive boundaries
0563     for (int idphi = -1; idphi <=+1; idphi++) {
0564       *pptile = & _tiles[_tile_index(irap-1,iphi+idphi)];
0565       pptile++;
0566     }   
0567       }
0568       // now set up last L (below X)
0569       *pptile = & _tiles[_tile_index(irap,iphi-1)];
0570       pptile++;
0571       // set up first R (above X)
0572       tile->RH_tiles = pptile;
0573       *pptile = & _tiles[_tile_index(irap,iphi+1)];
0574       pptile++;
0575       // set up remaining R's, to the right of X
0576       if (irap < _tiles_irap_max) {
0577     for (int idphi = -1; idphi <= +1; idphi++) {
0578       *pptile = & _tiles[_tile_index(irap+1,iphi+idphi)];
0579       pptile++;
0580     }   
0581       }
0582       // now put semaphore for end tile
0583       tile->end_tiles = pptile;
0584       // finally make sure tiles are untagged
0585       tile->tagged = false;
0586     }
0587   }
0588 
0589 }
0590 
0591 //----------------------------------------------------------------------
0592 /// return the tile index corresponding to the given rap,phi point
0593 template <class BJ, class I>
0594 int NNFJN2Tiled<BJ,I>::_tile_index(const double rap, const double phi) const {
0595   int irap, iphi;
0596   if      (rap <= _tiles_rap_min) {irap = 0;}
0597   else if (rap >= _tiles_rap_max) {irap = _tiles_irap_max-_tiles_irap_min;}
0598   else {
0599     //irap = int(floor((rap - _tiles_rap_min) / _tile_size_rap));
0600     irap = int(((rap - _tiles_rap_min) / _tile_size_rap));
0601     // following needed in case of rare but nasty rounding errors
0602     if (irap > _tiles_irap_max-_tiles_irap_min) {
0603       irap = _tiles_irap_max-_tiles_irap_min;} 
0604   }
0605   // allow for some extent of being beyond range in calculation of phi
0606   // as well
0607   //iphi = (int(floor(phi/_tile_size_phi)) + _n_tiles_phi) % _n_tiles_phi;
0608   // with just int and no floor, things run faster but beware
0609   iphi = int((phi+twopi)/_tile_size_phi) % _n_tiles_phi;
0610   return (iphi + irap * _n_tiles_phi);
0611 }
0612 
0613 //----------------------------------------------------------------------
0614 template <class BJ, class I>
0615 void NNFJN2Tiled<BJ,I>::_bj_remove_from_tiles(TiledJet * const jet) {
0616   Tile * tile = & _tiles[jet->tile_index];
0617 
0618   if (jet->previous == NULL) {
0619     // we are at head of the tile, so reset it.
0620     // If this was the only jet on the tile then tile->head will now be NULL
0621     tile->head = jet->next;
0622   } else {
0623     // adjust link from previous jet in this tile
0624     jet->previous->next = jet->next;
0625   }
0626   if (jet->next != NULL) {
0627     // adjust backwards-link from next jet in this tile
0628     jet->next->previous = jet->previous;
0629   }
0630 }
0631 
0632 
0633 //----------------------------------------------------------------------
0634 // overloaded version which additionally sets up information regarding the
0635 // tiling
0636 template <class BJ, class I>
0637 inline void NNFJN2Tiled<BJ,I>::_tiledjet_set_jetinfo(TiledJet * const tile_jet,
0638                                                    const PseudoJet &jet, 
0639                                                    int index) {
0640   // the this-> in the next line is required by standard compiler
0641   // see e.g. http://stackoverflow.com/questions/10639053/name-lookups-in-c-templates
0642   this->init_jet(tile_jet, jet, index); 
0643 
0644   // Then do the setup specific to the tiled case.
0645 
0646   // Find out which tile it belonds to
0647   tile_jet->tile_index = _tile_index(tile_jet->rap(), tile_jet->phi());
0648 
0649   // Insert it into the tile's linked list of jets
0650   Tile * tile = &_tiles[tile_jet->tile_index];
0651   tile_jet->previous   = NULL;
0652   tile_jet->next       = tile->head;
0653   if (tile_jet->next != NULL) {tile_jet->next->previous = tile_jet;}
0654   tile->head      = tile_jet;
0655 }
0656 
0657 //----------------------------------------------------------------------
0658 /// Add to the vector tile_union the tiles that are in the neighbourhood
0659 /// of the specified tile_index, including itself -- start adding
0660 /// from position n_near_tiles-1, and increase n_near_tiles as
0661 /// you go along (could have done it more C++ like with vector with reserved
0662 /// space, but fear is that it would have been slower, e.g. checking
0663 /// for end of vector at each stage to decide whether to resize it)
0664 template <class BJ, class I>
0665 void NNFJN2Tiled<BJ,I>::_add_neighbours_to_tile_union(const int tile_index, 
0666                                                     int & n_near_tiles) const {
0667   for (Tile * const * near_tile = _tiles[tile_index].begin_tiles; 
0668        near_tile != _tiles[tile_index].end_tiles; near_tile++){
0669     // get the tile number
0670     tile_union[n_near_tiles] = *near_tile - & _tiles[0];
0671     n_near_tiles++;
0672   }
0673 }
0674 
0675 //----------------------------------------------------------------------
0676 /// Like _add_neighbours_to_tile_union, but only adds neighbours if 
0677 /// their "tagged" status is false; when a neighbour is added its
0678 /// tagged status is set to true.
0679 ///
0680 /// Note that with a high level of warnings (-pedantic -Wextra -ansi,
0681 /// gcc complains about tile_index maybe being used uninitialised for
0682 /// oldB in ClusterSequence::_minheap_faster_tiled_N2_cluster(). We
0683 /// have explicitly checked that it was harmless so we could disable
0684 /// the gcc warning by hand using the construct below
0685 ///
0686 ///  #pragma GCC diagnostic push
0687 ///  #pragma GCC diagnostic ignored "-Wpragmas"
0688 ///  #pragma GCC diagnostic ignored "-Wuninitialized"
0689 ///  #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
0690 ///    ...
0691 ///  #pragma GCC diagnostic pop
0692 ///
0693 /// the @GCC diagnostic push/pop directive was only introduced in
0694 /// gcc-4.6, so for broader usage, we'd need to insert #pragma GCC
0695 /// diagnostic ignored "-Wpragmas" at the top of this file
0696 template <class BJ, class I>
0697 inline void NNFJN2Tiled<BJ,I>::_add_untagged_neighbours_to_tile_union(
0698                const int tile_index, 
0699            int & n_near_tiles)  {
0700   for (Tile ** near_tile = _tiles[tile_index].begin_tiles; 
0701        near_tile != _tiles[tile_index].end_tiles; near_tile++){
0702     if (! (*near_tile)->tagged) {
0703       (*near_tile)->tagged = true;
0704       // get the tile number
0705       tile_union[n_near_tiles] = *near_tile - & _tiles[0];
0706       n_near_tiles++;
0707     }
0708   }
0709 }
0710 
0711 
0712 
0713 FASTJET_END_NAMESPACE      // defined in fastjet/internal/base.hh
0714 
0715 
0716 #endif // __FASTJET_NNFJN2TILED_HH__