Fork me on GitHub

source: git/external/fastjet/contribs/RecursiveTools/RecursiveSymmetryCutBase.cc@ 91eef4d

Last change on this file since 91eef4d was b7b836a, checked in by Pavel Demin <pavel-demin@…>, 6 years ago

update FastJet library to 3.3.1 and FastJet Contrib library to 1.036

  • Property mode set to 100644
File size: 24.1 KB
Line 
1// $Id: RecursiveSymmetryCutBase.cc 1080 2017-09-28 07:51:37Z gsoyez $
2//
3// Copyright (c) 2014-, Gavin P. Salam, Gregory Soyez, Jesse Thaler
4//
5//----------------------------------------------------------------------
6// This file is part of FastJet contrib.
7//
8// It is free software; you can redistribute it and/or modify it under
9// the terms of the GNU General Public License as published by the
10// Free Software Foundation; either version 2 of the License, or (at
11// your option) any later version.
12//
13// It is distributed in the hope that it will be useful, but WITHOUT
14// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16// License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this code. If not, see <http://www.gnu.org/licenses/>.
20//----------------------------------------------------------------------
21
22#include "RecursiveSymmetryCutBase.hh"
23#include "fastjet/JetDefinition.hh"
24#include "fastjet/ClusterSequenceAreaBase.hh"
25#include <cassert>
26#include <algorithm>
27#include <cstdlib>
28
29using namespace std;
30
31FASTJET_BEGIN_NAMESPACE // defined in fastjet/internal/base.hh
32
33namespace contrib{
34
35LimitedWarning RecursiveSymmetryCutBase::_negative_mass_warning;
36LimitedWarning RecursiveSymmetryCutBase::_mu2_gt1_warning;
37//LimitedWarning RecursiveSymmetryCutBase::_nonca_warning;
38LimitedWarning RecursiveSymmetryCutBase::_explicit_ghost_warning;
39
40bool RecursiveSymmetryCutBase::_verbose = false;
41
42//----------------------------------------------------------------------
43PseudoJet RecursiveSymmetryCutBase::result(const PseudoJet & jet) const {
44 // construct the input jet (by default, recluster with C/A)
45 if (! jet.has_constituents()){
46 throw Error("RecursiveSymmetryCutBase can only be applied to jets with constituents");
47 }
48
49 PseudoJet j = _recluster_if_needed(jet);
50
51 // sanity check: the jet must have a valid CS
52 if (! j.has_valid_cluster_sequence()){
53 throw Error("RecursiveSymmetryCutBase can only be applied to jets associated to a (valid) cluster sequence");
54 }
55
56 // check that area information is there in case we have a subtractor
57 // GS: do we really need this since subtraction may not require areas?
58 if (_subtractor) {
59 const ClusterSequenceAreaBase * csab =
60 dynamic_cast<const ClusterSequenceAreaBase *>(j.associated_cs());
61 if (csab == 0 || (!csab->has_explicit_ghosts()))
62 _explicit_ghost_warning.warn("RecursiveSymmetryCutBase: there is no clustering sequence, or it lacks explicit ghosts: subtraction is not guaranteed to function properly");
63 }
64
65 // establish the first subjet and optionally subtract it
66 PseudoJet subjet = j;
67 if (_subtractor && (!_input_jet_is_subtracted)) {
68 subjet = (*_subtractor)(subjet);
69 }
70
71 // variables for tracking what will happen
72 PseudoJet piece1, piece2;
73
74 // vectors for storing optional verbose structure
75 // these hold the deltaR, symmetry, and mu values of dropped branches
76 std::vector<double> dropped_delta_R;
77 std::vector<double> dropped_symmetry;
78 std::vector<double> dropped_mu;
79
80 double sym, mu2;
81
82 // now recurse into the jet's structure
83 RecursionStatus status;
84 while ((status=recurse_one_step(subjet, piece1, piece2, sym, mu2)) != recursion_success) {
85 // start with sanity checks:
86 if ((status == recursion_issue) || (status == recursion_no_parents)) {
87 // we should return piece1 by our convention for recurse_one_step
88 PseudoJet result;
89 if (status == recursion_issue){
90 result = piece1;
91 if (_verbose) cout << "reached end; returning null jet " << endl;
92 } else {
93 result = _result_no_substructure(piece1);
94 if (_verbose) cout << "no parents found; returning last PJ or empty jet" << endl;
95 }
96
97 if (result != 0) {
98 // if in grooming mode, add dummy structure information
99 StructureType * structure = new StructureType(result);
100 // structure->_symmetry = 0.0;
101 // structure->_mu = 0.0;
102 // structure->_delta_R = 0.0;
103 if (_verbose_structure) { // still want to store verbose information about dropped branches
104 structure->_has_verbose = true;
105 structure->_dropped_symmetry = dropped_symmetry;
106 structure->_dropped_mu = dropped_mu;
107 structure->_dropped_delta_R = dropped_delta_R;
108 }
109 result.set_structure_shared_ptr(SharedPtr<PseudoJetStructureBase>(structure));
110 }
111
112 return result;
113 }
114
115 assert(status == recursion_dropped);
116
117 // if desired, store information about dropped branches before recursing
118 if (_verbose_structure) {
119 dropped_delta_R.push_back(piece1.delta_R(piece2));
120 dropped_symmetry.push_back(sym);
121 dropped_mu.push_back((mu2 >= 0) ? sqrt(mu2) : -sqrt(-mu2));
122 }
123
124 subjet = piece1;
125 }
126
127
128 // we've tagged the splitting, return the jet with its substructure
129 StructureType * structure = new StructureType(subjet);
130 structure->_symmetry = sym;
131 structure->_mu = (mu2 >= 0) ? sqrt(mu2) : -sqrt(-mu2);
132 structure->_delta_R = sqrt(squared_geometric_distance(piece1, piece2));
133 if (_verbose_structure) {
134 structure->_has_verbose = true;
135 structure->_dropped_symmetry = dropped_symmetry;
136 structure->_dropped_mu = dropped_mu;
137 structure->_dropped_delta_R = dropped_delta_R;
138 }
139 subjet.set_structure_shared_ptr(SharedPtr<PseudoJetStructureBase>(structure));
140 return subjet;
141}
142
143
144
145//----------------------------------------------------------------------
146// the method below is the one actually performing one step of the
147// recursion.
148//
149// It returns a status code (defined above)
150//
151// In case of success, all the information is filled
152// In case of "no parents", piee1 is the same subjet
153// In case of trouble, piece2 will be a 0 PJ and piece1 is the PJ we
154// should return (either 0 itself if the issue was critical, or
155// non-wero in case of a minor issue just causing the recursion to
156// stop)
157RecursiveSymmetryCutBase::RecursionStatus
158 RecursiveSymmetryCutBase::recurse_one_step(const PseudoJet & subjet,
159 PseudoJet &piece1, PseudoJet &piece2,
160 double &sym, double &mu2,
161 void *extra_parameters) const {
162 if (!subjet.has_parents(piece1, piece2)){
163 piece1 = subjet;
164 piece2 = PseudoJet();
165 return recursion_no_parents;
166 }
167
168 // first sanity check:
169 // - zero or negative pts are not allowed for the input subjet
170 // - zero or negative masses are not allowed for configurations
171 // in which the mass will effectively appear in a denominator
172 // (The masses will be checked later)
173 if (subjet.pt2() <= 0){ // this is a critical problem, return an empty PJ
174 piece1 = piece2 = PseudoJet();
175 return recursion_issue;
176 }
177
178 if (_subtractor) {
179 piece1 = (*_subtractor)(piece1);
180 piece2 = (*_subtractor)(piece2);
181 }
182
183 // determine the symmetry parameter
184 if (_symmetry_measure == y) {
185 // the original d_{ij}/m^2 choice from MDT
186 // first make sure the mass is sensible
187 if (subjet.m2() <= 0) {
188 _negative_mass_warning.warn("RecursiveSymmetryCutBase: cannot calculate y, because (sub)jet mass is negative; bailing out");
189 // since rounding errors can give -ve masses, be a it more
190 // tolerant and consider that no substructure has been found
191 piece1 = _result_no_substructure(subjet);
192 piece2 = PseudoJet();
193 return recursion_issue;
194 }
195 sym = piece1.kt_distance(piece2) / subjet.m2();
196
197 } else if (_symmetry_measure == vector_z) {
198 // min(pt1, pt2)/(pt), where the denominator is a vector sum
199 // of the two subjets
200 sym = min(piece1.pt(), piece2.pt()) / subjet.pt();
201 } else if (_symmetry_measure == scalar_z) {
202 // min(pt1, pt2)/(pt1+pt2), where the denominator is a scalar sum
203 // of the two subjets
204 double pt1 = piece1.pt();
205 double pt2 = piece2.pt();
206 // make sure denominator is non-zero
207 sym = pt1 + pt2;
208 if (sym == 0){ // this is a critical problem, return an empty PJ
209 piece1 = piece2 = PseudoJet();
210 return recursion_issue;
211 }
212 sym = min(pt1, pt2) / sym;
213 } else if ((_symmetry_measure == theta_E) || (_symmetry_measure == cos_theta_E)){
214 // min(E1, E2)/(E1+E2)
215 double E1 = piece1.E();
216 double E2 = piece2.E();
217 // make sure denominator is non-zero
218 sym = E1 + E2;
219 if (sym == 0){ // this is a critical problem, return an empty PJ
220 piece1 = piece2 = PseudoJet();
221 return recursion_issue;
222 }
223 sym = min(E1, E2) / sym;
224 } else {
225 throw Error ("Unrecognized choice of symmetry_measure");
226 }
227
228 // determine the symmetry cut
229 // (This function is specified in the derived classes)
230 double this_symmetry_cut = symmetry_cut_fn(piece1, piece2, extra_parameters);
231
232 // and make a first tagging decision based on symmetry cut
233 bool tagged = (sym > this_symmetry_cut);
234
235 // if tagged based on symmetry cut, then check the mu cut (if relevant)
236 // and update the tagging decision. Calculate mu^2 regardless, for cases
237 // of users not cutting on mu2, but still interested in its value.
238 bool use_mu_cut = (_mu_cut != numeric_limits<double>::infinity());
239 mu2 = max(piece1.m2(), piece2.m2())/subjet.m2();
240 if (tagged && use_mu_cut) {
241 // first a sanity check -- mu2 won't be sensible if the subjet mass
242 // is negative, so we can't then trust the mu cut - bail out
243 if (subjet.m2() <= 0) {
244 _negative_mass_warning.warn("RecursiveSymmetryCutBase: cannot trust mu, because (sub)jet mass is negative; bailing out");
245 piece1 = piece2 = PseudoJet();
246 return recursion_issue;
247 }
248 if (mu2 > 1) _mu2_gt1_warning.warn("RecursiveSymmetryCutBase encountered mu^2 value > 1");
249 if (mu2 > pow(_mu_cut,2)) tagged = false;
250 }
251
252 // we'll continue unclustering, allowing for the different
253 // ways of choosing which parent to look into
254 if (_recursion_choice == larger_pt) {
255 if (piece1.pt2() < piece2.pt2()) std::swap(piece1, piece2);
256 } else if (_recursion_choice == larger_mt) {
257 if (piece1.mt2() < piece2.mt2()) std::swap(piece1, piece2);
258 } else if (_recursion_choice == larger_m) {
259 if (piece1.m2() < piece2.m2()) std::swap(piece1, piece2);
260 } else if (_recursion_choice == larger_E) {
261 if (piece1.E() < piece2.E()) std::swap(piece1, piece2);
262 } else {
263 throw Error ("Unrecognized value for recursion_choice");
264 }
265
266 return tagged ? recursion_success : recursion_dropped;
267}
268
269
270//----------------------------------------------------------------------
271string RecursiveSymmetryCutBase::description() const {
272 ostringstream ostr;
273 ostr << "Recursive " << (_grooming_mode ? "Groomer" : "Tagger") << " with a symmetry cut ";
274
275 switch(_symmetry_measure) {
276 case y:
277 ostr << "y"; break;
278 case scalar_z:
279 ostr << "scalar_z"; break;
280 case vector_z:
281 ostr << "vector_z"; break;
282 case theta_E:
283 ostr << "theta_E"; break;
284 case cos_theta_E:
285 ostr << "cos_theta_E"; break;
286 default:
287 cerr << "failed to interpret symmetry_measure" << endl; exit(-1);
288 }
289 ostr << " > " << symmetry_cut_description();
290
291 if (_mu_cut != numeric_limits<double>::infinity()) {
292 ostr << ", mass-drop cut mu=max(m1,m2)/m < " << _mu_cut;
293 } else {
294 ostr << ", no mass-drop requirement";
295 }
296
297 ostr << ", recursion into the subjet with larger ";
298 switch(_recursion_choice) {
299 case larger_pt:
300 ostr << "pt"; break;
301 case larger_mt:
302 ostr << "mt(=sqrt(m^2+pt^2))"; break;
303 case larger_m:
304 ostr << "mass"; break;
305 case larger_E:
306 ostr << "energy"; break;
307 default:
308 cerr << "failed to interpret recursion_choice" << endl; exit(-1);
309 }
310
311 if (_subtractor) {
312 ostr << ", subtractor: " << _subtractor->description();
313 if (_input_jet_is_subtracted) {ostr << " (input jet is assumed already subtracted)";}
314 }
315
316 if (_recluster) {
317 ostr << " and reclustering using " << _recluster->description();
318 }
319
320 return ostr.str();
321}
322
323//----------------------------------------------------------------------
324// helper for handling the reclustering
325PseudoJet RecursiveSymmetryCutBase::_recluster_if_needed(const PseudoJet &jet) const{
326 if (! _do_reclustering) return jet;
327 if (_recluster) return (*_recluster)(jet);
328 if (is_ee()){
329#if FASTJET_VERSION_NUMBER >= 30100
330 return Recluster(JetDefinition(ee_genkt_algorithm, JetDefinition::max_allowable_R, 0.0), true)(jet);
331#else
332 return Recluster(JetDefinition(ee_genkt_algorithm, JetDefinition::max_allowable_R, 0.0))(jet);
333#endif
334 }
335
336 return Recluster(cambridge_algorithm, JetDefinition::max_allowable_R)(jet);
337}
338
339//----------------------------------------------------------------------
340// decide what to return when no substructure has been found
341double RecursiveSymmetryCutBase::squared_geometric_distance(const PseudoJet &j1,
342 const PseudoJet &j2) const{
343 if (_symmetry_measure == theta_E){
344 double dot_3d = j1.px()*j2.px() + j1.py()*j2.py() + j1.pz()*j2.pz();
345 double cos_theta = max(-1.0,min(1.0, dot_3d/sqrt(j1.modp2()*j2.modp2())));
346 double theta = acos(cos_theta);
347 return theta*theta;
348 } else if (_symmetry_measure == cos_theta_E){
349 double dot_3d = j1.px()*j2.px() + j1.py()*j2.py() + j1.pz()*j2.pz();
350 return max(0.0, 2*(1-dot_3d/sqrt(j1.modp2()*j2.modp2())));
351 }
352
353 return j1.squared_distance(j2);
354}
355
356//----------------------------------------------------------------------
357PseudoJet RecursiveSymmetryCutBase::_result_no_substructure(const PseudoJet &last_parent) const{
358 if (_grooming_mode){
359 // in grooming mode, return the last parent
360 return last_parent;
361 } else {
362 // in tagging mode, return an empty PseudoJet
363 return PseudoJet();
364 }
365}
366
367
368//========================================================================
369// implementation of the details of the structure
370
371// the number of dropped subjets
372int RecursiveSymmetryCutBase::StructureType::dropped_count(bool global) const {
373 check_verbose("dropped_count()");
374
375 // if this jet has no substructure, just return an empty vector
376 if (!has_substructure()) return _dropped_delta_R.size();
377
378 // deal with the non-global case
379 if (!global) return _dropped_delta_R.size();
380
381 // for the global case, we've unfolded the recursion (likely more
382 // efficient as it requires less copying)
383 unsigned int count = 0;
384 vector<const RecursiveSymmetryCutBase::StructureType*> to_parse;
385 to_parse.push_back(this);
386
387 unsigned int i_parse = 0;
388 while (i_parse<to_parse.size()){
389 const RecursiveSymmetryCutBase::StructureType *current = to_parse[i_parse];
390 count += current->_dropped_delta_R.size();
391
392 // check if we need to recurse deeper in the substructure
393 //
394 // we can have 2 situations here for the underlying structure (the
395 // one we've wrapped around):
396 // - it's of the clustering type
397 // - it's a composite jet
398 // only in the 2nd case do we have to recurse deeper
399 const CompositeJetStructure *css = dynamic_cast<const CompositeJetStructure*>(current->_structure.get());
400 if (css == 0){ ++i_parse; continue; }
401
402 vector<PseudoJet> prongs = css->pieces(PseudoJet()); // argument irrelevant
403 assert(prongs.size() == 2);
404 for (unsigned int i_prong=0; i_prong<2; ++i_prong){
405 if (prongs[i_prong].has_structure_of<RecursiveSymmetryCutBase>()){
406 RecursiveSymmetryCutBase::StructureType* prong_structure
407 = (RecursiveSymmetryCutBase::StructureType*) prongs[i_prong].structure_ptr();
408 if (prong_structure->has_substructure())
409 to_parse.push_back(prong_structure);
410 }
411 }
412
413 ++i_parse;
414 }
415 return count;
416}
417
418// the delta_R of all the dropped subjets
419vector<double> RecursiveSymmetryCutBase::StructureType::dropped_delta_R(bool global) const {
420 check_verbose("dropped_delta_R()");
421
422 // if this jet has no substructure, just return an empty vector
423 if (!has_substructure()) return vector<double>();
424
425 // deal with the non-global case
426 if (!global) return _dropped_delta_R;
427
428 // for the global case, we've unfolded the recursion (likely more
429 // efficient as it requires less copying)
430 vector<double> all_dropped;
431 vector<const RecursiveSymmetryCutBase::StructureType*> to_parse;
432 to_parse.push_back(this);
433
434 unsigned int i_parse = 0;
435 while (i_parse<to_parse.size()){
436 const RecursiveSymmetryCutBase::StructureType *current = to_parse[i_parse];
437 all_dropped.insert(all_dropped.end(), current->_dropped_delta_R.begin(), current->_dropped_delta_R.end());
438
439 // check if we need to recurse deeper in the substructure
440 //
441 // we can have 2 situations here for the underlying structure (the
442 // one we've wrapped around):
443 // - it's of the clustering type
444 // - it's a composite jet
445 // only in the 2nd case do we have to recurse deeper
446 const CompositeJetStructure *css = dynamic_cast<const CompositeJetStructure*>(current->_structure.get());
447 if (css == 0){ ++i_parse; continue; }
448
449 vector<PseudoJet> prongs = css->pieces(PseudoJet()); // argument irrelevant
450 assert(prongs.size() == 2);
451 for (unsigned int i_prong=0; i_prong<2; ++i_prong){
452 if (prongs[i_prong].has_structure_of<RecursiveSymmetryCutBase>()){
453 RecursiveSymmetryCutBase::StructureType* prong_structure
454 = (RecursiveSymmetryCutBase::StructureType*) prongs[i_prong].structure_ptr();
455 if (prong_structure->has_substructure())
456 to_parse.push_back(prong_structure);
457 }
458 }
459
460 ++i_parse;
461 }
462 return all_dropped;
463}
464
465// the symmetry of all the dropped subjets
466vector<double> RecursiveSymmetryCutBase::StructureType::dropped_symmetry(bool global) const {
467 check_verbose("dropped_symmetry()");
468
469 // if this jet has no substructure, just return an empty vector
470 if (!has_substructure()) return vector<double>();
471
472 // deal with the non-global case
473 if (!global) return _dropped_symmetry;
474
475 // for the global case, we've unfolded the recursion (likely more
476 // efficient as it requires less copying)
477 vector<double> all_dropped;
478 vector<const RecursiveSymmetryCutBase::StructureType*> to_parse;
479 to_parse.push_back(this);
480
481 unsigned int i_parse = 0;
482 while (i_parse<to_parse.size()){
483 const RecursiveSymmetryCutBase::StructureType *current = to_parse[i_parse];
484 all_dropped.insert(all_dropped.end(), current->_dropped_symmetry.begin(), current->_dropped_symmetry.end());
485
486 // check if we need to recurse deeper in the substructure
487 //
488 // we can have 2 situations here for the underlying structure (the
489 // one we've wrapped around):
490 // - it's of the clustering type
491 // - it's a composite jet
492 // only in the 2nd case do we have to recurse deeper
493 const CompositeJetStructure *css = dynamic_cast<const CompositeJetStructure*>(current->_structure.get());
494 if (css == 0){ ++i_parse; continue; }
495
496 vector<PseudoJet> prongs = css->pieces(PseudoJet()); // argument irrelevant
497 assert(prongs.size() == 2);
498 for (unsigned int i_prong=0; i_prong<2; ++i_prong){
499 if (prongs[i_prong].has_structure_of<RecursiveSymmetryCutBase>()){
500 RecursiveSymmetryCutBase::StructureType* prong_structure
501 = (RecursiveSymmetryCutBase::StructureType*) prongs[i_prong].structure_ptr();
502 if (prong_structure->has_substructure())
503 to_parse.push_back(prong_structure);
504 }
505 }
506
507 ++i_parse;
508 }
509 return all_dropped;
510}
511
512// the mu of all the dropped subjets
513vector<double> RecursiveSymmetryCutBase::StructureType::dropped_mu(bool global) const {
514 check_verbose("dropped_mu()");
515
516 // if this jet has no substructure, just return an empty vector
517 if (!has_substructure()) return vector<double>();
518
519 // deal with the non-global case
520 if (!global) return _dropped_mu;
521
522 // for the global case, we've unfolded the recursion (likely more
523 // efficient as it requires less copying)
524 vector<double> all_dropped;
525 vector<const RecursiveSymmetryCutBase::StructureType*> to_parse;
526 to_parse.push_back(this);
527
528 unsigned int i_parse = 0;
529 while (i_parse<to_parse.size()){
530 const RecursiveSymmetryCutBase::StructureType *current = to_parse[i_parse];
531 all_dropped.insert(all_dropped.end(), current->_dropped_mu.begin(), current->_dropped_mu.end());
532
533 // check if we need to recurse deeper in the substructure
534 //
535 // we can have 2 situations here for the underlying structure (the
536 // one we've wrapped around):
537 // - it's of the clustering type
538 // - it's a composite jet
539 // only in the 2nd case do we have to recurse deeper
540 const CompositeJetStructure *css = dynamic_cast<const CompositeJetStructure*>(current->_structure.get());
541 if (css == 0){ ++i_parse; continue; }
542
543 vector<PseudoJet> prongs = css->pieces(PseudoJet()); // argument irrelevant
544 assert(prongs.size() == 2);
545 for (unsigned int i_prong=0; i_prong<2; ++i_prong){
546 if (prongs[i_prong].has_structure_of<RecursiveSymmetryCutBase>()){
547 RecursiveSymmetryCutBase::StructureType* prong_structure
548 = (RecursiveSymmetryCutBase::StructureType*) prongs[i_prong].structure_ptr();
549 if (prong_structure->has_substructure())
550 to_parse.push_back(prong_structure);
551 }
552 }
553
554 ++i_parse;
555 }
556 return all_dropped;
557}
558
559// the maximum of the symmetry over the dropped subjets
560double RecursiveSymmetryCutBase::StructureType::max_dropped_symmetry(bool global) const {
561 check_verbose("max_dropped_symmetry()");
562
563 // if there is no substructure, just exit
564 if (!has_substructure()){ return 0.0; }
565
566 // local value of the max_dropped_symmetry
567 double local_max = (_dropped_symmetry.size() == 0)
568 ? 0.0 : *max_element(_dropped_symmetry.begin(),_dropped_symmetry.end());
569
570 // recurse down the structure if instructed to do so
571 if (global){
572 // we can have 2 situations here for the underlying structure (the
573 // one we've wrapped around):
574 // - it's of the clustering type
575 // - it's a composite jet
576 // only in the 2nd case do we have to recurse deeper
577 const CompositeJetStructure *css = dynamic_cast<const CompositeJetStructure*>(_structure.get());
578 if (css == 0) return local_max;
579
580 vector<PseudoJet> prongs = css->pieces(PseudoJet()); // argument irrelevant
581 assert(prongs.size() == 2);
582 for (unsigned int i_prong=0; i_prong<2; ++i_prong){
583 // check if the prong has further substructure
584 if (prongs[i_prong].has_structure_of<RecursiveSymmetryCutBase>()){
585 RecursiveSymmetryCutBase::StructureType* prong_structure
586 = (RecursiveSymmetryCutBase::StructureType*) prongs[i_prong].structure_ptr();
587 local_max = max(local_max, prong_structure->max_dropped_symmetry(true));
588 }
589 }
590 }
591
592 return local_max;
593}
594
595//------------------------------------------------------------------------
596// helper class to sort by decreasing thetag
597class SortRecursiveSoftDropStructureZgThetagPair{
598public:
599 bool operator()(const pair<double, double> &p1, const pair<double, double> &p2) const{
600 return p1.second > p2.second;
601 }
602};
603//------------------------------------------------------------------------
604
605// the (zg,thetag) pairs of all the splitting that were found and passed the SD condition
606vector<pair<double,double> > RecursiveSymmetryCutBase::StructureType::sorted_zg_and_thetag() const {
607 //check_verbose("sorted_zg_and_thetag()");
608
609 // if this jet has no substructure, just return an empty vector
610 if (!has_substructure()) return vector<pair<double,double> >();
611
612 // otherwise fill a vector with all the prongs (no specific ordering)
613 vector<pair<double,double> > all;
614 vector<const RecursiveSymmetryCutBase::StructureType*> to_parse;
615 to_parse.push_back(this);
616
617 unsigned int i_parse = 0;
618 while (i_parse<to_parse.size()){
619 const RecursiveSymmetryCutBase::StructureType *current = to_parse[i_parse];
620 all.push_back(pair<double,double>(current->_symmetry, current->_delta_R));
621
622 vector<PseudoJet> prongs = current->pieces(PseudoJet());
623 assert(prongs.size() == 2);
624 for (unsigned int i_prong=0; i_prong<2; ++i_prong){
625 if (prongs[i_prong].has_structure_of<RecursiveSymmetryCutBase>()){
626 RecursiveSymmetryCutBase::StructureType* prong_structure
627 = (RecursiveSymmetryCutBase::StructureType*) prongs[i_prong].structure_ptr();
628 if (prong_structure->has_substructure())
629 to_parse.push_back(prong_structure);
630 }
631 }
632
633 ++i_parse;
634 }
635
636 sort(all.begin(), all.end(), SortRecursiveSoftDropStructureZgThetagPair());
637 return all;
638}
639
640} // namespace contrib
641
642FASTJET_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.