File indexing completed on 2025-08-06 08:17:45
0001 #include "FastJetAlgo.h"
0002
0003 #include "Jet.h"
0004 #include "JetContainer.h"
0005 #include "Jetv2.h"
0006
0007 #include <phool/phool.h>
0008
0009 #include <TClonesArray.h>
0010 #include <TSystem.h>
0011
0012
0013 #include <fastjet/AreaDefinition.hh>
0014 #include <fastjet/ClusterSequence.hh>
0015 #include <fastjet/ClusterSequenceArea.hh>
0016 #include <fastjet/FunctionOfPseudoJet.hh> // for FunctionOfPse...
0017 #include <fastjet/JetDefinition.hh>
0018 #include <fastjet/PseudoJet.hh>
0019 #include <fastjet/Selector.hh>
0020 #include <fastjet/contrib/ConstituentSubtractor.hh>
0021 #include <fastjet/contrib/RecursiveSymmetryCutBase.hh> // for RecursiveSymm...
0022 #include <fastjet/contrib/SoftDrop.hh>
0023 #include <fastjet/tools/BackgroundEstimatorBase.hh>
0024 #include <fastjet/tools/GridMedianBackgroundEstimator.hh>
0025 #include <fastjet/tools/JetMedianBackgroundEstimator.hh>
0026
0027 #include <boost/format.hpp>
0028
0029
0030 #include <cassert>
0031 #include <cmath> // for isfinite
0032 #include <fstream>
0033 #include <iostream>
0034 #include <map> // for _Rb_tree_iterator
0035 #include <memory> // for allocator_traits<>::value_type
0036 #include <string> // for operator<<
0037 #include <utility> // for pair
0038 #include <vector>
0039
0040 FastJetAlgo::FastJetAlgo(const FastJetOptions& options)
0041 : m_opt{options}
0042 {
0043 fastjet::ClusterSequence clusseq;
0044 if (m_opt.verbosity > 0)
0045 {
0046 fastjet::ClusterSequence::print_banner();
0047 }
0048 else
0049 {
0050 std::ostringstream nullstream;
0051 fastjet::ClusterSequence::set_fastjet_banner_stream(&nullstream);
0052 fastjet::ClusterSequence::print_banner();
0053 fastjet::ClusterSequence::set_fastjet_banner_stream(&std::cout);
0054 }
0055 }
0056
0057 void FastJetAlgo::identify(std::ostream& os)
0058 {
0059 os << " FastJetAlgo: ";
0060 if (m_opt.algo == Jet::ANTIKT)
0061 {
0062 os << "ANTIKT r=" << m_opt.jet_R;
0063 }
0064 else if (m_opt.algo == Jet::KT)
0065 {
0066 os << "KT r=" << m_opt.jet_R;
0067 }
0068 else if (m_opt.algo == Jet::CAMBRIDGE)
0069 {
0070 os << "CAMBRIDGE r=" << m_opt.jet_R;
0071 }
0072 os << std::endl;
0073
0074 m_opt.print(os);
0075 }
0076
0077 fastjet::JetDefinition FastJetAlgo::get_fastjet_definition() const
0078 {
0079 if (m_opt.algo == Jet::ANTIKT)
0080 {
0081 return fastjet::JetDefinition(fastjet::antikt_algorithm, m_opt.jet_R, fastjet::E_scheme, fastjet::Best);
0082 }
0083 if (m_opt.algo == Jet::KT)
0084 {
0085 return fastjet::JetDefinition(fastjet::kt_algorithm, m_opt.jet_R, fastjet::E_scheme, fastjet::Best);
0086 }
0087 if (m_opt.algo == Jet::CAMBRIDGE)
0088 {
0089 return fastjet::JetDefinition(fastjet::cambridge_algorithm, m_opt.jet_R, fastjet::E_scheme, fastjet::Best);
0090 }
0091
0092 std::cout << PHWHERE << std::endl;
0093 std::cout << "Warning, no recognized jet clustering algorithm provided in FastJetAlgo" << std::endl
0094 << "defaulting to antikt_algorithm" << std::endl;
0095
0096 return fastjet::JetDefinition(fastjet::antikt_algorithm, m_opt.jet_R, fastjet::E_scheme, fastjet::Best);
0097 }
0098
0099 fastjet::Selector FastJetAlgo::get_selector() const
0100 {
0101
0102 if (m_opt.use_jet_max_eta && m_opt.use_jet_min_pt)
0103 {
0104 return fastjet::SelectorAbsRapMax(m_opt.jet_max_eta) && fastjet::SelectorPtMin(m_opt.jet_min_pt);
0105 }
0106 if (m_opt.use_jet_max_eta)
0107 {
0108 return fastjet::SelectorAbsRapMax(m_opt.jet_max_eta);
0109 }
0110
0111 return fastjet::SelectorPtMin(m_opt.jet_min_pt);
0112 }
0113
0114 std::vector<fastjet::PseudoJet> FastJetAlgo::cluster_jets(
0115 std::vector<fastjet::PseudoJet>& pseudojets)
0116 {
0117 auto jetdef = get_fastjet_definition();
0118 m_cluseq = new fastjet::ClusterSequence(pseudojets, jetdef);
0119
0120 if (m_opt.use_jet_selection)
0121 {
0122 auto selector = get_selector();
0123 return fastjet::sorted_by_pt(selector(m_cluseq->inclusive_jets()));
0124 }
0125
0126 return fastjet::sorted_by_pt(m_cluseq->inclusive_jets());
0127 }
0128
0129 std::vector<fastjet::PseudoJet> FastJetAlgo::cluster_area_jets(
0130 std::vector<fastjet::PseudoJet>& pseudojets)
0131 {
0132 auto jetdef = get_fastjet_definition();
0133
0134 fastjet::AreaDefinition area_def(
0135 fastjet::active_area_explicit_ghosts,
0136 fastjet::GhostedAreaSpec(m_opt.ghost_max_rap, 1, m_opt.ghost_area));
0137
0138 m_cluseqarea = new fastjet::ClusterSequenceArea(pseudojets, jetdef, area_def);
0139
0140 fastjet::Selector selector = (m_opt.use_jet_selection
0141 ? (!fastjet::SelectorIsPureGhost() && get_selector())
0142 : !fastjet::SelectorIsPureGhost());
0143
0144 return fastjet::sorted_by_pt(selector(m_cluseqarea->inclusive_jets()));
0145 }
0146
0147 float FastJetAlgo::calc_rhomeddens(std::vector<fastjet::PseudoJet>& constituents) const
0148 {
0149 fastjet::AreaDefinition area_def(
0150 fastjet::active_area_explicit_ghosts,
0151 fastjet::GhostedAreaSpec(m_opt.ghost_max_rap, 1, m_opt.ghost_area));
0152
0153 fastjet::Selector rho_select = (!fastjet::SelectorNHardest(m_opt.nhardestcut_jetmedbkgdens)) * fastjet::SelectorAbsEtaMax(m_opt.etahardestcut_jetmedbkgdens);
0154
0155 fastjet::JetDefinition jet_def_bkgd(fastjet::kt_algorithm, m_opt.jet_R);
0156 fastjet::JetMedianBackgroundEstimator bge{rho_select, jet_def_bkgd, area_def};
0157 bge.set_particles(constituents);
0158 return bge.rho();
0159 }
0160
0161 std::vector<fastjet::PseudoJet>
0162 FastJetAlgo::jets_to_pseudojets(std::vector<Jet*>& particles) const
0163 {
0164 std::vector<fastjet::PseudoJet> pseudojets;
0165 for (unsigned int ipart = 0; ipart < particles.size(); ++ipart)
0166 {
0167
0168
0169
0170
0171
0172
0173 if (particles[ipart]->get_e() < m_opt.constituent_min_E)
0174 {
0175 continue;
0176 }
0177 if (!std::isfinite(particles[ipart]->get_px()) ||
0178 !std::isfinite(particles[ipart]->get_py()) ||
0179 !std::isfinite(particles[ipart]->get_pz()) ||
0180 !std::isfinite(particles[ipart]->get_e()))
0181 {
0182 std::cout << PHWHERE << " invalid particle kinematics:"
0183 << " px: " << particles[ipart]->get_px()
0184 << " py: " << particles[ipart]->get_py()
0185 << " pz: " << particles[ipart]->get_pz()
0186 << " e: " << particles[ipart]->get_e() << std::endl;
0187 gSystem->Exit(1);
0188 }
0189 fastjet::PseudoJet pseudojet(particles[ipart]->get_px(),
0190 particles[ipart]->get_py(),
0191 particles[ipart]->get_pz(),
0192 particles[ipart]->get_e());
0193 if (m_opt.use_constituent_min_pt && pseudojet.perp() < m_opt.constituent_min_pt)
0194 {
0195 continue;
0196 }
0197 pseudojet.set_user_index(ipart);
0198 pseudojets.push_back(pseudojet);
0199 }
0200 return pseudojets;
0201 }
0202
0203 void FastJetAlgo::first_call_init(JetContainer* jetcont)
0204 {
0205 m_first_cluster_call = false;
0206 m_opt.initialize();
0207
0208 if (jetcont == nullptr)
0209 {
0210 return;
0211 }
0212
0213 if (m_opt.doSoftDrop)
0214 {
0215 jetcont->add_property({Jet::PROPERTY::prop_zg, Jet::PROPERTY::prop_Rg, Jet::PROPERTY::prop_mu});
0216 m_zg_index = jetcont->property_index(Jet::PROPERTY::prop_zg);
0217 m_Rg_index = jetcont->property_index(Jet::PROPERTY::prop_Rg);
0218 m_mu_index = jetcont->property_index(Jet::PROPERTY::prop_mu);
0219 }
0220
0221 if (m_opt.calc_area)
0222 {
0223 jetcont->add_property(Jet::PROPERTY::prop_area);
0224 m_area_index = jetcont->property_index(Jet::PROPERTY::prop_area);
0225 }
0226
0227 if (m_opt.cs_calc_constsub)
0228 {
0229 cs_bge_rho = new fastjet::GridMedianBackgroundEstimator(m_opt.cs_max_eta, 0.5);
0230 cs_subtractor = new fastjet::contrib::ConstituentSubtractor();
0231 cs_subtractor->set_distance_type(fastjet::contrib::ConstituentSubtractor::deltaR);
0232 cs_subtractor->set_max_distance(m_opt.cs_max_dist);
0233 cs_subtractor->set_alpha(m_opt.cs_alpha);
0234 cs_subtractor->set_ghost_area(m_opt.cs_ghost_area);
0235 cs_subtractor->set_max_eta(m_opt.cs_max_eta);
0236 cs_subtractor->set_background_estimator(cs_bge_rho);
0237 if (m_opt.cs_max_pt > 0)
0238 {
0239 cs_sel_max_pt = new fastjet::Selector(fastjet::SelectorPtMax(m_opt.cs_max_pt));
0240 cs_subtractor->set_particle_selector(cs_sel_max_pt);
0241 }
0242 cs_subtractor->initialize();
0243 if (m_opt.verbosity > 1)
0244 {
0245 std::cout << cs_subtractor->description() << std::endl;
0246 }
0247 }
0248
0249 jetcont->set_algo(m_opt.algo);
0250 jetcont->set_jetpar_R(m_opt.jet_R);
0251 }
0252
0253 void FastJetAlgo::cluster_and_fill(std::vector<Jet*>& particles, JetContainer* jetcont)
0254 {
0255 if (m_first_cluster_call)
0256 {
0257 first_call_init(jetcont);
0258 }
0259
0260
0261 if (m_opt.verbosity > 1)
0262 {
0263 std::cout << " Verbosity>1 FastJetAlgo::process_event -- entered" << std::endl;
0264 }
0265 if (m_opt.verbosity > 8)
0266 {
0267 std::cout << " Verbosity>8 #input particles: " << particles.size() << std::endl;
0268 }
0269
0270
0271 auto pseudojets = jets_to_pseudojets(particles);
0272
0273
0274 if (m_opt.cs_calc_constsub)
0275 {
0276 if (m_opt.verbosity > 100)
0277 {
0278 std::cout << " Before Constituent Subtraction: " << std::endl;
0279 int i = 0;
0280 double sumpt = 0.;
0281 for (const auto& c : pseudojets)
0282 {
0283 sumpt += c.perp();
0284 if (i < 100)
0285 {
0286 std::cout << (boost::format(" jet[%2i] %8.4f sum %8.4f") % i % c.perp() % sumpt).str() << std::endl;
0287 }
0288 i++;
0289 }
0290 auto _c = pseudojets.back();
0291 std::cout << (boost::format(" jet[%2i] %8.4f sum %8.4f") % i++ % _c.perp() % sumpt).str() << std::endl
0292 << std::endl;
0293 }
0294
0295 pseudojets = fastjet::SelectorAbsEtaMax(m_opt.cs_max_eta)(pseudojets);
0296 cs_bge_rho->set_particles(pseudojets);
0297 auto subtracted_pseudojets = cs_subtractor->subtract_event(pseudojets);
0298 pseudojets = std::move(subtracted_pseudojets);
0299
0300 if (m_opt.verbosity > 100)
0301 {
0302 std::cout << " After Constituent Subtraction: " << std::endl;
0303 int i = 0;
0304 double sumpt = 0.;
0305 for (const auto& c : pseudojets)
0306 {
0307 sumpt += c.perp();
0308 if (i < 100)
0309 {
0310 std::cout << (boost::format(" jet[%2i] %8.4f sum %8.4f") % i % c.perp() % sumpt).str() << std::endl;
0311 }
0312 i++;
0313 }
0314 auto _c = pseudojets.back();
0315 std::cout << (boost::format(" jet[%2i] %8.4f sum %8.4f") % i++ % _c.perp() % sumpt).str() << std::endl
0316 << std::endl;
0317 }
0318 }
0319
0320 if (m_opt.calc_jetmedbkgdens)
0321 {
0322 jetcont->set_rho_median(calc_rhomeddens(pseudojets));
0323 }
0324
0325 auto fastjets = (m_opt.calc_area ? cluster_area_jets(pseudojets) : cluster_jets(pseudojets));
0326
0327 if (m_opt.verbosity > 8)
0328 {
0329 std::cout << " Verbosity>8 fastjets: " << fastjets.size() << std::endl;
0330 }
0331 for (unsigned int ijet = 0; ijet < fastjets.size(); ++ijet)
0332 {
0333 auto* jet = jetcont->add_jet();
0334 jet->set_px(fastjets[ijet].px());
0335 jet->set_py(fastjets[ijet].py());
0336 jet->set_pz(fastjets[ijet].pz());
0337 jet->set_e(fastjets[ijet].e());
0338 jet->set_id(ijet);
0339
0340 if (m_opt.calc_area)
0341 {
0342 jet->set_property(m_area_index, fastjets[ijet].area());
0343 }
0344
0345
0346
0347
0348 if (m_opt.doSoftDrop && fastjets[ijet].perp() > 5)
0349 {
0350 fastjet::contrib::SoftDrop sd(m_opt.SD_beta, m_opt.SD_zcut);
0351 if (m_opt.verbosity > 5)
0352 {
0353 std::cout << "FastJetAlgo::get_jets : created SoftDrop groomer configuration : "
0354 << sd.description() << std::endl;
0355 }
0356
0357 fastjet::PseudoJet sd_jet = sd(fastjets[ijet]);
0358
0359 if (m_opt.verbosity > 5)
0360 {
0361 std::cout << "original jet: pt / eta / phi / m = " << fastjets[ijet].perp()
0362 << " / " << fastjets[ijet].eta() << " / " << fastjets[ijet].phi() << " / "
0363 << fastjets[ijet].m() << std::endl;
0364 std::cout << "SoftDropped jet: pt / eta / phi / m = " << sd_jet.perp() << " / "
0365 << sd_jet.eta() << " / " << sd_jet.phi() << " / " << sd_jet.m() << std::endl;
0366
0367 std::cout << " delta_R between subjets: " << sd_jet.structure_of<fastjet::contrib::SoftDrop>().delta_R() << std::endl;
0368 std::cout << " symmetry measure(z): " << sd_jet.structure_of<fastjet::contrib::SoftDrop>().symmetry() << std::endl;
0369 std::cout << " mass drop(mu): " << sd_jet.structure_of<fastjet::contrib::SoftDrop>().mu() << std::endl;
0370 }
0371
0372
0373 jet->set_property(m_zg_index, sd_jet.structure_of<fastjet::contrib::SoftDrop>().symmetry());
0374 jet->set_property(m_Rg_index, sd_jet.structure_of<fastjet::contrib::SoftDrop>().delta_R());
0375 jet->set_property(m_mu_index, sd_jet.structure_of<fastjet::contrib::SoftDrop>().mu());
0376 }
0377
0378
0379
0380 std::vector<fastjet::PseudoJet> constituents = fastjets[ijet].constituents();
0381 if (m_opt.calc_area)
0382 {
0383 for (auto& comp : constituents)
0384 {
0385 if (comp.is_pure_ghost())
0386 {
0387 continue;
0388 }
0389
0390 if (m_opt.save_jet_components)
0391 {
0392 jet->insert_comp(particles[comp.user_index()]->get_comp_vec(), true);
0393 }
0394 }
0395 }
0396 else
0397 {
0398
0399 if (m_opt.save_jet_components)
0400 {
0401 for (auto& comp : constituents)
0402 {
0403 jet->insert_comp(particles[comp.user_index()]->get_comp_vec(), true);
0404 }
0405 }
0406 }
0407 jet->set_comp_sort_flag();
0408 }
0409 if (m_opt.verbosity > 1)
0410 {
0411 std::cout << "FastJetAlgo::process_event -- exited" << std::endl;
0412 }
0413 delete (m_opt.calc_area ? m_cluseqarea : m_cluseq);
0414 }
0415
0416 std::vector<Jet*> FastJetAlgo::get_jets(std::vector<Jet*> particles)
0417 {
0418
0419 auto pseudojets = jets_to_pseudojets(particles);
0420 auto fastjets = cluster_jets(pseudojets);
0421
0422 fastjet::contrib::SoftDrop sd(m_opt.SD_beta, m_opt.SD_zcut);
0423 if (m_opt.verbosity > 5)
0424 {
0425 std::cout << "FastJetAlgo::get_jets : created SoftDrop groomer configuration : " << sd.description() << std::endl;
0426 }
0427
0428
0429 std::vector<Jet*> jets;
0430 for (unsigned int ijet = 0; ijet < fastjets.size(); ++ijet)
0431 {
0432 Jet* jet = new Jetv2();
0433 jet->set_px(fastjets[ijet].px());
0434 jet->set_py(fastjets[ijet].py());
0435 jet->set_pz(fastjets[ijet].pz());
0436 jet->set_e(fastjets[ijet].e());
0437 jet->set_id(ijet);
0438
0439
0440
0441 if (m_opt.doSoftDrop && fastjets[ijet].perp() > 5)
0442 {
0443 fastjet::PseudoJet sd_jet = sd(fastjets[ijet]);
0444
0445 if (m_opt.verbosity > 5)
0446 {
0447 std::cout << "original jet: pt / eta / phi / m = " << fastjets[ijet].perp() << " / " << fastjets[ijet].eta() << " / " << fastjets[ijet].phi() << " / " << fastjets[ijet].m() << std::endl;
0448 std::cout << "SoftDropped jet: pt / eta / phi / m = " << sd_jet.perp() << " / " << sd_jet.eta() << " / " << sd_jet.phi() << " / " << sd_jet.m() << std::endl;
0449
0450 std::cout << " delta_R between subjets: " << sd_jet.structure_of<fastjet::contrib::SoftDrop>().delta_R() << std::endl;
0451 std::cout << " symmetry measure(z): " << sd_jet.structure_of<fastjet::contrib::SoftDrop>().symmetry() << std::endl;
0452 std::cout << " mass drop(mu): " << sd_jet.structure_of<fastjet::contrib::SoftDrop>().mu() << std::endl;
0453 }
0454
0455
0456 jet->set_property(Jet::PROPERTY::prop_zg, sd_jet.structure_of<fastjet::contrib::SoftDrop>().symmetry());
0457 jet->set_property(Jet::PROPERTY::prop_Rg, sd_jet.structure_of<fastjet::contrib::SoftDrop>().delta_R());
0458 jet->set_property(Jet::PROPERTY::prop_mu, sd_jet.structure_of<fastjet::contrib::SoftDrop>().mu());
0459 }
0460
0461
0462
0463 std::vector<fastjet::PseudoJet> constituents = fastjets[ijet].constituents();
0464 if (m_opt.calc_area)
0465 {
0466 for (auto& comp : constituents)
0467 {
0468 if (comp.is_pure_ghost())
0469 {
0470 continue;
0471 }
0472
0473 if (m_opt.save_jet_components)
0474 {
0475 jet->insert_comp(particles[comp.user_index()]->get_comp_vec(), true);
0476 }
0477 }
0478 }
0479 else
0480 {
0481
0482 if (m_opt.save_jet_components)
0483 {
0484 for (auto& comp : constituents)
0485 {
0486 jet->insert_comp(particles[comp.user_index()]->get_comp_vec(), true);
0487 }
0488 }
0489 }
0490
0491 jet->set_comp_sort_flag();
0492
0493
0494 jets.push_back(jet);
0495 }
0496
0497
0498
0499 if (m_opt.verbosity > 1)
0500 {
0501 std::cout << "FastJetAlgo::process_event -- exited" << std::endl;
0502 }
0503
0504 delete m_cluseq;
0505
0506 return jets;
0507 }