28#include "TDatabasePDG.h"
31Ort::Env
global_env(ORT_LOGGING_LEVEL_WARNING,
"GlobalEnv");
36void Scaler::load(
const std::string&
filename)
39 if (!
file.is_open()) {
40 throw std::runtime_error(
"Error: Could not open scaler file!");
43 std::string json_str((std::istreambuf_iterator<char>(
file)), std::istreambuf_iterator<char>());
46 rapidjson::Document doc;
47 doc.Parse(json_str.c_str());
49 if (doc.HasParseError()) {
50 throw std::runtime_error(
"Error: JSON parsing failed!");
53 normal_min = jsonArrayToVector(doc[
"normal"][
"min"]);
54 normal_max = jsonArrayToVector(doc[
"normal"][
"max"]);
55 outlier_center = jsonArrayToVector(doc[
"outlier"][
"center"]);
56 outlier_scale = jsonArrayToVector(doc[
"outlier"][
"scale"]);
59std::vector<double> Scaler::inverse_transform(
const std::vector<double>& input)
61 std::vector<double>
output;
62 for (
int i = 0;
i < input.size(); ++
i) {
63 if (
i < input.size() - 2) {
64 output.push_back(input[
i] * (normal_max[
i] - normal_min[
i]) + normal_min[
i]);
66 output.push_back(input[
i] * outlier_scale[
i - (input.size() - 2)] + outlier_center[
i - (input.size() - 2)]);
73std::vector<double> Scaler::jsonArrayToVector(
const rapidjson::Value& jsonArray)
75 std::vector<double>
vec;
76 for (
int i = 0;
i < jsonArray.Size(); ++
i) {
77 vec.push_back(jsonArray[
i].GetDouble());
84ONNXGenerator::ONNXGenerator(Ort::Env& shared_env,
const std::string& model_path)
85 : env(shared_env), session(nullptr)
87 Ort::SessionOptions session_options;
88 session_options.SetIntraOpNumThreads(1);
89 session = Ort::Session(env, model_path.c_str(), session_options);
92std::vector<double> ONNXGenerator::generate_sample()
94 Ort::AllocatorWithDefaultOptions allocator;
97 std::vector<float>
z(100);
99 v = rand_gen.Gaus(0.0, 1.0);
103 std::vector<int64_t> input_shape = {1, 100};
105 Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
108 Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
109 memory_info,
z.data(),
z.size(), input_shape.data(), input_shape.size());
111 const char* input_names[] = {
"z"};
112 const char* output_names[] = {
"output"};
113 auto output_tensors = session.Run(Ort::RunOptions{
nullptr}, input_names, &input_tensor, 1, output_names, 1);
116 float* output_data = output_tensors.front().GetTensorMutableData<
float>();
118 auto output_tensor_info = output_tensors.front().GetTensorTypeAndShapeInfo();
119 size_t output_data_size = output_tensor_info.GetElementCount();
120 std::vector<double>
output;
121 for (
int i = 0;
i < output_data_size; ++
i) {
122 output.push_back(output_data[
i]);
143constexpr double kFcLxIn = 82.428409;
144constexpr double kRodROut = 254.25 + 2.2;
145constexpr double kLooperRadialReach = 10.;
146constexpr double kTPCActiveRMin = kFcLxIn - kLooperRadialReach;
147constexpr double kTPCActiveRMax = kRodROut + kLooperRadialReach;
150bool GenTPCLoopers::isInTPCActiveVolume(
double vx,
double vy)
const
152 const double vt = std::sqrt(vx * vx + vy * vy);
153 return (vt >= kTPCActiveRMin && vt <= kTPCActiveRMax);
156void GenTPCLoopers::setGeomProtection(
bool protect)
158 mGeomProtection = protect;
159 if (mGeomProtection) {
160 LOG(
debug) <<
"TPC loopers geometrical protection: ON (accepting vertices with "
161 << kTPCActiveRMin <<
" <= Vt <= " << kTPCActiveRMax <<
" cm)";
163 LOG(warning) <<
"TPC loopers geometrical protection: OFF - loopers will be generated outside the TPC active volume as well.";
167bool GenTPCLoopers::ownsCollision(
const std::vector<o2::steer::EventPart>& parts,
int source)
175 int highest = std::numeric_limits<int>::min();
176 for (
const auto& part : parts) {
177 highest = std::max(highest, part.sourceID);
182bool GenTPCLoopers::ownsContextCollisions()
195 const auto contextAndPrefix = simConfig.getCollContextFilenameAndEventPrefix();
196 const std::string contextName = contextAndPrefix.first.empty() ?
"collisioncontext.root" : contextAndPrefix.first;
197 const std::string& simPrefix = contextAndPrefix.second;
198 if (!std::filesystem::exists(contextName)) {
203 LOG(warn) <<
"Could not read the collision context from " << contextName
204 <<
"; loopers will be generated without checking for other sources";
207 const auto source = context->findSimPrefix(simPrefix);
209 if (context->getSimPrefixes().size() <= 1) {
212 LOG(warn) <<
"Simulation prefix " << simPrefix <<
" not found in the collision context " << contextName
213 <<
", which is shared by " << context->getSimPrefixes().size() <<
" sources; loopers will remain OFF";
217 unsigned int nPresent = 0, nOwned = 0;
218 for (
const auto& parts : context->getEventParts()) {
219 if (ownsCollision(parts,
source)) {
222 if (std::find_if(parts.begin(), parts.end(), [
source](
const auto& part) { return part.sourceID == source; }) != parts.end()) {
226 LOG(info) <<
"Loopers: source " << simPrefix <<
" owns " << nOwned <<
" of the " << nPresent
227 <<
" collisions it takes part in, out of " << context->getEventParts().size() <<
" in " << contextName;
231void GenTPCLoopers::setupContextCoverage()
240 mEventLooperTime.clear();
241 mSourceID = mCollisionContext ? mCollisionContext->findSimPrefix(
o2::conf::SimConfig::Instance().getCollContextFilenameAndEventPrefix().second) : -1;
245 const auto& parts = mCollisionContext->getEventParts();
246 std::vector<int> nUses;
247 double ownedTime = 0.;
248 for (
size_t c = 0;
c < parts.size() &&
c < mInteractionTimeRecords.size(); ++
c) {
249 const double gap = (
c + 1 < mInteractionTimeRecords.size())
250 ? mInteractionTimeRecords[
c + 1].bc2ns() - mInteractionTimeRecords[
c].bc2ns()
251 : mTimeEnd - mInteractionTimeRecords[
c].bc2ns();
252 const double owned = ownsCollision(parts[
c], mSourceID) ? gap : 0.;
254 for (
const auto& part : parts[
c]) {
255 if (part.sourceID != mSourceID) {
258 if (part.entryID >= (
int)mEventLooperTime.size()) {
259 mEventLooperTime.resize(part.entryID + 1, 0.);
260 nUses.resize(part.entryID + 1, 0);
262 mEventLooperTime[part.entryID] += owned;
263 nUses[part.entryID]++;
266 for (
size_t e = 0;
e < mEventLooperTime.size(); ++
e) {
267 mEventLooperTime[
e] /= std::max(nUses[
e], 1);
269 LOG(info) <<
"Loopers: the " << mEventLooperTime.size() <<
" events of this simulation share the "
270 << ownedTime <<
" ns of the timeframe it owns";
273GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton,
274 std::string poisson, std::string gauss, std::string scaler_pair,
275 std::string scaler_compton)
278 std::ifstream model_file[2];
279 model_file[0].open(model_pairs);
280 model_file[1].open(model_compton);
281 if (!model_file[0].is_open() || model_file[0].peek() == std::ifstream::traits_type::eof()) {
282 LOG(fatal) <<
"Error: Pairs model file is empty or does not exist!";
285 if (!model_file[1].is_open() || model_file[1].peek() == std::ifstream::traits_type::eof()) {
286 LOG(fatal) <<
"Error: Compton model file is empty or does not exist!";
289 model_file[0].close();
290 model_file[1].close();
292 std::ifstream scaler_file[2];
293 scaler_file[0].open(scaler_pair);
294 scaler_file[1].open(scaler_compton);
295 if (!scaler_file[0].is_open() || scaler_file[0].peek() == std::ifstream::traits_type::eof()) {
296 LOG(fatal) <<
"Error: Pairs scaler file is empty or does not exist!";
299 if (!scaler_file[1].is_open() || scaler_file[1].peek() == std::ifstream::traits_type::eof()) {
300 LOG(fatal) <<
"Error: Compton scaler file is empty or does not exist!";
303 scaler_file[0].close();
304 scaler_file[1].close();
306 if (poisson !=
"" && poisson !=
"None" && poisson !=
"none") {
307 std::ifstream poisson_file(poisson);
308 if (!poisson_file.is_open() || poisson_file.peek() == std::ifstream::traits_type::eof()) {
309 LOG(fatal) <<
"Error: Poisson file is empty or does not exist!";
312 poisson_file >> mPoisson[0] >> mPoisson[1] >> mPoisson[2];
313 poisson_file.close();
318 if (gauss !=
"" && gauss !=
"None" && gauss !=
"none") {
319 std::ifstream gauss_file(gauss);
320 if (!gauss_file.is_open() || gauss_file.peek() == std::ifstream::traits_type::eof()) {
321 LOG(fatal) <<
"Error: Gauss file is empty or does not exist!";
324 gauss_file >> mGauss[0] >> mGauss[1] >> mGauss[2] >> mGauss[3];
329 mONNX_pair = std::make_unique<ONNXGenerator>(
global_env, model_pairs);
330 mScaler_pair = std::make_unique<Scaler>();
331 mScaler_pair->load(scaler_pair);
332 mONNX_compton = std::make_unique<ONNXGenerator>(
global_env, model_compton);
333 mScaler_compton = std::make_unique<Scaler>();
334 mScaler_compton->load(scaler_compton);
337Bool_t GenTPCLoopers::generateEvent()
342 mGenElectrons.clear();
344 unsigned int nLoopers, nLoopersPairs, nLoopersCompton;
345 LOG(
debug) <<
"mCurrentEvent is " << mCurrentEvent;
348 mTimeLimit = (mCurrentEvent < (
int)mEventLooperTime.size())
349 ? mEventLooperTime[mCurrentEvent]
350 : ((mCurrentEvent < mInteractionTimeRecords.
size() - 1) ? mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns() : mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns());
351 LOG(
debug) <<
"Current event time: " << mTimeLimit <<
" ns";
354 nLoopers = mFlatGasOrbit ? (mFlatGasNumber * (mTimeLimit / o2::constants::lhc::LHCOrbitNS)) : (mFlatGasNumber * (mTimeLimit / mIntTimeRecMean));
355 nLoopersPairs =
static_cast<unsigned int>(std::round(nLoopers * mLoopsFractionPairs));
356 nLoopersCompton = nLoopers - nLoopersPairs;
357 SetNLoopers(nLoopersPairs, nLoopersCompton);
358 LOG(info) <<
"Flat gas loopers: " << nLoopers <<
" (pairs: " << nLoopersPairs <<
", compton: " << nLoopersCompton <<
")";
359 generateEvent(mTimeLimit);
364 mNLoopersPairs =
static_cast<unsigned int>(std::round(mMultiplier[0] * PoissonPairs()));
365 LOG(
debug) <<
"Generated loopers pairs (Poisson): " << mNLoopersPairs;
368 mNLoopersCompton =
static_cast<unsigned int>(std::round(mMultiplier[1] * GaussianElectrons()));
369 LOG(
debug) <<
"Generated compton electrons (Gauss): " << mNLoopersCompton;
372 for (
int i = 0;
i < mNLoopersPairs; ++
i) {
373 std::vector<double> pair = mONNX_pair->generate_sample();
375 std::vector<double> transformed_pair = mScaler_pair->inverse_transform(pair);
376 mGenPairs.push_back(transformed_pair);
379 for (
int i = 0;
i < mNLoopersCompton; ++
i) {
380 std::vector<double> electron = mONNX_compton->generate_sample();
382 std::vector<double> transformed_electron = mScaler_compton->inverse_transform(electron);
383 mGenElectrons.push_back(transformed_electron);
389Bool_t GenTPCLoopers::generateEvent(
double time_limit)
391 LOG(info) <<
"Time constraint for loopers: " << time_limit <<
" ns";
393 for (
int i = 0;
i < mNLoopersPairs; ++
i) {
394 std::vector<double> pair = mONNX_pair->generate_sample();
396 std::vector<double> transformed_pair = mScaler_pair->inverse_transform(pair);
397 transformed_pair[9] = gRandom->Uniform(0., time_limit);
398 mGenPairs.push_back(transformed_pair);
401 for (
int i = 0;
i < mNLoopersCompton; ++
i) {
402 std::vector<double> electron = mONNX_compton->generate_sample();
404 std::vector<double> transformed_electron = mScaler_compton->inverse_transform(electron);
405 transformed_electron[6] = gRandom->Uniform(0., time_limit);
406 mGenElectrons.push_back(transformed_electron);
408 LOG(info) <<
"Generated Particles with time limit";
412std::vector<TParticle> GenTPCLoopers::importParticles()
414 std::vector<TParticle> particles;
415 const double mass_e = TDatabasePDG::Instance()->GetParticle(11)->Mass();
416 const double mass_p = TDatabasePDG::Instance()->GetParticle(-11)->Mass();
418 mNSkippedCompton = 0;
420 for (
auto& pair : mGenPairs) {
421 double px_e, py_e, pz_e, px_p, py_p, pz_p;
422 double vx, vy, vz,
time;
423 double e_etot, p_etot;
427 if (mGeomProtection && !isInTPCActiveVolume(pair[6], pair[7])) {
441 e_etot = TMath::Sqrt(px_e * px_e + py_e * py_e + pz_e * pz_e + mass_e * mass_e);
442 p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mass_p * mass_p);
444 TParticle electron(11, 1, -1, -1, -1, -1, px_e, py_e, pz_e, e_etot, vx, vy, vz,
time / 1e9);
448 particles.push_back(electron);
450 TParticle positron(-11, 1, -1, -1, -1, -1, px_p, py_p, pz_p, p_etot, vx, vy, vz,
time / 1e9);
453 particles.push_back(positron);
456 for (
auto& compton : mGenElectrons) {
458 double vx, vy, vz,
time;
460 if (mGeomProtection && !isInTPCActiveVolume(compton[3], compton[4])) {
471 etot = TMath::Sqrt(px * px + py * py + pz * pz + mass_e * mass_e);
473 TParticle electron(11, 1, -1, -1, -1, -1, px, py, pz, etot, vx, vy, vz,
time / 1e9);
477 particles.push_back(electron);
483unsigned int GenTPCLoopers::PoissonPairs()
485 unsigned int poissonValue;
488 poissonValue = mRandGen.Poisson(mPoisson[0]);
489 }
while (poissonValue < mPoisson[1] || poissonValue > mPoisson[2]);
494unsigned int GenTPCLoopers::GaussianElectrons()
496 unsigned int gaussValue;
499 gaussValue = mRandGen.Gaus(mGauss[0], mGauss[1]);
500 }
while (gaussValue < mGauss[2] || gaussValue > mGauss[3]);
505void GenTPCLoopers::SetNLoopers(
unsigned int nsig_pair,
unsigned int nsig_compton)
508 mNLoopersPairs = nsig_pair;
509 mNLoopersCompton = nsig_compton;
512 LOG(info) <<
"Poissonian parameters correctly loaded.";
514 mNLoopersPairs = nsig_pair;
517 LOG(info) <<
"Gaussian parameters correctly loaded.";
519 mNLoopersCompton = nsig_compton;
524void GenTPCLoopers::SetMultiplier(
const std::array<float, 2>& mult)
528 if (mult[0] < 0 || mult[1] < 0) {
529 LOG(fatal) <<
"Error: Multiplier values must be non-negative!";
532 LOG(info) <<
"Multiplier values set to: Pair = " << mult[0] <<
", Compton = " << mult[1];
533 mMultiplier[0] = mult[0];
534 mMultiplier[1] = mult[1];
538void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit)
542 if (nloopers_orbit > 0) {
543 mFlatGasOrbit =
true;
544 mFlatGasNumber = nloopers_orbit;
545 LOG(info) <<
"Flat gas loopers will be generated using orbit reference.";
547 mFlatGasOrbit =
false;
549 LOG(warn) <<
"Warning: Number of loopers per event must be non-negative! Switching option off.";
553 mFlatGasNumber = number;
557 mContextFile = std::filesystem::exists(
"collisioncontext.root") ? TFile::Open(
"collisioncontext.root") : nullptr;
559 mInteractionTimeRecords = mCollisionContext ? mCollisionContext->getEventRecords() : std::vector<o2::InteractionTimeRecord>{};
561 if (mInteractionTimeRecords.empty()) {
565 LOG(warn) <<
"No interaction time records in the collision context; this timeframe holds no collision";
567 mTimeEnd = tfEndIR.bc2ns();
569 LOG(info) <<
"Interaction Time records has " << mInteractionTimeRecords.size() <<
" entries.";
570 mCollisionContext->printCollisionSummary();
571 for (
int c = 0;
c < (
int)mInteractionTimeRecords.size() - 1;
c++) {
572 mIntTimeRecMean += mInteractionTimeRecords[
c + 1].bc2ns() - mInteractionTimeRecords[
c].bc2ns();
574 if (mInteractionTimeRecords.size() > 1) {
575 mIntTimeRecMean /= (mInteractionTimeRecords.size() - 1);
578 auto rate = mCollisionContext->getDigitizerInteractionRate();
579 mIntTimeRecMean =
rate > 0. ? 1.e9 /
rate : (double)o2::constants::lhc::LHCOrbitNS;
580 LOG(info) <<
"Only one collision in this timeframe; taking " << mIntTimeRecMean
581 <<
" ns as the mean interaction spacing from the interaction rate";
584 const auto& lastIR = mInteractionTimeRecords.back();
586 mTimeEnd = finalOrbitIR.bc2ns();
587 LOG(
debug) <<
"Final orbit start time: " << mTimeEnd <<
" ns while last interaction record time is " << mInteractionTimeRecords.back().bc2ns() <<
" ns";
588 setupContextCoverage();
594 LOG(info) <<
"Flat gas loopers: " << (mFlatGas ?
"ON" :
"OFF") <<
", Reference loopers number per " << (mFlatGasOrbit ?
"orbit " :
"event ") << mFlatGasNumber;
597void GenTPCLoopers::setFractionPairs(
float fractionPairs)
599 if (fractionPairs < 0 || fractionPairs > 1) {
600 LOG(fatal) <<
"Error: Loops fraction for pairs must be in the range [0, 1].";
603 mLoopsFractionPairs = fractionPairs;
604 LOG(info) <<
"Pairs fraction set to: " << mLoopsFractionPairs;
607void GenTPCLoopers::SetRate(
const std::string& rateFile,
bool isPbPb =
true,
int intRate)
610 TFile rate_file(rateFile.c_str(),
"READ");
611 if (!rate_file.IsOpen() || rate_file.IsZombie()) {
612 LOG(fatal) <<
"Error: Rate file is empty or does not exist!";
615 const char* fitName = isPbPb ?
"fitPbPb" :
"fitpp";
616 auto fit = (
TF1*)rate_file.Get(fitName);
618 LOG(fatal) <<
"Error: Could not find fit function '" << fitName <<
"' in rate file!";
621 mInteractionRate = intRate;
622 if (mInteractionRate < 0) {
623 mContextFile = std::filesystem::exists(
"collisioncontext.root") ? TFile::Open(
"collisioncontext.root") : nullptr;
624 if (!mContextFile || mContextFile->IsZombie()) {
625 LOG(fatal) <<
"Error: Interaction rate not provided and collision context file not found!";
629 mInteractionRate = std::floor(mCollisionContext->getDigitizerInteractionRate());
630 LOG(info) <<
"Interaction rate retrieved from collision context: " << mInteractionRate <<
" Hz";
631 if (mInteractionRate < 0) {
632 LOG(fatal) <<
"Error: Invalid interaction rate retrieved from collision context!";
636 auto ref =
static_cast<int>(std::floor(
fit->Eval(mInteractionRate / 1000.)));
639 LOG(fatal) <<
"Computed flat gas number reference per orbit is <=0";
642 LOG(info) <<
"Set flat gas number to " <<
ref <<
" loopers per orbit using " << fitName <<
" from " << mInteractionRate <<
" Hz interaction rate.";
644 setFlatGas(flat, -1,
ref);
648void GenTPCLoopers::SetAdjust(
float adjust)
650 if (mFlatGas && mFlatGasOrbit && adjust >= -1.f && adjust != 0.f) {
651 LOG(info) <<
"Adjusting flat gas number per orbit by " << adjust * 100.f <<
"%";
652 mFlatGasNumber =
static_cast<int>(std::round(mFlatGasNumber * (1.f + adjust)));
653 LOG(info) <<
"New flat gas number per orbit: " << mFlatGasNumber;
configStandalone Light OpenGL display display_light Apply smoothing to points Enable Z buffer Highlight links Highlight seeds Highlight sector tracks Highlight final tracks Show individual Show related Highlight grid Show only individual collision Color clusters belonging to track objects Set line Enable drawing TPC data Enable drawing TOF data Enable drawing magnetic field Set field line step count Invert colors configStandalone Heavy OpenGL display display_heavy Show tracks with ITS contribution Show tracks with TOF contribution Enable propagation of loopers Show clusters at nominal x of pad row for early transformed data Split CE tracks when they cross the central Mark clusters Mark adjacent clusters Hide rejected tracks Apply filter on tracks to be displayed Separate increase X h configStandalone Camera window renderer OpenGL display display_camera Orient the camera such that the y axis is always upwards Full Screen MultiSample Anti Downsampling Anti Enable Vertical Sync Target frame or if the backend cannot limit to the refresh Use OpenGL indirect draws to reduce number of draw calls Display h configStandalone Vulkan display display_vulkan Have uniform buffers in host accessible device memory h configStandalone Internal display display_internal Number of updates the Qt GUI may send ahead of the last one the display has before it waits for the display Opacity of the console background Number of console lines the history is shortened to this when it reaches twice the size Time in seconds an info message fades out after setInfoTime h OpenGL display display Font size Do not use Freetype for font Renderer for event Frontend to use for event ROOT macros used as track filter Console script file
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error First event to Number of events to Number of iterations to Number of iterations to Number of initial iterations excluded from const char e
Ort::Env global_env(ORT_LOGGING_LEVEL_WARNING, "GlobalEnv")
static const HBFUtils & Instance()
static SimConfig & Instance()
static DigitizationContext * loadFromFile(std::string_view filename="")
GLsizei GLsizei GLchar * source
GLdouble GLdouble GLdouble z
uint8_t int statusCode int
const track::TrackFitContext< NLayers > & fit
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"