ATLAS Offline Software
Loading...
Searching...
No Matches
Pythia8B_i.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5// ======================================================================
6// Pythia8B_i
7// James.Catmore@cern.ch, Eva.Bouhova@cern.ch, Maria.Smizanska@cern.ch (July 2011)
8// Inherits from Pythia8_i (jmonk@hep.ucl.ac.uk)
9// Implementation of repeated hadronization of b-quark events in Pythia8
10// Based on pythia8150/examples/main15.cc in the Pythia8 distribution,
11// T. Sjostrand et al,
12// http://home.thep.lu.se/~torbjorn/pythiaaux/present.html
13// ======================================================================
17#include "AtlasHepMC/GenEvent.h"
18
19// calls to fortran routines
21
22// Pointer to random engine
23CLHEP::HepRandomEngine* Pythia8B_i::p_rndmEngine = nullptr;
24
26// User properties not defined by Pythia8_i
28(const std::string &name, ISvcLocator *pSvcLocator): Pythia8_i(name,pSvcLocator) {
29 declareProperty("NHadronizationLoops", m_had=1);
30 declareProperty("NDecayLoops", m_dec=1);
31 declareProperty("SelectBQuarks",m_selectBQuarks=true);
32 declareProperty("SelectCQuarks",m_selectCQuarks=false);
33 declareProperty("QuarkPtCut", m_qPtCut= 0.0);
34 declareProperty("AntiQuarkPtCut", m_aqPtCut= 0.0);
35 declareProperty("QuarkEtaCut", m_qEtaCut=102.5);
36 declareProperty("AntiQuarkEtaCut", m_aqEtaCut=102.5);
37 declareProperty("RequireBothQuarksPassCuts", m_and=false);
38 declareProperty("VetoDoubleBEvents", m_vetoDoubleB=true);
39 declareProperty("VetoDoubleCEvents", m_vetoDoubleC=true);
40 declareProperty("BPDGCodes", m_bcodes);
41 declareProperty("TriggerPDGCode", m_trigCode=0); // 0 = no selection applied
42 declareProperty("TriggerStatePtCut", m_trigPtCut);
43 declareProperty("TriggerStateEtaCut", m_trigEtaCut=2.5);
44 declareProperty("MinimumCountPerCut", m_cutCount);
45 declareProperty("TriggerInvariantMassCut",m_invMass=0.0);
46 declareProperty("TriggerOppositeCharges",m_oppCharges=false);
47 declareProperty("SignalPDGCodes", m_sigCodes);
48 declareProperty("SignalPtCuts", m_sigPtCuts);
49 declareProperty("SignalEtaCuts", m_sigEtaCuts);
50 declareProperty("NumberOfSignalsRequiredPerEvent", m_nSignalRequired=1);
51 declareProperty("UserSelection", m_userString="NONE");
52 declareProperty("UserSelectionVariables", m_userVar);
53 declareProperty("SuppressSmallPT", m_doSuppressSmallPT=false);
54
55 m_totalBQuark = 0;
57 m_totalCQuark = 0;
60 m_totalHard = 0;
61 m_totalClone = 0;
65 m_speciesCount.clear();
68
69 for (std::vector<int>::iterator iit=m_bcodes.begin(); iit!=m_bcodes.end(); ++iit) {
70 m_speciesCount[*iit] = 0;
71 }
72 m_runinfo = std::make_shared<HepMC3::GenRunInfo>();
73 std::vector<std::string> names = {"Default"};
74 m_runinfo->set_weight_names(names);
75}
76
80
81
83
84 // Logic checks
85 unsigned int trigPtCutsSize = m_trigPtCut.size();
86 if (trigPtCutsSize!=m_cutCount.size()) {
87 ATH_MSG_ERROR("You are requesting " << trigPtCutsSize << " trigger-like pt cuts but are providing required counts for " << m_cutCount.size() << " cuts. This doesn't make sense.");
88 return StatusCode::FAILURE;
89 }
90
91 // This over-rides the genInitialize in the base class Pythia8_i, but then calls it
92 // Sets the built-in UserHook called SuppressLowPT
93 // FOR ONIA USE ONLY
94 ATH_MSG_INFO("genInitialize() from Pythia8B_i");
95
96 bool canSetHook=true;
98 Pythia8_i::m_userHooks=std::vector<std::string>(1, "SuppressSmallPT");
99 }
100
101 if (!canSetHook) {
102 ATH_MSG_ERROR(" *** Unable to initialise PythiaB !! ***");
103 return StatusCode::FAILURE;
104 }
105
106 if(m_userString == "NONE") m_userString.clear();
107 // Call the base class genInitialize()
109
110 ATH_MSG_DEBUG("... seeding Athena random number generator");
111 if (m_useRndmGenSvc){
112 p_rndmEngine = m_atlasRndmEngine->getEngine(); // NOT THREAD-SAFE
113 if (!p_rndmEngine) {
114 ATH_MSG_FATAL("Unable to retrieve HepRandomEngine. Bailing out.");
115 return StatusCode::FAILURE;
116 }
117 }
118 return StatusCode::SUCCESS;
119}
120
121
124
125 // Just counter setting
126 ATH_MSG_INFO("Pythia8B_i from genuserInitialize()");
127
128 // Initialize global counters
129 m_totalBQuark = 0;
131 m_totalCQuark = 0;
134 m_totalHard = 0;
135 m_totalClone = 0;
136 m_atLeastOneAcc = 0;
139 m_speciesCount.clear();
140 for (std::vector<int>::iterator iit=m_bcodes.begin(); iit!=m_bcodes.end(); ++iit) {
141 m_speciesCount[*iit] = 0;
142 }
143
144 return StatusCode::SUCCESS;
145}
146
149
150 ATH_MSG_DEBUG(">>> Pythia8B_i from callGenerator");
151
152 // Check the buffers are empty - if not return so that the GenModule
153 // calls fillEvt again and takes another event from the buffer
154 if (m_BEventBuffer.size()!=0) {
155 ATH_MSG_DEBUG("BEventBuffer not empty; skipping callGenerator");
156 return StatusCode::SUCCESS;
157 }
158
160 //Re-seed the random number stream
161 long seeds[7];
162 const EventContext& ctx = Gaudi::Hive::currentContext();
163 ATHRNG::calculateSeedsMC21(seeds, "PYTHIA8B", ctx.eventID().event_number(), m_dsid, m_randomSeed);
164 m_atlasRndmEngine->getEngine()->setSeeds(seeds, 0); // NOT THREAD-SAFE
165 m_seeds.clear();
166 m_seeds.push_back(seeds[0]);
167 m_seeds.push_back(seeds[1]);
168 }
169
170 // Generator. Shorthand for event.
171 Pythia8::Event& event = Pythia8_i::m_pythia->event;
172
173 // Begin event loop.
174 int iEvent(0);
175 while (iEvent < 1) { // keep going until an event is accepted
176
177 ATH_MSG_DEBUG("Throwing the dice....");
178// if (!Pythia8_i::m_pythia->next()) continue;
179 if ( !Pythia8_i::m_pythia->next() ) {
180 // First check if it failed because it ran out of events.
181 if ( Pythia8_i::m_pythia->info.atEndOfFile() ) {
182 return StatusCode::FAILURE;
183 }
184
185 // Otherwise, just make sure that it's not failing too many times in a row.
187 if ( m_failureCount >= m_maxFailures ) {
188 ATH_MSG_ERROR("Exceeded the max number of consecutive event failures.");
189 return StatusCode::FAILURE;
190 } else {
191 ATH_MSG_INFO("Event generation failed - re-trying.");
192 continue;
193 }
194 }
195 // Reset failure counter and increment event counter after a successful event
196 m_failureCount = 0;
198
199 // Find b(c)/antib(c) quarks and enforce cuts as required
200 int nbBeforeSelection(0);
201 int nbbarBeforeSelection(0);
202 int ncBeforeSelection(0);
203 int ncbarBeforeSelection(0);
204 int nbQuark(0);
205 int nbbarQuark(0);
206 int ncQuark(0);
207 int ncbarQuark(0);
208 int stat;
209 for (int i = 0; i < event.size(); ++i) {
210 stat = event[i].statusAbs();
211 bool isBQuark(false);
212 bool isCQuark(false);
213 bool isAntiBQuark(false);
214 bool isAntiCQuark(false);
215 // b-quarks (=anti-B hadrons)
216 if (event[i].id() == 5 && (stat == 62 || stat == 63)) {isBQuark=true; ++nbBeforeSelection;}
217 if (event[i].id() == 4 && (stat == 62 || stat == 63)) {isCQuark=true; ++ncBeforeSelection;}
218 if ( (isBQuark && m_selectBQuarks) || ((isCQuark && m_selectCQuarks)) ) {
219 bool passesPtCut(false); bool passesEtaCut(false);
220 std::string accString = " : REJECTED";
221 double qpt = event[i].pT(); double qeta = std::abs(event[i].eta());
222 if (qpt>m_qPtCut) passesPtCut=true;
223 if (qeta<m_qEtaCut) passesEtaCut=true;
224 if (passesPtCut && passesEtaCut) {
225 if (isBQuark) ++nbQuark;
226 if (isCQuark) ++ncQuark;
227 accString = " : ACCEPTED";
228 }
229 if (isBQuark) ATH_MSG_DEBUG("bQuark pt/eta = " << qpt << "/" << qeta << accString);
230 if (isCQuark) ATH_MSG_DEBUG("cQuark pt/eta = " << qpt << "/" << qeta << accString);
231 }
232 // anti-b quarks (=B-hadrons)
233 if (event[i].id() == -5 && (stat == 62 || stat == 63)) {isAntiBQuark=true; ++nbbarBeforeSelection;}
234 if (event[i].id() == -4 && (stat == 62 || stat == 63)) {isAntiCQuark=true; ++ncbarBeforeSelection;}
235 if ( (isAntiBQuark && m_selectBQuarks) || ((isAntiCQuark && m_selectCQuarks)) ) {
236 bool passesPtCut(false); bool passesEtaCut(false);
237 std::string accString = " : REJECTED";
238 double aqpt = event[i].pT(); double aqeta = std::abs(event[i].eta());
239 if (aqpt>m_aqPtCut) passesPtCut=true;
240 if (aqeta<m_aqEtaCut) passesEtaCut=true;
241 if (passesPtCut && passesEtaCut) {
242 if (isAntiBQuark) ++nbbarQuark;
243 if (isAntiCQuark) ++ncbarQuark;
244 accString = " : ACCEPTED";
245 }
246 if (isAntiBQuark) ATH_MSG_DEBUG("bbarQuark pt/eta = " << aqpt << "/" << aqeta << accString);
247 if (isAntiCQuark) ATH_MSG_DEBUG("ccbarQuark pt/eta = " << aqpt << "/" << aqeta << accString);
248 }
249 }
250 if (nbBeforeSelection+nbbarBeforeSelection>=4 && m_vetoDoubleB) {
251 ATH_MSG_DEBUG("At user request, rejecting double b-bbar event; throwing dice again");
252 continue;
253 }
254 if (ncBeforeSelection+ncbarBeforeSelection>=4 && m_vetoDoubleC) {
255 ATH_MSG_DEBUG("At user request, rejecting double c-cbar event; throwing dice again");
256 continue;
257 }
258 bool rejectBBbar(false);
259 bool rejectCCbar(false);
260 // Enforce user selections
261 if (!m_and && (nbbarQuark==0 && nbQuark==0) && m_selectBQuarks) rejectBBbar=true;
262 if (m_and && (nbbarQuark==0 || nbQuark==0) && m_selectBQuarks) rejectBBbar=true;
263 if (!m_and && (ncbarQuark==0 && ncQuark==0) && m_selectCQuarks) rejectCCbar=true;
264 if (m_and && (ncbarQuark==0 || ncQuark==0) && m_selectCQuarks) rejectCCbar=true;
265 if (m_selectBQuarks && m_selectCQuarks && rejectBBbar && rejectCCbar) {
266 ATH_MSG_DEBUG("No b- or c- quarks accepted; throwing the dice again");
267 continue;
268 }
269 if (m_selectBQuarks && !m_selectCQuarks && rejectBBbar) {
270 ATH_MSG_DEBUG("No b-quarks accepted; throwing the dice again");
271 continue;
272 }
273 if (!m_selectBQuarks && m_selectCQuarks && rejectCCbar) {
274 ATH_MSG_DEBUG("No c-quarks accepted; throwing the dice again");
275 continue;
276 }
277 m_totalBQuark += nbQuark;
278 m_totalBBarQuark += nbbarQuark;
279 m_totalCQuark += ncQuark;
280 m_totalCBarQuark += ncbarQuark;
281 ++m_totalHard;
282
283 // Switch off decays of species of interest if requested (only for repeated decays)
284 bool doRepeatedDecays(false); if (m_dec>1) doRepeatedDecays=true;
285 if (doRepeatedDecays) {
286 for (unsigned int iC = 0; iC < m_bcodes.size(); ++iC) Pythia8_i::m_pythia->particleData.mayDecay( m_bcodes[iC], false);
287 }
288
289 // REPEATED HADRONIZATION LOOP
290 // Make a spare copy of event
291 Pythia8::Event eventCopy;
292 std::vector<Pythia8::Event> repeatHadronizedEvents;
293 std::vector<Pythia8::Event>::iterator eventItr;
294 eventCopy = event;
295 // Begin loop over rounds of hadronization for same event.
296 for (unsigned int iRepeat = 0; iRepeat < m_had; ++iRepeat) {
297 ++m_totalClone;
298 // Repeated hadronization: restore saved event record.
299 if (iRepeat > 0) event = eventCopy;
300 // Repeated hadronization: do HadronLevel (repeatedly).
301 if (!Pythia8_i::m_pythia->forceHadronLevel()) continue;
302 repeatHadronizedEvents.push_back(event); // save the events for selections
303 }
304
305 // REPEATED DECAY LOOP
306 std::vector<Pythia8::Event> savedEvents;
307 if (doRepeatedDecays) {
308 // switch decays back on
309 for (unsigned int iC = 0; iC < m_bcodes.size(); ++iC) Pythia8_i::m_pythia->particleData.mayDecay( m_bcodes[iC], true);
310 // Loop over repeatedly hadronized events
311 for (eventItr=repeatHadronizedEvents.begin(); eventItr!=repeatHadronizedEvents.end(); ++eventItr) {
312 for (unsigned int iRepeat = 0; iRepeat < m_dec; ++iRepeat) {
313 event = (*eventItr);
314 if (!Pythia8_i::m_pythia->moreDecays()) break;
315 savedEvents.push_back(event);
316 }
317 }
318 }
319 if (!doRepeatedDecays) savedEvents = std::move(repeatHadronizedEvents);
320
321 //event.list();
322
323 // Make final selections on savedEvents
324 std::vector<Pythia8::Event> finalEvents;
325 bool signalSelect(false);
326 if (m_sigCodes.size()>0) signalSelect=true;
327 for (eventItr=savedEvents.begin(); eventItr!=savedEvents.end(); ++eventItr) {
328 // Does event contain basic leptons needed to fire triggers?
331 // Does the events contain a signal decay as required by the user
332 if (signalSelect) {
333 if (!cleanUndecayed(*eventItr,m_bcodes)) continue;
334 if (!signalAccept(*eventItr,m_sigCodes,m_nSignalRequired)) continue;
335 }
336 // User-defined selections if any
337 if (!m_userString.empty()) {
338 if (!userSelection(*eventItr,m_userString,m_userVar)) continue;
339 }
340 finalEvents.push_back(*eventItr);
341 ++iEvent;
342 }
343 savedEvents.clear();
344 if (finalEvents.size()>0) ++m_atLeastOneAcc;
345
346 // Species counting and preparing vector for filling
347 for (eventItr=finalEvents.begin(); eventItr!=finalEvents.end(); ++eventItr) {
348 for (int i = 0; i < (*eventItr).size(); ++i) {
349 int id = (*eventItr)[i].id();
350 std::map<int,int>::iterator it;
351 it = m_speciesCount.find(id);
352 if (it != m_speciesCount.end()) {
353 it->second++;
354 }
355 }
356 m_BEventBuffer.push_back(*eventItr);
359 //(*eventItr).list();
360 }
361
362 // End of event loop.
363 }
364
365 StatusCode returnCode = StatusCode::SUCCESS;
366
367 return returnCode;
368}
369
372
373 ATH_MSG_DEBUG(">>> Pythia8B_i from fillEvt");
374 ATH_MSG_DEBUG("BEventBuffer contains " << m_BEventBuffer.size() << " events ");
375 if (m_BEventBuffer.size()==0 ) {
376 ATH_MSG_DEBUG("BEventBuffer now empty - going to next Pythia event");
377 return StatusCode::SUCCESS;
378 }
379
380 Pythia8::Event &pyev = *(m_BEventBuffer.begin());
381 evt->set_event_number(*(m_internalEventNumbers.begin()));
382 m_pythiaToHepMC.fill_next_event(pyev, evt, 1, &Pythia8_i::m_pythia->info);
383
384
385 // set the randomseeds
388
389 // set the event weight
390 if (!evt->run_info()) evt->set_run_info(m_runinfo);
391 evt->set_units(HepMC3::Units::MEV, HepMC3::Units::MM);
392
393 // Remove event/number from buffer
394 m_BEventBuffer.erase(m_BEventBuffer.begin());
396
397
398 return StatusCode::SUCCESS;
399}
400
401
404
405 ATH_MSG_INFO(">>> Pythia8B_i from genFinalize");
406
407 Pythia8_i::m_pythia->stat();
408
409 Pythia8::Info info = Pythia8_i::m_pythia->info;
410 double xs = info.sigmaGen();// in mb
411 xs *= 1000. * 1000.;//convert to nb
412
413 std::cout << "MetaData: cross-section (nb)= " << xs << std::endl;
414 std::cout << "Total events from Pythia = " << m_totalPythiaCalls<< std::endl;
415 std::cout << "Number of accepted b quarks = " << m_totalBQuark<< std::endl;
416 std::cout << "Number of accepted bbar quarks = " << m_totalBBarQuark<< std::endl;
417 std::cout << "Number of accepted c quarks = " << m_totalCQuark<< std::endl;
418 std::cout << "Number of accepted cbar quarks = " << m_totalCBarQuark<< std::endl;
419 std::cout << "Number of accepted b/c events before hadronization = " << m_totalHard<< std::endl;
420 std::cout << "Number of hadronization loops per b/c-event = " << m_had<< std::endl;
421 std::cout << "Total number of hadronization loops = " << m_totalClone<< std::endl;
422 std::cout << "Number of accepted b/c events yielding at least one finally accepted event = " << m_atLeastOneAcc<< std::endl;
423 std::cout << "Average number of accepted events per hard event = " << static_cast<float>(m_internal_event_number)/static_cast<float>(m_atLeastOneAcc)<< std::endl;
424 std::cout << "Number of events passing trigger cuts = " << m_passingTriggerCuts<< std::endl;
425 std::cout << "Number of accepted events = " << m_internal_event_number<< std::endl;
426 std::cout << "Summary of cuts: " << std::endl;
428 if (m_selectBQuarks) std::cout << "Quarks cuts apply to b-quarks" << std::endl;
429 if (m_selectCQuarks) std::cout << "Quarks cuts apply to c-quarks" << std::endl;
430 std::cout << "Quark pt > " << m_qPtCut << std::endl;
431 std::cout << "Antiquark pt > " << m_aqPtCut << std::endl;
432 std::cout << "Quark eta < " << m_qEtaCut << std::endl;
433 std::cout << "Antiquark eta < " << m_aqEtaCut << std::endl;
434 if (m_and) std::cout << "Require quark and anti-quark pass cuts" << std::endl;
435 }
436 std::cout << "Trigger lepton type = " << m_trigCode << std::endl;
437 std::cout << "Trigger lepton pt cuts: ";
438 for (unsigned int prCntr=0; prCntr<m_trigPtCut.size(); ++prCntr) {std::cout << m_trigPtCut[prCntr] << " ";}
439 std::cout << std::endl;
440 std::cout << "Trigger lepton eta cut: " << m_trigEtaCut << std::endl;
441 std::cout << "Required number of leptons passing each trigger cut = ";
442 for (unsigned int prCntr=0; prCntr<m_cutCount.size(); ++prCntr) {std::cout << m_cutCount[prCntr] << " ";}
443 std::cout << std::endl;
444 std::cout << "Invariant mass of trigger leptons > " << m_invMass << std::endl;
445 if (m_oppCharges) std::cout << "Trigger leptons required to have opposite charges" << std::endl;
447 if (!m_userString.empty()) std::cout << "User selection: " << m_userString << std::endl;
448
449 if (m_speciesCount.size()>0) {
450 std::cout <<"\nSpecies\tCount\n"<< std::endl;
451 for ( std::map<int,int>::iterator iter = m_speciesCount.begin();
452 iter != m_speciesCount.end(); ++iter )
453 std::cout << iter->first << '\t' << iter->second << '\n'<< std::endl;
454 }
455 if (m_dec>1) std::cout << "Number of times each of these states were copied and decayed: " << m_dec << std::endl;
456
457 float cloningFactor = (float)(m_internal_event_number)/(float)(m_atLeastOneAcc);
458 float finalXS = ((float)xs*((float)m_internal_event_number/(float)m_totalPythiaCalls))/(float)m_had;
459
460 std::cout << " I===================================================================================== " << std::endl;
461 std::cout << " I CROSSSECTION OF YOUR B-CHANNEL IS I " << std::endl;
462 std::cout << " I BX= PX*NB/AC/MHAD= I " << finalXS << " nb" << std::endl;
463 std::cout << " I I " << std::endl;
464 std::cout << " I IN CASE YOU FORCED ANY DECAY YOU SHOULD I " << std::endl;
465 std::cout << " I CORRECT CROSS SECTION BX FURTHER, MULTIPLYING I " << std::endl;
466 std::cout << " I BX BY BRANCHING RATIO(S) OF YOUR FORCED I " << std::endl;
467 std::cout << " I DECAY(S) AND BY A FACTOR OF 2 FOR SYMMETRY I " << std::endl;
468 std::cout << " I I " << std::endl;
469 std::cout << " I MORE DETAILS ON CROSS SECTION I " << std::endl;
470 std::cout << " I PYTHIA CROSS SECTION IS PX= I " << xs <<" nb" << std::endl;
471 std::cout << " I NUMBER OF ACCEPTED HARD EVENTS AC= I " << m_totalPythiaCalls << std::endl;
472 std::cout << " I NUMBER OF ACCEPTED B-EVENTS IS NB= I " << m_internal_event_number<< std::endl;
473 std::cout << " I REPEATED HADRONIZATIONS IN EACH EVENT MHAD= I " << m_had << std::endl;
474 std::cout << " I AVERAGE NUM OF ACCEPTED EVTS IN HADRONIZATION LOOP I " << cloningFactor << std::endl;
475 std::cout << " I IN CASE YOU FORCED ANY DECAY YOU SHOULD I " << std::endl;
476 std::cout << " I CORRECT CROSS SECTION BX FURTHER, MULTIPLYING I " << std::endl;
477 std::cout << " I BX BY BRANCHING RATIO(S) OF YOUR FORCED I " << std::endl;
478 std::cout << " I DECAY(S) AND BY A FACTOR OF 2 FOR SYMMETRY I " << std::endl;
479 std::cout << " I I " << std::endl;
480 std::cout << " I===================================================================================== " << std::endl;
481 std::cout << "" << std::endl;
482 std::cout << "MetaData: cross-section (nb)= " << finalXS << std::endl;
483
484 return StatusCode::SUCCESS;
485}
486
488// Enforce trigger-like selections on final state leptons
489// User can require pt and eta cuts on a given number of leptons.
490// User can enforce at least one oppositely charged pair
491// User can enforce a minimum invariant mass on a pair
492
493bool Pythia8B_i::leptonSelect(Pythia8::Event &theEvent, const std::vector<double> &ptCut, double etaCut, const std::vector<int> &counts, int type_id, double massCut, bool opposite) {
494
495 if (type_id==0) return true;
496 bool passed(false);
497 std::string accString(" : REJECTED");
498
499 std::vector<int> leptonIDs;
500 int nCuts=ptCut.size();
501 std::vector<int> countGood(nCuts, 0);
502
503 for (int i = 0; i<theEvent.size(); ++i) {
504 const Pythia8::Particle &theParticle = theEvent[i];
505 int id = theParticle.idAbs();
506 if ( id == type_id ) { // find lepton flavour requested by user
507 double pt = theParticle.pT();
508 double eta = theParticle.eta();
509 ATH_MSG_DEBUG("Lepton of type " << id << " with pt/eta " << pt << "/" << eta);
510 for (int cutCntr=0; cutCntr<nCuts; ++cutCntr) {
511 if ( (pt>ptCut[cutCntr]) && (std::abs(eta)<etaCut) ) {
512 countGood[cutCntr] += 1;
513 leptonIDs.push_back(i); // remember leptons
514 }
515 }
516 }
517 // can the loop stop?
518 int nPassed(0);
519 for (int cutCntr=0; cutCntr<nCuts; ++cutCntr) {
520 if (countGood[cutCntr] >= counts[cutCntr]) ++nPassed;
521 }
522 if (nPassed==nCuts) break;
523 } // end of loop over particles
524
525 // Check the accumulated counts
526 int nPassed(0);
527 for (int cutCntr=0; cutCntr<nCuts; ++cutCntr) {
528 if (countGood[cutCntr] >= counts[cutCntr]) ++nPassed;
529 }
530 if (nPassed >= nCuts && nCuts==1) {accString=" : ACCEPTED"; passed = true;} // only 1 lepton required so no pair properties
531 if (nPassed >= nCuts && nCuts>1 && pairProperties(theEvent,leptonIDs,massCut,opposite)) {accString=" : ACCEPTED"; passed = true;}
532 ATH_MSG_DEBUG("Trigger-like selection" << accString);
533 return passed;
534}
535
537// Require that an event contains no undecayed particles of the types listed by
538// user
539bool Pythia8B_i::cleanUndecayed(Pythia8::Event &theEvent, const std::vector<int> &bCodes) {
540
541 bool cleanEvent(true);
542 std::string accString(" : ACCEPTED");
543 for (int i = 0; i<theEvent.size(); ++i) {
544 const Pythia8::Particle &theParticle = theEvent[i];
545 int id = theParticle.id();
546 int status = theParticle.status();
547 for (auto iItr = bCodes.begin(); iItr!=bCodes.end(); ++iItr) {
548 if ( (id == *iItr) && (status>0) ) {accString=" : REJECTED"; cleanEvent = false;}
549 }
550 }
551 ATH_MSG_DEBUG("Check event for undecayed signal particles" << accString);
552
553 return cleanEvent;
554
555}
556
558// Requires that a set of leptons represented by (index numbers in event record)
559// contains at least one pair with
560// - invariant mass above massCut
561// - opposite charges if user requires
562
563bool Pythia8B_i::pairProperties(Pythia8::Event &theEvent, const std::vector<int> &leptonIDs, double massCut, bool opposite) {
564
565 bool passesCuts(false);
566 std::string accString(" : REJECTED");
567 for (auto iit = leptonIDs.begin(); iit!=leptonIDs.end(); ++iit) {
568 for (auto iit2 = iit+1; iit2!=leptonIDs.end(); ++iit2) {
569 int q1=theEvent[*iit].charge();
570 int q2=theEvent[*iit2].charge();
571 if (opposite && (q1*q2>0)) continue;
572 double px1=theEvent[*iit].px();
573 double py1=theEvent[*iit].py();
574 double pz1=theEvent[*iit].pz();
575 double mass1=theEvent[*iit].mSel();
576 double e1=std::sqrt(px1*px1+py1*py1+pz1*pz1+mass1*mass1);
577 double px2=theEvent[*iit2].px();
578 double py2=theEvent[*iit2].py();
579 double pz2=theEvent[*iit2].pz();
580 double mass2=theEvent[*iit2].mSel();
581 double e2=std::sqrt(px2*px2+py2*py2+pz2*pz2+mass2*mass2);
582 double eSum=e1+e2;
583 double pxSum=px1+px2;
584 double pySum=py1+py2;
585 double pzSum=pz1+pz2;
586 double M=std::sqrt(eSum*eSum-pxSum*pxSum-pySum*pySum-pzSum*pzSum);
587 if (M>massCut) {
588 passesCuts=true;
589 ATH_MSG_DEBUG("Acceptable lepton pair with invariant mass : " << M);
590 break;
591 }
592 }
593 if (passesCuts) {accString=" : ACCEPTED"; break;}
594 }
595 ATH_MSG_DEBUG("Dilepton selection" << accString);
596 return passesCuts;
597}
598
600// Iteratively descends through a decay chain, recording all particles it comes
601// across
602void Pythia8B_i::descendThroughDecay(Pythia8::Event &theEvent, std::vector<Pythia8::Particle> &list, int i) const {
603
604 list.push_back(theEvent[i]);
605 std::vector<int> childrenIndices = theEvent.daughterList(i);
606 std::vector<int>::iterator iItr;
607 for (iItr = childrenIndices.begin(); iItr != childrenIndices.end(); ++iItr) {
608 descendThroughDecay(theEvent,list,*iItr);
609 }
610
611 return;
612
613}
614
616// Given a vector of Pythia8 particles, returns their PDG codes
617std::vector<int> Pythia8B_i::getCodes(const std::vector<Pythia8::Particle> &theParticles) const {
618
619 std::vector<int> codes;
620 codes.reserve(theParticles.size());
621 for (auto pItr = theParticles.begin(); pItr!=theParticles.end(); ++pItr ) {
622 codes.push_back( (*pItr).id() );
623 }
624 return codes;
625
626}
627
629// Compares two vectors of integers (any order) and if they are the same, returns
630// true
631bool Pythia8B_i::compare(std::vector<int> vect1, std::vector<int> vect2) const {
632
633 if (vect1.size()!=vect2.size()) return false;
634
635 bool isTheSame(true);
636 int size = vect1.size();
637 std::sort(vect1.rbegin(), vect1.rend());
638 std::sort(vect2.rbegin(), vect2.rend());
639 for (int i=0; i<size; ++i) {
640 if (vect1[i] != vect2[i]) {isTheSame = false; break;}
641 }
642 return isTheSame;
643
644}
645
647// Checks a vector of particles against a vector of pt and eta cuts and if all
648// particles do not pass the cuts, returns false
649// Last argument controls whether the cut is on pt or eta
650bool Pythia8B_i::passesPTCuts(const std::vector<Pythia8::Particle> &theParticles) const {
651
652 bool pass(true);
653 unsigned int i(0);
654 for (auto pItr=theParticles.cbegin(); pItr!=theParticles.cend(); ++pItr,++i) {
655 if ((*pItr).pT() < m_sigPtCuts[i]) pass = false;
656 if (!pass) break;
657 }
658 return pass;
659
660}
661
662bool Pythia8B_i::passesEtaCuts(const std::vector<Pythia8::Particle> &theParticles) const {
663
664 bool pass(true);
665 unsigned int i(0);
666 for (auto pItr=theParticles.cbegin(); pItr!=theParticles.cend(); ++pItr,++i) {
667 if (std::abs((*pItr).eta()) > m_sigEtaCuts[i]) pass = false;
668 if (!pass) break;
669 }
670
671 return pass;
672
673}
674
676// Given an event, checks whether it contains a decay process as defined by the
677// user and if so whether the final states pass user-defined cuts
678bool Pythia8B_i::signalAccept(Pythia8::Event &theEvent, const std::vector<int> &requiredDecay,
679 unsigned int nRequired) const {
680
681 bool acceptEvent(false);
682 std::vector<int> parentsIndices;
683 for (int i = 0; i<theEvent.size(); ++i) {
684 const Pythia8::Particle &theParticle = theEvent[i];
685 int id = theParticle.id();
686 if (id==requiredDecay[0]) parentsIndices.push_back(i);
687 }
688
689 // For the special case where the user merely requires a particular
690 // species to be in the event (mainly for EvtGen use)
691 if ( (requiredDecay.size()==1) && (parentsIndices.size()>0) ) {
692 acceptEvent = true;
693 return acceptEvent;
694 }
695
696 unsigned int goodDecayCounter(0);
697 for (std::vector<int>::iterator iItr = parentsIndices.begin(); iItr!=parentsIndices.end(); ++iItr) {
698 std::vector<Pythia8::Particle> decayMembers;
699 descendThroughDecay(theEvent,decayMembers,*iItr);
700 std::vector<int> pdgCodes = getCodes(decayMembers);
701 if (!compare(requiredDecay,std::move(pdgCodes))) {
702 ATH_MSG_DEBUG("Signal event REJECTED as does not contain required decay chain");
703 continue;
704 }
705 ATH_MSG_DEBUG("Event contains required signal decay chain");
706
707 if (decayMembers.size()==m_sigPtCuts.size()) {
708 if (!passesPTCuts(decayMembers)) {
709 ATH_MSG_DEBUG("Signal event REJECTED as signal chain does not pass pt cuts");
710 continue;
711 }
712 }
713
714 if (decayMembers.size()==m_sigEtaCuts.size()) {
715 if (!passesEtaCuts(decayMembers)) {
716 ATH_MSG_DEBUG("Signal event REJECTED as signal chain does not pass eta cuts");
717 continue;
718 }
719 }
720 if (nRequired==1) {
721 ATH_MSG_DEBUG("Signal decay good; event ACCEPTED");
722 acceptEvent = true;
723 break;
724 }
725 else {++goodDecayCounter;}
726 }
727 if (nRequired>1) {
728 if (goodDecayCounter>=nRequired) {
729 ATH_MSG_DEBUG(nRequired << "signal decays required; " << goodDecayCounter << " found; event ACCEPTED");
730 acceptEvent = true;
731 }
732 else if (goodDecayCounter<nRequired) {
733 ATH_MSG_DEBUG(nRequired << "signal decays required; " << goodDecayCounter << " found; event REJECTED");
734 acceptEvent = false;
735 }
736 }
737
738 return acceptEvent;
739
740}
741
744
745void Pythia8B_i::printSignalSelections(const std::vector<int> &signalProcess,const std::vector<double> &ptCuts, const std::vector<double> &etaCuts, unsigned int nRequired ) const {
746 std::cout << "Signal PDG codes required: ";
747 for (unsigned int k=0; k<m_sigCodes.size(); ++k) std::cout << signalProcess[k] << " ";
748 if (signalProcess.size()==ptCuts.size()) {
749 std::cout << "Cuts on the pt of the signal states: " << std::endl;
750 for (unsigned int l=0; l<ptCuts.size(); ++l) std::cout << ptCuts[l] << " ";
751 }
752 if (signalProcess.size()==etaCuts.size()) {
753 std::cout << "Cuts on the eta of the signal states: " << std::endl;
754 for (unsigned int l=0; l<etaCuts.size(); ++l) std::cout << etaCuts[l] << " ";
755 }
756 std::cout << "Number of such decays required per event: " << nRequired << std::endl;
757 std::cout << std::endl;
758}
759
760
Scalar eta() const
pseudorapidity method
#define ATH_MSG_ERROR(x)
#define ATH_MSG_FATAL(x)
#define ATH_MSG_INFO(x)
#define ATH_MSG_DEBUG(x)
#define CHECK(...)
Evaluate an expression and check for errors.
bool passed(DecisionID id, const DecisionIDContainer &)
checks if required decision ID is in the set of IDs in the container
size_t size() const
Number of registered mappings.
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
IntegerProperty m_randomSeed
Seed for random number engine.
Definition GenModule.h:84
std::shared_ptr< HepMC3::GenRunInfo > m_runinfo
The run info for HepMC3.
Definition GenModule.h:90
std::vector< long int > m_seeds
Definition Pythia8B_i.h:53
int m_totalClone
Definition Pythia8B_i.h:51
void printSignalSelections(const std::vector< int > &, const std::vector< double > &, const std::vector< double > &, unsigned int) const
std::vector< int > m_sigCodes
Definition Pythia8B_i.h:48
bool compare(std::vector< int >, std::vector< int >) const
static CLHEP::HepRandomEngine * p_rndmEngine
Definition Pythia8B_i.h:41
bool m_selectCQuarks
Definition Pythia8B_i.h:54
bool m_doSuppressSmallPT
Definition Pythia8B_i.h:60
int m_totalBQuark
Definition Pythia8B_i.h:51
int m_totalHard
Definition Pythia8B_i.h:51
std::vector< int > m_internalEventNumbers
Definition Pythia8B_i.h:59
double m_aqPtCut
Definition Pythia8B_i.h:55
std::vector< double > m_sigPtCuts
Definition Pythia8B_i.h:50
double m_aqEtaCut
Definition Pythia8B_i.h:55
unsigned int m_nSignalRequired
Definition Pythia8B_i.h:47
bool passesPTCuts(const std::vector< Pythia8::Particle > &) const
bool leptonSelect(Pythia8::Event &, const std::vector< double > &, double, const std::vector< int > &, int, double, bool)
int m_totalCQuark
Definition Pythia8B_i.h:51
Pythia8::SuppressSmallPT * m_SuppressSmallPT
Definition Pythia8B_i.h:61
unsigned int m_had
Definition Pythia8B_i.h:45
int m_atLeastOneAcc
Definition Pythia8B_i.h:51
int m_trigCode
Definition Pythia8B_i.h:46
virtual StatusCode callGenerator()
For calling the generator on each iteration of the event loop.
int m_totalBBarQuark
Definition Pythia8B_i.h:51
bool m_vetoDoubleC
Definition Pythia8B_i.h:54
void descendThroughDecay(Pythia8::Event &, std::vector< Pythia8::Particle > &, int) const
bool m_oppCharges
Definition Pythia8B_i.h:54
bool passesEtaCuts(const std::vector< Pythia8::Particle > &) const
unsigned int m_dec
Definition Pythia8B_i.h:45
std::vector< Pythia8::Event > m_BEventBuffer
Definition Pythia8B_i.h:58
std::vector< int > getCodes(const std::vector< Pythia8::Particle > &) const
std::vector< int > m_bcodes
Definition Pythia8B_i.h:48
double m_qEtaCut
Definition Pythia8B_i.h:55
std::map< int, int > m_speciesCount
Definition Pythia8B_i.h:52
double m_qPtCut
Definition Pythia8B_i.h:55
std::string m_userString
Definition Pythia8B_i.h:56
virtual StatusCode genFinalize()
For finalising the generator, if required.
bool m_vetoDoubleB
Definition Pythia8B_i.h:54
std::vector< int > m_cutCount
Definition Pythia8B_i.h:48
bool m_selectBQuarks
Definition Pythia8B_i.h:54
bool signalAccept(Pythia8::Event &, const std::vector< int > &, unsigned int) const
virtual StatusCode genuserInitialize()
For initialization of user code, if required. Called after genInitialize.
bool cleanUndecayed(Pythia8::Event &, const std::vector< int > &)
double m_invMass
Definition Pythia8B_i.h:55
unsigned int m_failureCount
Definition Pythia8B_i.h:62
std::vector< double > m_trigPtCut
Definition Pythia8B_i.h:49
bool userSelection(Pythia8::Event &, const std::string &, const std::vector< double > &)
virtual StatusCode fillEvt(HepMC::GenEvent *)
For filling the HepMC event object.
std::vector< double > m_userVar
Definition Pythia8B_i.h:57
bool pairProperties(Pythia8::Event &, const std::vector< int > &, double, bool)
virtual StatusCode genInitialize()
For initializing the generator, if required.
int m_totalPythiaCalls
Definition Pythia8B_i.h:51
std::vector< double > m_sigEtaCuts
Definition Pythia8B_i.h:50
int m_passingTriggerCuts
Definition Pythia8B_i.h:51
Pythia8B_i(const std::string &name, ISvcLocator *pSvcLocator)
int m_totalCBarQuark
Definition Pythia8B_i.h:51
int m_internal_event_number
Definition Pythia8B_i.h:51
double m_trigEtaCut
Definition Pythia8B_i.h:55
virtual StatusCode genInitialize()
For initializing the generator, if required.
Definition Pythia8_i.cxx:98
IntegerProperty m_dsid
Definition Pythia8_i.h:97
bool useRndmGenSvc() const
Definition Pythia8_i.h:85
HepMC::Pythia8ToHepMC m_pythiaToHepMC
Definition Pythia8_i.h:89
std::shared_ptr< customRndm > m_atlasRndmEngine
Definition Pythia8_i.h:93
StringArrayProperty m_userHooks
Definition Pythia8_i.h:98
std::unique_ptr< Pythia8::Pythia > m_pythia
Definition Pythia8_i.h:88
UnsignedIntegerProperty m_maxFailures
Definition Pythia8_i.h:90
BooleanProperty m_useRndmGenSvc
Definition Pythia8_i.h:92
Pythia8_i(const std::string &name, ISvcLocator *pSvcLocator)
Definition Pythia8_i.cxx:68
void calculateSeedsMC21(long *seeds, const std::string &algName, uint64_t ev, uint64_t run, uint64_t offset=0)
Set the random seed using a string (e.g.
void set_random_states(GenEvent *e, std::vector< long int > &a)
Definition GenEvent.h:588
HepMC3::GenEvent GenEvent
Definition GenEvent.h:39
void sort(typename DataModel_detail::iterator< DVL > beg, typename DataModel_detail::iterator< DVL > end)
Specialization of sort for DataVector/List.