Project
Loading...
Searching...
No Matches
AODToHepMC.cxx
Go to the documentation of this file.
1// Copyright 2023-2099 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
15#include <TMCProcess.h>
16namespace o2
17{
18namespace eventgen
19{
20// -------------------------------------------------------------------
22{
23
24 mOnlyGen = configs.onlyGen;
25 mUseTree = configs.useTree;
26 mPrecision = configs.precision;
27 mRecenter = configs.recenter;
28 enableDump(configs.dump);
29
30 LOG(debug) << "=== o2::rivet::Converter ===\n"
31 << " Dump to output: " << configs.dump << "\n"
32 << " Only generated tracks: " << mOnlyGen << "\n"
33 << " Use tree store: " << mUseTree << "\n"
34 << " Output precision: " << mPrecision;
35}
36// -------------------------------------------------------------------
38{
39 LOG(debug) << ">>> Starting an event";
40 mBeams.clear();
41 mOrphans.clear();
42 mParticles.clear();
43 mVertices.clear();
44 mEvent.clear();
45}
46// -------------------------------------------------------------------
47void AODToHepMC::process(Header const& collision,
48 Tracks const& tracks)
49{
50 LOG(debug) << "--- Processing track information";
51
52 if (not mUseTree) {
53 mEvent.reserve(tracks.size(), tracks.size() / 2);
54 }
55 makeHeader(collision);
56 makeParticles(tracks);
57 makeEvent(collision, tracks);
58}
59// -------------------------------------------------------------------
60void AODToHepMC::process(Header const& collision,
61 XSections const& xsections,
62 PdfInfos const& pdfs,
63 HeavyIons const& heavyions)
64{
65 LOG(debug) << "--- Processing auxiliary information";
66 makeXSection(xsections);
67 makePdfInfo(pdfs);
68 makeHeavyIon(heavyions, collision);
69}
70// -------------------------------------------------------------------
72{
73 LOG(debug) << "<<< an event";
74 if (not mWriter) {
75 return;
76 }
77 // If we have a writer, then dump event to output file
78 LOG(debug) << "=== write out";
79 mWriter->write_event(mEvent);
80}
81// ===================================================================
82void AODToHepMC::makeEvent(Header const& collision,
83 Tracks const& tracks)
84{
85 if (mUseTree) {
86 mEvent.reserve(mParticles.size() + mBeams.size(), mVertices.size());
87 mEvent.add_tree(mBeams);
88 } else {
89 if (mOrphans.size() > 0) {
90 if (mBeams.size() > 0) {
91 LOG(debug) << "Event has " << mOrphans.size() << " orphans "
92 << "out of " << mParticles.size() << " total, "
93 << "and explicit beams " << mBeams.size();
94 } else {
95 LOG(warning) << "Event has " << mOrphans.size() << " orphans "
96 << "out of " << mParticles.size() << " total, "
97 << "but no beams";
98 }
99 // Get collision IP
100 FourVector v(collision.posX(),
101 collision.posY(),
102 collision.posZ(),
103 collision.t());
104 auto ip = std::make_shared<HepMC3::GenVertex>(v);
105 mEvent.add_vertex(ip);
106 for (auto p : mOrphans) {
107 ip->add_particle_out(p);
108 }
109 // If we have no beam particles, try to make them
110 if (mBeams.size() == 0) {
111 makeBeams(collision, ip);
112 }
113 }
114 }
115 // Flesh out the tracks based on daughter information.
116 fleshOut(tracks);
117 LOG(debug) << "Event # " << mEvent.event_number() << " "
118 << "(# " << mEventNo << " processed)"
119 << "\n"
120 << "# of particles " << mParticles.size() << " "
121 << "(" << tracks.size() << " input tracks "
122 << mEvent.particles().size() << " output)"
123 << "\n"
124 << "# of orphans " << mOrphans.size() << "\n"
125 << "# of beams " << mBeams.size() << "\n"
126 << "# of vertexes " << mVertices.size() << " ("
127 << mEvent.vertices().size() << " output)";
128}
129// -------------------------------------------------------------------
131{
132 int eventNo = header.bcId();
133 if (eventNo == mLastBC) {
134 eventNo = mEventNo;
135 }
136
137 // Set the event number
138 mEvent.set_event_number(eventNo);
139 mEvent.weights().push_back(header.weight());
140 LOG(debug) << "Event # " << mEvent.event_number()
141 << " (BC: " << header.bcId()
142 << " serial: " << mEventNo
143 << " last: " << mLastBC << ") "
144 << " w/weight " << mEvent.weights().front();
145 // Store last seen BC
146 mLastBC = header.bcId();
147 // Increase internal counter of event
148 mEventNo++;
149}
150// -------------------------------------------------------------------
152{
153 LOG(debug) << "--- Process cross-section information";
154 if (not mCrossSec) {
155 // If we do not have a cross-sections object, create it
156 mCrossSec = std::make_shared<HepMC3::GenCrossSection>();
157 }
158
159 mEvent.set_cross_section(mCrossSec);
160 mCrossSec->set_cross_section(0.f, 0.f, 0, 0);
161
162 if (xsections.size() <= 0) {
163 // If we have no info, skip the rest
164 LOG(warning) << "??? No input cross-section";
165 return;
166 }
167
168 XSection xsection = xsections.iteratorAt(0);
169 mCrossSec->set_cross_section(xsection.xsectGen(),
170 xsection.xsectErr(),
171 xsection.accepted(),
172 xsection.attempted());
173}
174// -------------------------------------------------------------------
176{
177 LOG(debug) << "--- Process PDF information";
178 if (not mPdf) {
179 // If we do not have a Parton Distribution Function object, create it
180 mPdf = std::make_shared<HepMC3::GenPdfInfo>();
181 }
182
183 mEvent.set_pdf_info(mPdf);
184 mPdf->set(0, 0, 0.f, 0.f, 0.f, 0.f, 0.f, 0, 0);
185
186 if (pdfs.size() <= 0) {
187 // If we have no PDF info, skip the rest
188 LOG(warning) << "??? No input pdf-info, skipping";
189 return;
190 }
191
192 PdfInfo pdf = pdfs.iteratorAt(0);
193 mPdf->set(pdf.id1(),
194 pdf.id2(),
195 pdf.x1(),
196 pdf.x2(),
197 pdf.scalePdf(),
198 pdf.pdf1(),
199 pdf.pdf2(),
200 pdf.pdfId1(),
201 pdf.pdfId2());
202}
203// -------------------------------------------------------------------
205 Header const& header)
206{
207 LOG(debug) << "--- Process input heavy-ion header";
208 if (not mIon) {
209 // Generate heavy ion element if it doesn't exist
210 mIon = std::make_shared<HepMC3::GenHeavyIon>();
211 }
212
213 mEvent.set_heavy_ion(mIon);
214 mIon->impact_parameter = header.impactParameter();
215 mIon->event_plane_angle = 0.f;
216 mIon->Ncoll_hard = 0;
217 mIon->Npart_proj = 0;
218 mIon->Npart_targ = 0;
219 mIon->Nspec_proj_n = 0;
220 mIon->Nspec_proj_p = 0;
221 mIon->Nspec_targ_n = 0;
222 mIon->Nspec_targ_p = 0;
223 mIon->Ncoll = 0;
224 mIon->N_Nwounded_collisions = 0;
225 mIon->Nwounded_N_collisions = 0;
226 mIon->Nwounded_Nwounded_collisions = 0;
227 mIon->sigma_inel_NN = 0.f;
228 mIon->centrality = 0.f;
229#ifndef HEPMC3_NO_DEPRECATED
230 // Deprecated interface with a single eccentricity
231 mIon->eccentricity = 0.f;
232#else
233 // Newer interface that stores multiple orders of eccentricities.
234 mIon->eccentricities[1] = 0.f;
235#endif
236
237 if (heavyions.size() <= 0) {
238 // If we have no heavy-ion information, skip the rest
239 LOG(warning) << "??? No input heavy-ion header, skipping";
240 return;
241 }
242
243 HeavyIon heavyion = heavyions.iteratorAt(0);
244 float r = 1;
245 // We need to calculate the ratio projectile to target participants
246 // so that we may break up the number of spectators and so on. This
247 // is because the AOD HepMC3HeavyIons table does not store the
248 // relevant information directly.
249 if (heavyion.npartProj() < heavyion.npartTarg() and
250 heavyion.npartTarg() > 0) {
251 r = heavyion.npartProj() / heavyion.npartTarg();
252 } else if (heavyion.npartTarg() < heavyion.npartProj() and
253 heavyion.npartProj() > 0) {
254 r = heavyion.npartTarg() / heavyion.npartProj();
255 r = (1 - r);
256 }
257
258 // Heavy ion parameters. Note that number of projectile/target
259 // proton/neutrons are set by the ratio calculated above.
260 mIon->impact_parameter = heavyion.impactParameter();
261 mIon->event_plane_angle = heavyion.eventPlaneAngle();
262 mIon->Ncoll_hard = heavyion.ncollHard();
263 mIon->Npart_proj = heavyion.npartProj();
264 mIon->Npart_targ = heavyion.npartTarg();
265 mIon->Nspec_proj_n = heavyion.spectatorNeutrons() * (1 - r);
266 mIon->Nspec_proj_p = heavyion.spectatorProtons() * (1 - r);
267 mIon->Nspec_targ_n = heavyion.spectatorNeutrons() * r;
268 mIon->Nspec_targ_p = heavyion.spectatorProtons() * r;
269 mIon->Ncoll = heavyion.ncoll();
270 mIon->N_Nwounded_collisions = heavyion.nNwoundedCollisions();
271 mIon->Nwounded_N_collisions = heavyion.nwoundedNCollisions();
272 mIon->Nwounded_Nwounded_collisions = heavyion.nwoundedNwoundedCollisions();
273 mIon->sigma_inel_NN = heavyion.sigmaInelNN();
274 mIon->centrality = heavyion.centrality();
275#ifndef HEPMC3_NO_DEPRECATED
276 mIon->eccentricity = heavyion.eccentricity();
277#else
278 mIon->eccentricities[1] = heavyion.eccentricity();
279#endif
280}
281// -------------------------------------------------------------------
283{
284 // mVertices.reserve(tracks.size() / 2);
285 // mOrphans.reserve(tracks.size() / 20);
286 mBeams.reserve(2);
287 for (auto track : tracks) {
288 makeParticleRecursive(track, tracks);
289 }
290}
291// -------------------------------------------------------------------
293{
294 auto iter = mParticles.find(ref.globalIndex());
295 if (iter == mParticles.end()) {
296 return nullptr;
297 }
298
299 return iter->second;
300}
301// -------------------------------------------------------------------
302bool AODToHepMC::isIgnored(Track const& track) const
303{
304 bool fromEG = track.producedByGenerator();
305 if (!fromEG and mOnlyGen) {
306 LOG(debug) << " Track # " << track.globalIndex()
307 << " from transport, ignored";
308 return true;
309 }
310 return false;
311}
312// -------------------------------------------------------------------
314 Tracks const& tracks,
315 bool force)
316{
317 ParticlePtr particle = getParticle(track);
318
319 // Check if we already have the particle, and if so, return it
320 if (particle) {
321 return particle;
322 }
323
324 // Make this particle and store it
325 int motherStatus = 0;
326 particle = makeParticle(track, motherStatus, force);
327 if (not particle) {
328 return nullptr;
329 }
330
331 // Store mapping from index to particle
332 mParticles[track.globalIndex()] = particle;
333
334 // Generate mother particles, recurses down tree
335 ParticleList mothers;
336 VertexPtr vout;
337
338 // Check with has_mothers?
339 if (track.has_mothers()) {
340 for (auto mtrack : track.mothers_as<Tracks>()) {
341 auto mother = makeParticleRecursive(mtrack, tracks, true);
342 // If mother not found, continue
343 if (not mother) {
344 continue;
345 }
346
347 // Overrride mother status based on production mechanism of daughter
348 if (motherStatus != 0) {
349 mother->set_status(motherStatus);
350 }
351
352 mothers.push_back(mother);
353 // Update the production vertex if not set already
354 if (not vout) {
355 vout = mother->end_vertex();
356 }
357 }
358 }
359
360 // If we have no out vertex, and the particle isn't a beam
361 // particle, and we have mother particles, then create the
362 // out-going vertex.
363 if (not vout and
364 particle->status() != 4 and
365 mothers.size() > 0) {
366 vout = makeVertex(track);
367 if (mUseTree) {
368 mVertices.push_back(vout);
369 }
370
371 // If mothers do not have any end-vertex, add them to the found
372 // vertex.
373 for (auto mother : mothers) {
374 if (not mother->end_vertex()) {
375 vout->add_particle_in(mother);
376 }
377 }
378 }
379
380 // If we got a out-going vertex, add this particle to that
381 if (vout) {
382 vout->add_particle_out(particle);
383 }
384
385 // If this is a beam particle, add to them
386 if (particle->status() == 4) {
387 mBeams.push_back(particle);
388 }
389 // if if there no mothers, and this is not beam, then make
390 // this an orphan.
391 else if (mothers.size() <= 0) {
392 mOrphans.push_back(particle);
393 }
394 // return the particle
395 return particle;
396}
397// -------------------------------------------------------------------
399 Int_t& motherStatus,
400 bool force) const
401{
402 Int_t pdg = track.pdgCode();
403 int hepMCstatus = track.getHepMCStatusCode();
404 int egStatus = track.getGenStatusCode();
405 int transport = track.getProcess();
406 bool fromEG = track.producedByGenerator();
407
408 // Do not generate particle if it is not from generator and we are
409 // asked not to make other particles. Note, if a particle has this
410 // as one of it's mothers, we will make it despite the flag.
411 if (not force and mOnlyGen and !fromEG) {
412 return nullptr;
413 }
414
415 FourVector p(track.px(),
416 track.py(),
417 track.pz(),
418 track.e());
419
420 // Possibly update mother status depending on the production
421 // mechanism of this child.
422 motherStatus = 0; // 200 + uni; // Mother unknown status code
423 switch (transport) {
424 case kPDecay:
425 motherStatus = 2;
426 break; // Mother decayed!
427 }
428 int state = hepMCstatus < 0 ? 200 + transport : hepMCstatus;
429
430 ParticlePtr g = std::make_shared<HepMC3::GenParticle>(p, pdg, state);
431 // g->set_generated_mass(track.GetMass());
432
433 return g;
434}
435// -------------------------------------------------------------------
436void AODToHepMC::fleshOut(Tracks const& tracks)
437{
438 for (auto track : tracks) {
439 fleshOutParticle(track, tracks);
440 }
441}
442// -------------------------------------------------------------------
443void AODToHepMC::fleshOutParticle(Track const& track, Tracks const& tracks)
444{
445 // If we are only propagating generated tracks, then we need
446 // not process transported tracks
447 if (isIgnored(track)) {
448 return;
449 }
450
451 // Check that we can find the track in our map
452 auto particle = getParticle(track);
453 if (not particle) {
454 LOG(warning) << "No particle at " << track.globalIndex() << " in map";
455 return;
456 }
457
458 auto endVtx = particle->end_vertex();
459
460 // If endVtx is null, then the particle has no end vertex. This can
461 // be because the particle is truly a leaf particle (final-state,
462 // including after transport), or because the particle wasn't
463 // properly attached to the event. Since we cannot be sure, in the
464 // first case, that the status flag accurately reflects the
465 // sitation, we instead investigate the actual daughters.
466 //
467 // We check even if the particle has an end vertex and that all
468 // daughters have the same production vertex.
469 int svId = particle->id();
470 VertexPtr candidate = endVtx;
471 ParticleList headLess;
472
473 // Explicitly check for daughters, since creating empy slices takes
474 // up a lot of time
475 if (track.has_daughters()) {
476 for (auto dtrack : track.daughters_as<Tracks>()) {
477 // Check that the daughther is generated by EG. If not, and we
478 // only store generated tracks, then go on to the next daughter
479 // (or return?).
480 if (isIgnored(dtrack)) {
481 continue;
482 }
483
484 auto daughter = getParticle(dtrack);
485 if (not daughter) {
486 LOG(warning) << "Daughter " << dtrack.globalIndex()
487 << " of " << track.globalIndex()
488 << " not found in map!";
489 continue;
490 }
491
492 // We get the production vertex of the daughter. If there's no
493 // production vertex, then the daughter is deemed "head-less", and
494 // we will attach it to the end vertex of the mother, if one
495 // exists or is found below.
496 auto prodVtx = daughter->production_vertex();
497 if (not prodVtx) {
498 headLess.push_back(daughter);
499 continue;
500 }
501
502 // If the mother has an end vertex, but it doesn't match the
503 // production vertex of the daughter, then this daughter does not
504 // belong to that mother. This comes about because O2 encodes
505 // daughters as a range - a la HEPEVT, which requires a specific
506 // ordering of particles so that all daughters of a vertex are
507 // consequitive. Since we decide to trust the mother information,
508 // rather than the daughter information, we will simply disregard
509 // such daughters.
510 //
511 // This check may not be needed
512 if (endVtx and endVtx->id() != prodVtx->id()) {
513 continue;
514 }
515
516 // If we have a current candidate end vertex, but it doesn't match
517 // the production vertex of the daughter, then we give a warning.
518 //
519 // This check may not be needed
520 if (candidate and prodVtx->id() != candidate->id()) {
521 LOG(warning) << "Production vertex of daughter " << daughter->id()
522 << " of " << particle->id()
523 << " is not the same as previously found from "
524 << svId;
525 continue;
526 }
527 candidate = prodVtx;
528 svId = daughter->id();
529 }
530 }
531 // The logic below is a little funny
532 if (not endVtx) {
533 // Give warning for decayed or beam particles without and
534 // end vertex
535 if (not candidate and
536 (particle->status() == 4 or
537 particle->status() == 2)) {
538 // Only warn for beam and decayed particles
539 LOG(warning) << "Particle " << track.globalIndex()
540 << " (" << particle->id() << ")"
541 << " w/status " << particle->status()
542 << " does not have an end-vertex, "
543 << "nor was any found from its daughters";
544 }
545
546 // If we have a candidate, set the end vertex
547 if (candidate) {
548 endVtx = candidate;
549 endVtx->add_particle_in(particle);
550 }
551 }
552
553 // If we have head-less daughters, add them here
554 if (endVtx and headLess.size() > 0) {
555 for (auto daughter : headLess) {
556 endVtx->add_particle_out(daughter);
557 }
558 }
559}
560// -------------------------------------------------------------------
561void AODToHepMC::enableDump(const std::string& dump)
562{
563 if (not dump.empty() and mWriter) {
564 return;
565 }
566
567 if (dump.empty() and not mWriter) {
568 return;
569 }
570
571 if (not dump.empty()) {
572 LOG(debug) << "o2::rivet::Converter: Open output HepMC file " << dump;
573 mOutput = new std::ofstream(dump.c_str());
574 mWriter = std::make_shared<WriterAscii>(*mOutput);
575 mWriter->set_precision(mPrecision);
576 } else {
577 LOG(debug) << "o2::river::Converter\n"
578 << "*********************************\n"
579 << "Closing output HepMC file\n"
580 << "*********************************";
581 mWriter.reset();
582 if (mOutput) {
583 mOutput->close();
584 }
585
586 delete mOutput;
587 mOutput = nullptr;
588 }
589}
590// -------------------------------------------------------------------
592{
593 FourVector v(track.vx(),
594 track.vy(),
595 track.vz(),
596 track.vt());
597 auto vtx = std::make_shared<HepMC3::GenVertex>(v);
598 if (not mUseTree) {
599 mEvent.add_vertex(vtx);
600 }
601 return vtx;
602}
603// -------------------------------------------------------------------
604void AODToHepMC::recenter(Header const& collision)
605{
606 FourVector ip(collision.posX(),
607 collision.posY(),
608 collision.posZ(),
609 collision.t());
610
611 for (auto vertex : mEvent.vertices()) {
612 vertex->set_position(vertex->position() - ip);
613 }
614}
615
616// -------------------------------------------------------------------
617} /* namespace eventgen */
618} /* namespace o2 */
619//
620// EOF
621//
benchmark::State & state
uint64_t vertex
Definition RawEventData.h:9
std::ostringstream debug
void dump(const std::string what, DPMAP m, int verbose)
Definition dcs-ccdb.cxx:79
const GLdouble * v
Definition glcorearb.h:832
GLboolean GLboolean g
Definition glcorearb.h:1233
GLboolean r
Definition glcorearb.h:1233
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
ParticleVector mBeams
Cache of vertices.
Definition AODToHepMC.h:358
struct o2::eventgen::AODToHepMC::@467 configs
virtual void makePdfInfo(PdfInfos const &pdf)
CrossSectionPtr mCrossSec
Definition AODToHepMC.h:343
virtual VertexPtr makeVertex(const Track &track)
virtual void process(Header const &collision, Tracks const &tracks)
virtual void makeBeams(Header const &header, const VertexPtr ip)
Definition AODToHepMC.h:475
virtual void makeXSection(XSections const &xsection)
virtual void makeHeader(Header const &header)
std::list< ParticlePtr > ParticleList
Definition AODToHepMC.h:326
WriterAsciiPtr mWriter
Definition AODToHepMC.h:367
virtual ParticlePtr makeParticleRecursive(Track const &track, Tracks const &tracks, bool force=false)
virtual void makeHeavyIon(HeavyIons const &heavyion, Header const &header)
HepMC3::FourVector FourVector
Definition AODToHepMC.h:307
virtual void fleshOutParticle(Track const &track, Tracks const &tracks)
HepMC3::GenParticlePtr ParticlePtr
Definition AODToHepMC.h:309
o2::aod::HepMCHeavyIons HeavyIons
Definition AODToHepMC.h:293
o2::aod::HepMCPdfInfos PdfInfos
Definition AODToHepMC.h:291
virtual void makeEvent(Header const &collision, Tracks const &tracks)
virtual void makeParticles(Tracks const &tracks)
typename PdfInfos::iterator PdfInfo
Definition AODToHepMC.h:298
typename HeavyIons::iterator HeavyIon
Definition AODToHepMC.h:300
o2::aod::McParticles Tracks
Definition AODToHepMC.h:284
void enableDump(const std::string &dump)
virtual bool isIgnored(Track const &track) const
o2::aod::HepMCXSections XSections
Definition AODToHepMC.h:288
VertexList mVertices
Cache of particles.
Definition AODToHepMC.h:356
HepMC3::GenVertexPtr VertexPtr
Definition AODToHepMC.h:311
virtual void fleshOut(Tracks const &tracks)
virtual ParticlePtr makeParticle(const Track &track, Int_t &motherStatus, bool force) const
typename Tracks::iterator Track
Definition AODToHepMC.h:286
virtual void startEvent()
std::ofstream * mOutput
Definition AODToHepMC.h:377
virtual void endEvent()
typename XSections::iterator XSection
Definition AODToHepMC.h:295
ParticleList mOrphans
Cache of beam particles.
Definition AODToHepMC.h:360
virtual ParticlePtr getParticle(Track const &ref) const
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"