Skip to content

Commit dafdd19

Browse files
tubagundemclaude
andcommitted
Store the monopole mass in the MCEventHeader and use it when reading kinematics
The monopole mass of a simulation (G4.monopoleMass) was only known to the simulation itself. Consumers of the output (digitizer, reconstruction MC labels, QC, analysis) took the compiled-in default of 100 GeV through O2DatabasePDG, so MCTrack::GetMass(), GetEnergy(), GetRapidity() and Get4Momentum() were wrong for monopoles of any other mass. - O2HitMerger: apply the G4.* config key-values (they were not applied in the merger process) and write the mass to the MCEventHeader under o2::sim::MonopoleMassHeaderKey when G4.monopole is set. - MCKinematicsReader: take the mass from the header when headers or tracks are loaded and hand it to O2DatabasePDG; warn if kinematics of different masses are read in the same process. - O2DatabasePDG: setMonopoleMass()/getMonopoleMass(); Mass() returns the set mass for the monopole PDG codes. Unset by default, so the simulation itself keeps using TDatabasePDG. Limitations: entries already registered in a TDatabasePDG keep their mass (ROOT has no setter); G4 parameters given through --configFile are not applied in the merger; files produced before this change have no key. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
1 parent e74a2e0 commit dafdd19

4 files changed

Lines changed: 114 additions & 13 deletions

File tree

‎DataFormats/simulation/include/SimulationDataFormat/MonopoleParticles.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ constexpr int MonopolePdgAsymm = 4120000;
3434
/// Default monopole mass in GeV.
3535
constexpr double MonopoleMassDefaultGeV = 100.;
3636

37+
/// Key under which the monopole mass (GeV) used in a simulation is stored in the MCEventHeader,
38+
/// so that readers of the kinematics can use the mass the events were produced with.
39+
constexpr const char* MonopoleMassHeaderKey = "monopoleMass";
40+
3741
/// true for the monopole and antimonopole of both species
3842
constexpr bool isMonopole(int pdg) noexcept
3943
{

‎DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h‎

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#ifndef O2_O2DATABASEPDG_H
1717
#define O2_O2DATABASEPDG_H
1818

19+
#include <atomic>
1920
#include <cmath>
2021
#include <string>
2122
#include "TDatabasePDG.h"
@@ -39,7 +40,8 @@ class O2DatabasePDG
3940
static bool initialized = false; // initialize this --> adds particles to TDatabasePDG;
4041
auto db = TDatabasePDG::Instance();
4142
if (!initialized) {
42-
addALICEParticles(db);
43+
// uses the monopole mass of the simulation if a reader has already set it
44+
addALICEParticles(db, getMonopoleMass());
4345
initialized = true;
4446
}
4547
return db;
@@ -68,6 +70,26 @@ class O2DatabasePDG
6870
return true;
6971
}
7072

73+
// Sets the mass (GeV) of the monopole species for consumers of simulated data, which do not
74+
// have access to G4Params.monopoleMass. It is taken from the MCEventHeader of the
75+
// simulation (key o2::sim::MonopoleMassHeaderKey) by MCKinematicsReader.
76+
// It only affects Mass() (hence MCTrack::GetMass() / GetEnergy() / GetRapidity()) and
77+
// Instance() if called before the first Instance(): particles already registered in a
78+
// TDatabasePDG keep their mass, since ROOT offers no way to change it afterwards.
79+
// Not used (and not needed) in the simulation itself, where TDatabasePDG is registered
80+
// with G4Params.monopoleMass.
81+
static void setMonopoleMass(double massGeV) { monopoleMassOverride().store(massGeV); }
82+
83+
// mass set by setMonopoleMass(), or the default if it was never set
84+
static double getMonopoleMass()
85+
{
86+
const double mass = monopoleMassOverride().load();
87+
return mass > 0. ? mass : o2::sim::MonopoleMassDefaultGeV;
88+
}
89+
90+
// true if a monopole mass was set by setMonopoleMass()
91+
static bool hasMonopoleMassOverride() { return monopoleMassOverride().load() > 0.; }
92+
7193
// get particle's (if any) mass
7294
static Double_t MassImpl(TParticlePDG* particle, bool& success)
7395
{
@@ -82,6 +104,11 @@ class O2DatabasePDG
82104
// determine particle to get mass for based on PDG
83105
static Double_t Mass(int pdg, bool& success, TDatabasePDG* db = O2DatabasePDG::Instance())
84106
{
107+
if (hasMonopoleMassOverride() && o2::sim::isMonopole(pdg)) {
108+
// the table may have been filled with the default mass before the simulation mass was known
109+
success = true;
110+
return getMonopoleMass();
111+
}
85112
if (pdg < IONBASELOW || pdg > IONBASEHIGH) {
86113
// not an ion, return immediately
87114
return MassImpl(db->GetParticle(pdg), success);
@@ -99,6 +126,13 @@ class O2DatabasePDG
99126
O2DatabasePDG() = delete;
100127

101128
private:
129+
// negative: not set (the TDatabasePDG content is used)
130+
static std::atomic<double>& monopoleMassOverride()
131+
{
132+
static std::atomic<double> mass{-1.};
133+
return mass;
134+
}
135+
102136
static constexpr int IONBASELOW{1000000000};
103137
static constexpr int IONBASEHIGH{1099999999};
104138
};
@@ -161,7 +195,7 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db, double monopoleMa
161195
db->AddParticle("A_0+ ", " ", 0.9960, kFALSE, 0.0, +3, "meson", 9000211);
162196
db->AddParticle("A_0- ", " ", 0.9960, kFALSE, 0.0, -3, "meson", -9000211);
163197

164-
//db->AddParticle("F0P0 ", " ", 0.9960, kFALSE, 0.0, 0, "meson", 9010221);
198+
// db->AddParticle("F0P0 ", " ", 0.9960, kFALSE, 0.0, 0, "meson", 9010221);
165199

166200
db->AddParticle("KDL_2+ ", " ", 1.773, kFALSE, 0.0, +3, "meson", 10325);
167201
db->AddParticle("KDL_2- ", " ", 1.773, kFALSE, 0.0, -3, "meson", -10325);
@@ -261,7 +295,7 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db, double monopoleMa
261295
db->AddParticle("FeedbackPhoton", "FeedbackPhoton", 0, kFALSE,
262296
0, 0, "Special", kspe + 51);
263297

264-
//Hyper nuclei and exotica
298+
// Hyper nuclei and exotica
265299
ionCode = 1010010030;
266300
if (!db->GetParticle(ionCode)) {
267301
db->AddParticle("HyperTriton", "HyperTriton", 2.991134, kFALSE,
@@ -274,25 +308,25 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db, double monopoleMa
274308
2.5e-15, 3, "Ion", ionCode);
275309
}
276310

277-
//hyper hydrogen 4 ground state
311+
// hyper hydrogen 4 ground state
278312
ionCode = 1010010040;
279313
if (!db->GetParticle(ionCode)) {
280314
db->AddParticle("Hyperhydrog4", "Hyperhydrog4", 3.922434, kFALSE,
281315
2.5e-15, 3, "Ion", ionCode);
282316
}
283-
//anti hyper hydrogen 4 ground state
317+
// anti hyper hydrogen 4 ground state
284318
ionCode = -1010010040;
285319
if (!db->GetParticle(ionCode)) {
286320
db->AddParticle("AntiHyperhydrog4", "AntiHyperhydrog4", 3.922434, kFALSE,
287321
2.5e-15, 3, "Ion", ionCode);
288322
}
289-
//hyper helium 4 ground state
323+
// hyper helium 4 ground state
290324
ionCode = 1010020040;
291325
if (!db->GetParticle(ionCode)) {
292326
db->AddParticle("Hyperhelium4", "Hyperhelium4", 3.921728, kFALSE,
293327
2.5e-15, 6, "Ion", ionCode);
294328
}
295-
//anti hyper helium 4 ground state
329+
// anti hyper helium 4 ground state
296330
ionCode = -1010020040;
297331
if (!db->GetParticle(ionCode)) {
298332
db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.921728, kFALSE,
@@ -642,9 +676,9 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db, double monopoleMa
642676
0.040, -3, "Resonance", -ionCode);
643677
}
644678

645-
//Additional Hidden Strangeness Pentaquarks
679+
// Additional Hidden Strangeness Pentaquarks
646680

647-
//Ps +/-
681+
// Ps +/-
648682
ionCode = 9322132;
649683
if (!db->GetParticle(ionCode)) {
650684
db->AddParticle("Ps_Plus_1870", "Ps_Plus_1870", 1.870, kFALSE,
@@ -682,7 +716,7 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db, double monopoleMa
682716
0.10, -3, "Resonance", -ionCode);
683717
}
684718

685-
//Ps0
719+
// Ps0
686720
ionCode = 9322131;
687721
if (!db->GetParticle(ionCode)) {
688722
db->AddParticle("Ps_0_1870", "Ps_0_1870", 1.870, kFALSE,
@@ -786,14 +820,14 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db, double monopoleMa
786820

787821
// d*(2380) - dibaryon resonance
788822

789-
ionCode = 900010020; //Arbitrary choice - as deuteron but with leading 9 instead of 10
823+
ionCode = 900010020; // Arbitrary choice - as deuteron but with leading 9 instead of 10
790824
if (!db->GetParticle(ionCode)) {
791825
db->AddParticle("d*_2380", "d*_2380", 2.38, kFALSE,
792826
0.070, 3, "Resonance", ionCode);
793827
}
794828
db->AddAntiParticle("d*_2380_bar", -ionCode);
795829

796-
//Sexaquark (uuddss): compact, neutral and stable hypothetical bound state (arxiv.org/abs/1708.08951)
830+
// Sexaquark (uuddss): compact, neutral and stable hypothetical bound state (arxiv.org/abs/1708.08951)
797831
ionCode = 900000020;
798832
if (!db->GetParticle(ionCode)) {
799833
db->AddParticle("Sexaquark", "Sexaquark", 2.0, kTRUE, 0.0, 0, "Special", ionCode);
@@ -826,4 +860,4 @@ inline void O2DatabasePDG::addParticlesFromExternalFile(TDatabasePDG* db)
826860

827861
} // namespace o2
828862

829-
#endif //O2_O2DATABASEPDG_H
863+
#endif // O2_O2DATABASEPDG_H

‎Steer/src/MCKinematicsReader.cxx‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "Steer/MCKinematicsReader.h"
1414
#include "SimulationDataFormat/MCEventHeader.h"
1515
#include "SimulationDataFormat/TrackReference.h"
16+
#include "SimulationDataFormat/MonopoleParticles.h"
17+
#include "SimulationDataFormat/O2DatabasePDG.h"
1618
#include <TChain.h>
1719
#include <stdexcept>
1820
#include <string>
@@ -82,6 +84,29 @@ void MCKinematicsReader::initTracksForSource(int source) const
8284
}
8385
}
8486

87+
namespace
88+
{
89+
// Makes the monopole mass used by the simulation, which is stored in the event header, the
90+
// one of O2DatabasePDG; otherwise MCTrack::GetMass() (and GetEnergy(), GetRapidity(), ...) would
91+
// give the default mass for monopoles of a run with another G4.monopoleMass.
92+
void applyMonopoleMass(o2::dataformats::MCEventHeader const& header)
93+
{
94+
if (!header.hasInfo(o2::sim::MonopoleMassHeaderKey)) {
95+
return;
96+
}
97+
bool valid = false;
98+
const double mass = header.getInfo<double>(o2::sim::MonopoleMassHeaderKey, valid);
99+
if (!valid || !(mass > 0.)) {
100+
return;
101+
}
102+
if (o2::O2DatabasePDG::hasMonopoleMassOverride() && std::abs(o2::O2DatabasePDG::getMonopoleMass() - mass) > 1.e-6 * mass) {
103+
LOG(warn) << "Kinematics with different monopole masses (" << o2::O2DatabasePDG::getMonopoleMass() << " and " << mass
104+
<< " GeV) are read in the same process; monopole masses will be wrong for one of them";
105+
}
106+
o2::O2DatabasePDG::setMonopoleMass(mass);
107+
}
108+
} // namespace
109+
85110
void MCKinematicsReader::loadTracksForSourceAndEvent(int source, int event) const
86111
{
87112
auto chain = mInputChains[source];
@@ -95,6 +120,17 @@ void MCKinematicsReader::loadTracksForSourceAndEvent(int source, int event) cons
95120
mTracks[source][event] = new std::vector<o2::MCTrack>;
96121
*mTracks[source][event] = *loadtracks;
97122
delete loadtracks;
123+
// the monopole mass has to be known before the tracks are used
124+
if (auto hbr = chain->GetBranch("MCEventHeader.")) {
125+
o2::dataformats::MCEventHeader* header = nullptr;
126+
hbr->SetAddress(&header);
127+
hbr->GetEntry(event);
128+
if (header) {
129+
applyMonopoleMass(*header);
130+
}
131+
delete header;
132+
hbr->ResetAddress();
133+
}
98134
}
99135
}
100136
}
@@ -120,6 +156,7 @@ void MCKinematicsReader::loadHeadersForSource(int source) const
120156
for (int event = 0; event < br->GetEntries(); ++event) {
121157
br->GetEntry(event);
122158
mHeaders[source][event] = *header;
159+
applyMonopoleMass(mHeaders[source][event]);
123160
}
124161
delete header;
125162
header = nullptr;

‎run/O2HitMerger.cxx‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141

4242
#include "PrimaryServerState.h"
4343
#include <SimConfig/SimConfig.h>
44+
#include <SimConfig/G4Params.h>
45+
#include <CommonUtils/ConfigurableParam.h>
46+
#include <SimulationDataFormat/MonopoleParticles.h>
4447
#include <TPCSimulation/Detector.h>
4548
#include <ITSSimulation/Detector.h>
4649
#include <MFTSimulation/Detector.h>
@@ -147,6 +150,23 @@ void O2HitMerger::InitTask()
147150
// we didn't manage to get a configuration --> better to fail
148151
LOG(fatal) << "No configuration received. Aborting";
149152
}
153+
// The config key-values are not applied to the ConfigurableParams in this process. The merger needs the G4 ones
154+
// to record the monopole mass in the event headers (G4.monopole, G4.monopoleMass), so apply those.
155+
// Note: G4 parameters given via a config file (--configFile) are not applied here.
156+
{
157+
std::string g4Tokens;
158+
std::stringstream tokens(o2::conf::SimConfig::Instance().getKeyValueString());
159+
std::string token;
160+
while (std::getline(tokens, token, ';')) {
161+
const auto first = token.find_first_not_of(" \t");
162+
if (first != std::string::npos && token.compare(first, 3, "G4.") == 0) {
163+
g4Tokens += token.substr(first) + ";";
164+
}
165+
}
166+
if (!g4Tokens.empty()) {
167+
o2::conf::ConfigurableParam::updateFromString(g4Tokens);
168+
}
169+
}
150170
mAsService = o2::conf::SimConfig::Instance().asService();
151171
mForwardKine = o2::conf::SimConfig::Instance().forwardKine();
152172
mWriteToDisc = o2::conf::SimConfig::Instance().writeToDisc();
@@ -694,6 +714,12 @@ bool O2HitMerger::mergeAndFlushData()
694714
}
695715
}
696716

717+
// record the mass used for the monopoles, such that readers of the kinematics
718+
// (MCTrack::GetMass() and derived quantities) do not fall back to the default
719+
if (eventheader && o2::conf::G4Params::Instance().monopole) {
720+
eventheader->putInfo<double>(o2::sim::MonopoleMassHeaderKey, o2::conf::G4Params::Instance().monopoleMass);
721+
}
722+
697723
// now see which events can be discarded in any case due to no hits
698724
if (confref.isFilterOutNoHitEvents()) {
699725
if (eventheader && eventheader->getMCEventStats().getNHits() == 0) {

0 commit comments

Comments
 (0)