FastJet 3.5.0
Loading...
Searching...
No Matches
ClusterSequence_N2.icc
1// -*- C++ -*-
2#ifndef __FASTJET_CLUSTERQUENCE_N2_ICC__
3#define __FASTJET_CLUSTERQUENCE_N2_ICC__
4#include "fastjet/ClusterSequence.hh"
5
6//FJSTARTHEADER
7// $Id: ClusterSequence_N2.cc 1351 2009-01-09 18:03:03Z salam $
8//
9// Copyright (c) 2005-2025, Matteo Cacciari, Gavin P. Salam and Gregory Soyez
10//
11//----------------------------------------------------------------------
12// This file is part of FastJet.
13//
14// FastJet is free software; you can redistribute it and/or modify
15// it under the terms of the GNU General Public License as published by
16// the Free Software Foundation; either version 2 of the License, or
17// (at your option) any later version.
18//
19// The algorithms that underlie FastJet have required considerable
20// development. They are described in the original FastJet paper,
21// hep-ph/0512210 and in the manual, arXiv:1111.6097. If you use
22// FastJet as part of work towards a scientific publication, please
23// quote the version you use and include a citation to the manual and
24// optionally also to hep-ph/0512210.
25//
26// FastJet is distributed in the hope that it will be useful,
27// but WITHOUT ANY WARRANTY; without even the implied warranty of
28// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29// GNU General Public License for more details.
30//
31// You should have received a copy of the GNU General Public License
32// along with FastJet. If not, see <http://www.gnu.org/licenses/>.
33//----------------------------------------------------------------------
34//FJENDHEADER
35
36//----------------------------------------------------------------------
37/// Order(N^2) clustering
38///
39/// Works for any class BJ that satisfies certain minimal
40/// requirements (which are ...?)
41///
42/// - need to have _bj_set_jetinfo
43/// - need to have _bj_dist
44/// - should contain members kt2 (=energy^2), NN, NN_dist, _jets_index
45
46FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
47
48// this should not normally appear in an include file, but we'll make an
49// exception seeing as this is
50//using namespace std;
51
52
53template<class BJ> void ClusterSequence::_simple_N2_cluster() {
54 int n = _jets.size();
55 BJ * briefjets = new BJ[n];
56 BJ * jetA = briefjets, * jetB;
57
58 // initialise the basic jet info
59 for (int i = 0; i< n; i++) {
60 _bj_set_jetinfo(jetA, i);
61 jetA++; // move on to next entry of briefjets
62 }
63 BJ * tail = jetA; // a semaphore for the end of briefjets
64 BJ * head = briefjets; // a nicer way of naming start
65
66 // now initialise the NN distances: jetA will run from 1..n-1; and
67 // jetB from 0..jetA-1
68 for (jetA = head + 1; jetA != tail; jetA++) {
69 // set NN info for jetA based on jets running from head..jetA-1,
70 // checking in the process whether jetA itself is an undiscovered
71 // NN of one of those jets.
72 _bj_set_NN_crosscheck(jetA, head, jetA);
73 }
74
75
76 // now create the diJ (where J is i's NN) table -- remember that
77 // we differ from standard normalisation here by a factor of R2
78 double * diJ = new double[n];
79 jetA = head;
80 for (int i = 0; i < n; i++) {
81 diJ[i] = _bj_diJ(jetA);
82 jetA++; // have jetA follow i
83 }
84
85 // now run the recombination loop
86 while (tail != head) {
87
88 // find the minimum of the diJ on this round
89 double diJ_min = diJ[0];
90 int diJ_min_jet = 0;
91 for (int i = 1; i < n; i++) {
92 if (diJ[i] < diJ_min) {diJ_min_jet = i; diJ_min = diJ[i];}
93 }
94
95 // do the recombination between A and B
96 jetA = & briefjets[diJ_min_jet];
97 // GPS mod 2009-02-11
98 //jetB = jetA->NN;
99 jetB = static_cast<BJ *>(jetA->NN);
100 // put the normalisation back in
101 diJ_min *= _invR2;
102 if (jetB != NULL) {
103 // jet-jet recombination
104 // If necessary relabel A & B to ensure jetB < jetA, that way if
105 // the larger of them == newtail then that ends up being jetA and
106 // the new jet that is added as jetB is inserted in a position that
107 // has a future!
108 if (jetA < jetB) {std::swap(jetA,jetB);}
109
110 int nn; // new jet index
111 _do_ij_recombination_step(jetA->_jets_index, jetB->_jets_index, diJ_min, nn);
112
113 // what was jetB will now become the new jet
114 _bj_set_jetinfo(jetB, nn);
115
116 } else {
117 // jet-beam recombination
118 _do_iB_recombination_step(jetA->_jets_index, diJ_min);
119 }
120
121 // now update our nearest neighbour info and diJ table
122 // first reduce size of table
123 tail--; n--;
124 // Copy last jet contents and diJ info into position of jetA
125 *jetA = *tail;
126 diJ[jetA - head] = diJ[tail-head];
127
128 // Initialise jetB's NN distance as well as updating it for
129 // other particles.
130
131 // There are several potential variants of the algorithm below for
132 // updating the NN info and diJ table. Differences affect cases
133 // where jetB is null. Numbers below are for antikt, R=0.4,
134 // Nparticles=50, cf. top-level/development-notes/2025-03-speed and
135 // the percent number indicates the change in runtime relative to
136 // "original" (more negative is better)
137 //
138 // 0: original, i.e. up to FJ version 3.4.3, good compromise in normal conditions, pathological for very small R (cf. 2503.08146)
139 // 1: add extra non-null jetB test in loop intel/linux:+1%, M2Pro/mac:+2%
140 // 2: two independent loops intel/linux:-3%, M2Pro/mac:+2%
141 // 3: proxy jetB_or_A to avoid null test intel/linux:-1%, M2Pro/mac:-1%
142 //
143 // Note that on intel/linux/gcc(14.2) -O2 v. -O3 tends to be a small difference
144 // while on M2Pro/mac/clang(17) -O3 is about 10% faster than -O2, while gcc is 7% faster and less sensitive to -O2 v. -O3
145 //
146 // We settle on strategy 2 for the 3.5.X series, starting 2025-05, on the
147 // assumption that intel/linux is the dominant platform for most
148 // very long runs with FJ.
149#define FJ_N2_UPDATE_STRATEGY 2
150
151#if FJ_N2_UPDATE_STRATEGY==0
152 for (BJ * jetI = head; jetI != tail; jetI++) {
153 // see if jetI had jetA or jetB as a NN -- if so recalculate the NN
154 if (jetI->NN == jetA || jetI->NN == jetB) {
155 _bj_set_NN_nocross(jetI, head, tail);
156 diJ[jetI-head] = _bj_diJ(jetI); // update diJ
157 }
158 // check whether new jetB is closer than jetI's current NN and
159 // if need be update things
160 if (jetB != NULL) {
161 double dist = _bj_dist(jetI,jetB);
162 if (dist < jetI->NN_dist) {
163 if (jetI != jetB) {
164 jetI->NN_dist = dist;
165 jetI->NN = jetB;
166 diJ[jetI-head] = _bj_diJ(jetI); // update diJ...
167 }
168 }
169 if (dist < jetB->NN_dist) {
170 if (jetI != jetB) {
171 jetB->NN_dist = dist;
172 jetB->NN = jetI;}
173 }
174 }
175 // if jetI's NN is the new tail then relabel it so that it becomes jetA
176 if (jetI->NN == tail) {jetI->NN = jetA;}
177 }
178 if (jetB != NULL) {diJ[jetB-head] = _bj_diJ(jetB);}
179#elif FJ_N2_UPDATE_STRATEGY==1
180 for (BJ * jetI = head; jetI != tail; jetI++) {
181 // see if jetI had jetA or jetB as a NN -- if so recalculate the NN
182 if (jetI->NN == jetA || (jetB && jetI->NN == jetB)) {
183 _bj_set_NN_nocross(jetI, head, tail);
184 diJ[jetI-head] = _bj_diJ(jetI); // update diJ
185 }
186 // check whether new jetB is closer than jetI's current NN and
187 // if need be update things
188 if (jetB != NULL) {
189 double dist = _bj_dist(jetI,jetB);
190 if (dist < jetI->NN_dist) {
191 if (jetI != jetB) {
192 jetI->NN_dist = dist;
193 jetI->NN = jetB;
194 diJ[jetI-head] = _bj_diJ(jetI); // update diJ...
195 }
196 }
197 if (dist < jetB->NN_dist) {
198 if (jetI != jetB) {
199 jetB->NN_dist = dist;
200 jetB->NN = jetI;}
201 }
202 }
203 // if jetI's NN is the new tail then relabel it so that it becomes jetA
204 if (jetI->NN == tail) {jetI->NN = jetA;}
205 }
206 if (jetB != NULL) {diJ[jetB-head] = _bj_diJ(jetB);}
207#elif FJ_N2_UPDATE_STRATEGY==2
208 if (jetB != NULL){
209 for (BJ * jetI = head; jetI != tail; jetI++) {
210 // see if jetI had jetA or jetB as a NN -- if so recalculate the NN
211 if (jetI->NN == jetA || jetI->NN == jetB) {
212 _bj_set_NN_nocross(jetI, head, tail);
213 diJ[jetI-head] = _bj_diJ(jetI); // update diJ
214 }
215 // check whether new jetB is closer than jetI's current NN and
216 // if need be update things
217 double dist = _bj_dist(jetI,jetB);
218 if (dist < jetI->NN_dist) {
219 if (jetI != jetB) {
220 jetI->NN_dist = dist;
221 jetI->NN = jetB;
222 diJ[jetI-head] = _bj_diJ(jetI); // update diJ...
223 }
224 }
225 // and check whether jetI is potentially jetB's NN
226 if (dist < jetB->NN_dist) {
227 if (jetI != jetB) {
228 jetB->NN_dist = dist;
229 jetB->NN = jetI;}
230 }
231 // if jetI's NN is the new tail then relabel it so that it becomes jetA
232 if (jetI->NN == tail) {jetI->NN = jetA;}
233 }
234 diJ[jetB-head] = _bj_diJ(jetB);
235 } else {
236 // case where jetB == NULL
237 for (BJ * jetI = head; jetI != tail; jetI++) {
238 // see if jetI had jetA as a NN -- if so recalculate the NN
239 if (jetI->NN == jetA) {
240 _bj_set_NN_nocross(jetI, head, tail);
241 diJ[jetI-head] = _bj_diJ(jetI); // update diJ
242 }
243 // if jetI's NN is the new tail then relabel it so that it becomes jetA
244 if (jetI->NN == tail) {jetI->NN = jetA;}
245 }
246 }
247#elif FJ_N2_UPDATE_STRATEGY==3
248 auto jetB_or_A = jetB ? jetB : jetA;
249 for (BJ * jetI = head; jetI != tail; jetI++) {
250 // see if jetI had jetA or jetB as a NN -- if so recalculate the NN
251 if (jetI->NN == jetA || jetI->NN == jetB_or_A) {
252 _bj_set_NN_nocross(jetI, head, tail);
253 diJ[jetI-head] = _bj_diJ(jetI); // update diJ
254 }
255 // check whether new jetB is closer than jetI's current NN and
256 // if need be update things
257 if (jetB != NULL) {
258 double dist = _bj_dist(jetI,jetB);
259 if (dist < jetI->NN_dist) {
260 if (jetI != jetB) {
261 jetI->NN_dist = dist;
262 jetI->NN = jetB;
263 diJ[jetI-head] = _bj_diJ(jetI); // update diJ...
264 }
265 }
266 if (dist < jetB->NN_dist) {
267 if (jetI != jetB) {
268 jetB->NN_dist = dist;
269 jetB->NN = jetI;}
270 }
271 }
272 // if jetI's NN is the new tail then relabel it so that it becomes jetA
273 if (jetI->NN == tail) {jetI->NN = jetA;}
274 }
275 if (jetB != NULL) {diJ[jetB-head] = _bj_diJ(jetB);}
276#else
277#error "unknown N2 update strategy"
278#endif
279#undef FJ_N2_UPDATE_STRATEGY
280 }
281
282
283 // final cleaning up;
284 delete[] diJ;
285 delete[] briefjets;
286}
287
288
289
290// //----------------------------------------------------------------------
291// // initialises a GenBriefJet
292// template<> inline void ClusterSequence::_bj_set_jetinfo(
293// GenBriefJet * const jetA, const int _jets_index) const {
294//
295// jetA->init(_jets[_jets_index]);
296// jetA->_jets_index = _jets_index;
297//
298// }
299//
300//
301// //----------------------------------------------------------------------
302// // returns the distance between two GenBriefJets
303// template<> double ClusterSequence::_bj_dist(
304// const GenBriefJet * const jeta,
305// const GenBriefJet * const jetb) const {
306// return jeta->geom_ij(jetb);
307// }
308
309FASTJET_END_NAMESPACE
310
311#endif // __FASTJET_CLUSTERQUENCE_N2_ICC__
312
313