24#include "TDatabasePDG.h"
27Ort::Env
global_env(ORT_LOGGING_LEVEL_WARNING,
"GlobalEnv");
32void Scaler::load(
const std::string&
filename)
35 if (!file.is_open()) {
36 throw std::runtime_error(
"Error: Could not open scaler file!");
39 std::string json_str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
42 rapidjson::Document doc;
43 doc.Parse(json_str.c_str());
45 if (doc.HasParseError()) {
46 throw std::runtime_error(
"Error: JSON parsing failed!");
49 normal_min = jsonArrayToVector(doc[
"normal"][
"min"]);
50 normal_max = jsonArrayToVector(doc[
"normal"][
"max"]);
51 outlier_center = jsonArrayToVector(doc[
"outlier"][
"center"]);
52 outlier_scale = jsonArrayToVector(doc[
"outlier"][
"scale"]);
55std::vector<double> Scaler::inverse_transform(
const std::vector<double>& input)
57 std::vector<double>
output;
58 for (
int i = 0;
i < input.size(); ++
i) {
59 if (
i < input.size() - 2) {
60 output.push_back(input[
i] * (normal_max[
i] - normal_min[
i]) + normal_min[
i]);
62 output.push_back(input[
i] * outlier_scale[
i - (input.size() - 2)] + outlier_center[
i - (input.size() - 2)]);
69std::vector<double> Scaler::jsonArrayToVector(
const rapidjson::Value& jsonArray)
71 std::vector<double>
vec;
72 for (
int i = 0;
i < jsonArray.Size(); ++
i) {
73 vec.push_back(jsonArray[
i].GetDouble());
80ONNXGenerator::ONNXGenerator(Ort::Env& shared_env,
const std::string& model_path)
81 : env(shared_env), session(nullptr)
83 Ort::SessionOptions session_options;
84 session_options.SetIntraOpNumThreads(1);
85 session = Ort::Session(env, model_path.c_str(), session_options);
88std::vector<double> ONNXGenerator::generate_sample()
90 Ort::AllocatorWithDefaultOptions allocator;
93 std::vector<float>
z(100);
95 v = rand_gen.Gaus(0.0, 1.0);
99 std::vector<int64_t> input_shape = {1, 100};
101 Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
104 Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
105 memory_info,
z.data(),
z.size(), input_shape.data(), input_shape.size());
107 const char* input_names[] = {
"z"};
108 const char* output_names[] = {
"output"};
109 auto output_tensors = session.Run(Ort::RunOptions{
nullptr}, input_names, &input_tensor, 1, output_names, 1);
112 float* output_data = output_tensors.front().GetTensorMutableData<
float>();
114 auto output_tensor_info = output_tensors.front().GetTensorTypeAndShapeInfo();
115 size_t output_data_size = output_tensor_info.GetElementCount();
116 std::vector<double>
output;
117 for (
int i = 0;
i < output_data_size; ++
i) {
118 output.push_back(output_data[
i]);
139constexpr double kFcLxIn = 82.428409;
140constexpr double kRodROut = 254.25 + 2.2;
141constexpr double kLooperRadialReach = 10.;
142constexpr double kTPCActiveRMin = kFcLxIn - kLooperRadialReach;
143constexpr double kTPCActiveRMax = kRodROut + kLooperRadialReach;
146bool GenTPCLoopers::isInTPCActiveVolume(
double vx,
double vy)
const
148 const double vt = std::sqrt(vx * vx + vy * vy);
149 return (vt >= kTPCActiveRMin && vt <= kTPCActiveRMax);
152void GenTPCLoopers::setGeomProtection(
bool protect)
154 mGeomProtection = protect;
155 if (mGeomProtection) {
156 LOG(
debug) <<
"TPC loopers geometrical protection: ON (accepting vertices with "
157 << kTPCActiveRMin <<
" <= Vt <= " << kTPCActiveRMax <<
" cm)";
159 LOG(warning) <<
"TPC loopers geometrical protection: OFF - loopers will be generated outside the TPC active volume as well.";
163GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton,
164 std::string poisson, std::string gauss, std::string scaler_pair,
165 std::string scaler_compton)
168 std::ifstream model_file[2];
169 model_file[0].open(model_pairs);
170 model_file[1].open(model_compton);
171 if (!model_file[0].is_open() || model_file[0].peek() == std::ifstream::traits_type::eof()) {
172 LOG(fatal) <<
"Error: Pairs model file is empty or does not exist!";
175 if (!model_file[1].is_open() || model_file[1].peek() == std::ifstream::traits_type::eof()) {
176 LOG(fatal) <<
"Error: Compton model file is empty or does not exist!";
179 model_file[0].close();
180 model_file[1].close();
182 std::ifstream scaler_file[2];
183 scaler_file[0].open(scaler_pair);
184 scaler_file[1].open(scaler_compton);
185 if (!scaler_file[0].is_open() || scaler_file[0].peek() == std::ifstream::traits_type::eof()) {
186 LOG(fatal) <<
"Error: Pairs scaler file is empty or does not exist!";
189 if (!scaler_file[1].is_open() || scaler_file[1].peek() == std::ifstream::traits_type::eof()) {
190 LOG(fatal) <<
"Error: Compton scaler file is empty or does not exist!";
193 scaler_file[0].close();
194 scaler_file[1].close();
196 if (poisson !=
"" && poisson !=
"None" && poisson !=
"none") {
197 std::ifstream poisson_file(poisson);
198 if (!poisson_file.is_open() || poisson_file.peek() == std::ifstream::traits_type::eof()) {
199 LOG(fatal) <<
"Error: Poisson file is empty or does not exist!";
202 poisson_file >> mPoisson[0] >> mPoisson[1] >> mPoisson[2];
203 poisson_file.close();
208 if (gauss !=
"" && gauss !=
"None" && gauss !=
"none") {
209 std::ifstream gauss_file(gauss);
210 if (!gauss_file.is_open() || gauss_file.peek() == std::ifstream::traits_type::eof()) {
211 LOG(fatal) <<
"Error: Gauss file is empty or does not exist!";
214 gauss_file >> mGauss[0] >> mGauss[1] >> mGauss[2] >> mGauss[3];
219 mONNX_pair = std::make_unique<ONNXGenerator>(
global_env, model_pairs);
220 mScaler_pair = std::make_unique<Scaler>();
221 mScaler_pair->load(scaler_pair);
222 mONNX_compton = std::make_unique<ONNXGenerator>(
global_env, model_compton);
223 mScaler_compton = std::make_unique<Scaler>();
224 mScaler_compton->load(scaler_compton);
227Bool_t GenTPCLoopers::generateEvent()
232 mGenElectrons.clear();
234 unsigned int nLoopers, nLoopersPairs, nLoopersCompton;
235 LOG(
debug) <<
"mCurrentEvent is " << mCurrentEvent;
236 LOG(
debug) <<
"Current event time: " << ((mCurrentEvent < mInteractionTimeRecords.size() - 1) ?
std::to_string(mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns()) :
std::
to_string(mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns())) <<
" ns";
237 LOG(
debug) <<
"Current time offset wrt BC: " << mInteractionTimeRecords[mCurrentEvent].getTimeOffsetWrtBC() <<
" ns";
238 mTimeLimit = (mCurrentEvent < mInteractionTimeRecords.size() - 1) ? mInteractionTimeRecords[mCurrentEvent + 1].bc2ns() - mInteractionTimeRecords[mCurrentEvent].bc2ns() : mTimeEnd - mInteractionTimeRecords[mCurrentEvent].bc2ns();
242 nLoopersPairs =
static_cast<unsigned int>(std::round(nLoopers * mLoopsFractionPairs));
243 nLoopersCompton = nLoopers - nLoopersPairs;
244 SetNLoopers(nLoopersPairs, nLoopersCompton);
245 LOG(info) <<
"Flat gas loopers: " << nLoopers <<
" (pairs: " << nLoopersPairs <<
", compton: " << nLoopersCompton <<
")";
246 generateEvent(mTimeLimit);
251 mNLoopersPairs =
static_cast<unsigned int>(std::round(mMultiplier[0] * PoissonPairs()));
252 LOG(
debug) <<
"Generated loopers pairs (Poisson): " << mNLoopersPairs;
255 mNLoopersCompton =
static_cast<unsigned int>(std::round(mMultiplier[1] * GaussianElectrons()));
256 LOG(
debug) <<
"Generated compton electrons (Gauss): " << mNLoopersCompton;
259 for (
int i = 0;
i < mNLoopersPairs; ++
i) {
260 std::vector<double> pair = mONNX_pair->generate_sample();
262 std::vector<double> transformed_pair = mScaler_pair->inverse_transform(pair);
263 mGenPairs.push_back(transformed_pair);
266 for (
int i = 0;
i < mNLoopersCompton; ++
i) {
267 std::vector<double> electron = mONNX_compton->generate_sample();
269 std::vector<double> transformed_electron = mScaler_compton->inverse_transform(electron);
270 mGenElectrons.push_back(transformed_electron);
276Bool_t GenTPCLoopers::generateEvent(
double time_limit)
278 LOG(info) <<
"Time constraint for loopers: " << time_limit <<
" ns";
280 for (
int i = 0;
i < mNLoopersPairs; ++
i) {
281 std::vector<double> pair = mONNX_pair->generate_sample();
283 std::vector<double> transformed_pair = mScaler_pair->inverse_transform(pair);
284 transformed_pair[9] = gRandom->Uniform(0., time_limit);
285 mGenPairs.push_back(transformed_pair);
288 for (
int i = 0;
i < mNLoopersCompton; ++
i) {
289 std::vector<double> electron = mONNX_compton->generate_sample();
291 std::vector<double> transformed_electron = mScaler_compton->inverse_transform(electron);
292 transformed_electron[6] = gRandom->Uniform(0., time_limit);
293 mGenElectrons.push_back(transformed_electron);
295 LOG(info) <<
"Generated Particles with time limit";
299std::vector<TParticle> GenTPCLoopers::importParticles()
301 std::vector<TParticle> particles;
302 const double mass_e = TDatabasePDG::Instance()->GetParticle(11)->Mass();
303 const double mass_p = TDatabasePDG::Instance()->GetParticle(-11)->Mass();
305 mNSkippedCompton = 0;
307 for (
auto& pair : mGenPairs) {
308 double px_e, py_e, pz_e, px_p, py_p, pz_p;
309 double vx, vy, vz,
time;
310 double e_etot, p_etot;
314 if (mGeomProtection && !isInTPCActiveVolume(pair[6], pair[7])) {
328 e_etot = TMath::Sqrt(px_e * px_e + py_e * py_e + pz_e * pz_e + mass_e * mass_e);
329 p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mass_p * mass_p);
331 TParticle electron(11, 1, -1, -1, -1, -1, px_e, py_e, pz_e, e_etot, vx, vy, vz,
time / 1e9);
335 particles.push_back(electron);
337 TParticle positron(-11, 1, -1, -1, -1, -1, px_p, py_p, pz_p, p_etot, vx, vy, vz,
time / 1e9);
340 particles.push_back(positron);
343 for (
auto& compton : mGenElectrons) {
345 double vx, vy, vz,
time;
347 if (mGeomProtection && !isInTPCActiveVolume(compton[3], compton[4])) {
358 etot = TMath::Sqrt(px * px + py * py + pz * pz + mass_e * mass_e);
360 TParticle electron(11, 1, -1, -1, -1, -1, px, py, pz, etot, vx, vy, vz,
time / 1e9);
364 particles.push_back(electron);
370unsigned int GenTPCLoopers::PoissonPairs()
372 unsigned int poissonValue;
375 poissonValue = mRandGen.Poisson(mPoisson[0]);
376 }
while (poissonValue < mPoisson[1] || poissonValue > mPoisson[2]);
381unsigned int GenTPCLoopers::GaussianElectrons()
383 unsigned int gaussValue;
386 gaussValue = mRandGen.Gaus(mGauss[0], mGauss[1]);
387 }
while (gaussValue < mGauss[2] || gaussValue > mGauss[3]);
392void GenTPCLoopers::SetNLoopers(
unsigned int nsig_pair,
unsigned int nsig_compton)
395 mNLoopersPairs = nsig_pair;
396 mNLoopersCompton = nsig_compton;
399 LOG(info) <<
"Poissonian parameters correctly loaded.";
401 mNLoopersPairs = nsig_pair;
404 LOG(info) <<
"Gaussian parameters correctly loaded.";
406 mNLoopersCompton = nsig_compton;
411void GenTPCLoopers::SetMultiplier(
const std::array<float, 2>& mult)
415 if (mult[0] < 0 || mult[1] < 0) {
416 LOG(fatal) <<
"Error: Multiplier values must be non-negative!";
419 LOG(info) <<
"Multiplier values set to: Pair = " << mult[0] <<
", Compton = " << mult[1];
420 mMultiplier[0] = mult[0];
421 mMultiplier[1] = mult[1];
425void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit)
429 if (nloopers_orbit > 0) {
430 mFlatGasOrbit =
true;
431 mFlatGasNumber = nloopers_orbit;
432 LOG(info) <<
"Flat gas loopers will be generated using orbit reference.";
434 mFlatGasOrbit =
false;
436 LOG(warn) <<
"Warning: Number of loopers per event must be non-negative! Switching option off.";
440 mFlatGasNumber = number;
444 mContextFile = std::filesystem::exists(
"collisioncontext.root") ? TFile::Open(
"collisioncontext.root") : nullptr;
446 mInteractionTimeRecords = mCollisionContext ? mCollisionContext->getEventRecords() : std::vector<o2::InteractionTimeRecord>{};
447 if (mInteractionTimeRecords.empty()) {
448 LOG(error) <<
"Error: No interaction time records found in the collision context!";
451 LOG(info) <<
"Interaction Time records has " << mInteractionTimeRecords.size() <<
" entries.";
452 mCollisionContext->printCollisionSummary();
454 for (
int c = 0;
c < mInteractionTimeRecords.size() - 1;
c++) {
455 mIntTimeRecMean += mInteractionTimeRecords[
c + 1].bc2ns() - mInteractionTimeRecords[
c].bc2ns();
457 mIntTimeRecMean /= (mInteractionTimeRecords.size() - 1);
460 const auto& lastIR = mInteractionTimeRecords.back();
462 mTimeEnd = finalOrbitIR.bc2ns();
463 LOG(
debug) <<
"Final orbit start time: " << mTimeEnd <<
" ns while last interaction record time is " << mInteractionTimeRecords.back().bc2ns() <<
" ns";
468 LOG(info) <<
"Flat gas loopers: " << (mFlatGas ?
"ON" :
"OFF") <<
", Reference loopers number per " << (mFlatGasOrbit ?
"orbit " :
"event ") << mFlatGasNumber;
471void GenTPCLoopers::setFractionPairs(
float fractionPairs)
473 if (fractionPairs < 0 || fractionPairs > 1) {
474 LOG(fatal) <<
"Error: Loops fraction for pairs must be in the range [0, 1].";
477 mLoopsFractionPairs = fractionPairs;
478 LOG(info) <<
"Pairs fraction set to: " << mLoopsFractionPairs;
481void GenTPCLoopers::SetRate(
const std::string& rateFile,
bool isPbPb =
true,
int intRate)
484 TFile rate_file(rateFile.c_str(),
"READ");
485 if (!rate_file.IsOpen() || rate_file.IsZombie()) {
486 LOG(fatal) <<
"Error: Rate file is empty or does not exist!";
489 const char* fitName = isPbPb ?
"fitPbPb" :
"fitpp";
490 auto fit = (
TF1*)rate_file.Get(fitName);
492 LOG(fatal) <<
"Error: Could not find fit function '" << fitName <<
"' in rate file!";
495 mInteractionRate = intRate;
496 if (mInteractionRate < 0) {
497 mContextFile = std::filesystem::exists(
"collisioncontext.root") ? TFile::Open(
"collisioncontext.root") : nullptr;
498 if (!mContextFile || mContextFile->IsZombie()) {
499 LOG(fatal) <<
"Error: Interaction rate not provided and collision context file not found!";
503 mInteractionRate = std::floor(mCollisionContext->getDigitizerInteractionRate());
504 LOG(info) <<
"Interaction rate retrieved from collision context: " << mInteractionRate <<
" Hz";
505 if (mInteractionRate < 0) {
506 LOG(fatal) <<
"Error: Invalid interaction rate retrieved from collision context!";
510 auto ref =
static_cast<int>(std::floor(
fit->Eval(mInteractionRate / 1000.)));
513 LOG(fatal) <<
"Computed flat gas number reference per orbit is <=0";
516 LOG(info) <<
"Set flat gas number to " <<
ref <<
" loopers per orbit using " << fitName <<
" from " << mInteractionRate <<
" Hz interaction rate.";
518 setFlatGas(flat, -1,
ref);
522void GenTPCLoopers::SetAdjust(
float adjust)
524 if (mFlatGas && mFlatGasOrbit && adjust >= -1.f && adjust != 0.f) {
525 LOG(info) <<
"Adjusting flat gas number per orbit by " << adjust * 100.f <<
"%";
526 mFlatGasNumber =
static_cast<int>(std::round(mFlatGasNumber * (1.f + adjust)));
527 LOG(info) <<
"New flat gas number per orbit: " << mFlatGasNumber;
Ort::Env global_env(ORT_LOGGING_LEVEL_WARNING, "GlobalEnv")
static const HBFUtils & Instance()
GLdouble GLdouble GLdouble z
constexpr double LHCOrbitNS
int32_t const char * file
const track::TrackFitContext< NLayers > & fit
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"