Project
Loading...
Searching...
No Matches
TPCLoopers.cxx
Go to the documentation of this file.
1// Copyright 2024-2025 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
16#include "CCDB/CcdbApi.h"
18#include "TF1.h"
19#include <filesystem>
22#include <iostream>
23#include <fstream>
24#include "TDatabasePDG.h"
25
26// Static Ort::Env instance for multiple onnx model loading
27Ort::Env global_env(ORT_LOGGING_LEVEL_WARNING, "GlobalEnv");
28
29// This class is responsible for loading the scaler parameters from a JSON file
30// and applying the inverse transformation to the generated data.
31
32void Scaler::load(const std::string& filename)
33{
34 std::ifstream file(filename);
35 if (!file.is_open()) {
36 throw std::runtime_error("Error: Could not open scaler file!");
37 }
38
39 std::string json_str((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
40 file.close();
41
42 rapidjson::Document doc;
43 doc.Parse(json_str.c_str());
44
45 if (doc.HasParseError()) {
46 throw std::runtime_error("Error: JSON parsing failed!");
47 }
48
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"]);
53}
54
55std::vector<double> Scaler::inverse_transform(const std::vector<double>& input)
56{
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]);
61 } else {
62 output.push_back(input[i] * outlier_scale[i - (input.size() - 2)] + outlier_center[i - (input.size() - 2)]);
63 }
64 }
65
66 return output;
67}
68
69std::vector<double> Scaler::jsonArrayToVector(const rapidjson::Value& jsonArray)
70{
71 std::vector<double> vec;
72 for (int i = 0; i < jsonArray.Size(); ++i) {
73 vec.push_back(jsonArray[i].GetDouble());
74 }
75 return vec;
76}
77
78// This class loads the ONNX model and generates samples using it.
79
80ONNXGenerator::ONNXGenerator(Ort::Env& shared_env, const std::string& model_path)
81 : env(shared_env), session(nullptr)
82{
83 Ort::SessionOptions session_options;
84 session_options.SetIntraOpNumThreads(1);
85 session = Ort::Session(env, model_path.c_str(), session_options);
86}
87
88std::vector<double> ONNXGenerator::generate_sample()
89{
90 Ort::AllocatorWithDefaultOptions allocator;
91
92 // Generate a latent vector (z)
93 std::vector<float> z(100);
94 for (auto& v : z) {
95 v = rand_gen.Gaus(0.0, 1.0);
96 }
97
98 // Prepare input tensor
99 std::vector<int64_t> input_shape = {1, 100};
100 // Get memory information
101 Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
102
103 // Create input tensor correctly
104 Ort::Value input_tensor = Ort::Value::CreateTensor<float>(
105 memory_info, z.data(), z.size(), input_shape.data(), input_shape.size());
106 // Run inference
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);
110
111 // Extract output
112 float* output_data = output_tensors.front().GetTensorMutableData<float>();
113 // Get the size of the output tensor
114 auto output_tensor_info = output_tensors.front().GetTensorTypeAndShapeInfo();
115 size_t output_data_size = output_tensor_info.GetElementCount(); // Total number of elements in the tensor
116 std::vector<double> output;
117 for (int i = 0; i < output_data_size; ++i) {
118 output.push_back(output_data[i]);
119 }
120
121 return output;
122}
123
124namespace o2
125{
126namespace eventgen
127{
128
129namespace
130{
131// Radial limits of the region from which a looper can still reach the TPC
132// sensitive gas. The field cage positions are those used by the
133// "ExcludeFCGap" selection in o2::tpc::Detector::ProcessHits()
134// A looper can enter the sensitive gas from just outside it, so an additional margin is set
135// with a factor two over the largest radial excursion which was observed in validation (~4.2 cm).
136//
137// No cut is applied on z because loopers spiral the field lines and a vertex as far as |z| = 283 cm
138// feeds hits into the gas. A cut here would discard loopers that produce TPC signals.
139constexpr double kFcLxIn = 82.428409; // cm, inner field cage strips
140constexpr double kRodROut = 254.25 + 2.2; // cm, outer field cage rods plus their radial size
141constexpr double kLooperRadialReach = 10.; // cm, margin for the helix sweep
142constexpr double kTPCActiveRMin = kFcLxIn - kLooperRadialReach;
143constexpr double kTPCActiveRMax = kRodROut + kLooperRadialReach;
144} // namespace
145
146bool GenTPCLoopers::isInTPCActiveVolume(double vx, double vy) const
147{
148 const double vt = std::sqrt(vx * vx + vy * vy);
149 return (vt >= kTPCActiveRMin && vt <= kTPCActiveRMax);
150}
151
152void GenTPCLoopers::setGeomProtection(bool protect)
153{
154 mGeomProtection = protect;
155 if (mGeomProtection) {
156 LOG(debug) << "TPC loopers geometrical protection: ON (accepting vertices with "
157 << kTPCActiveRMin << " <= Vt <= " << kTPCActiveRMax << " cm)";
158 } else {
159 LOG(warning) << "TPC loopers geometrical protection: OFF - loopers will be generated outside the TPC active volume as well.";
160 }
161}
162
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)
166{
167 // Checking if the model files exist and are not empty
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!";
173 exit(1);
174 }
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!";
177 exit(1);
178 }
179 model_file[0].close();
180 model_file[1].close();
181 // Checking if the scaler files exist and are not empty
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!";
187 exit(1);
188 }
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!";
191 exit(1);
192 }
193 scaler_file[0].close();
194 scaler_file[1].close();
195 // Checking if the poisson file exists and it's not empty
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!";
200 exit(1);
201 } else {
202 poisson_file >> mPoisson[0] >> mPoisson[1] >> mPoisson[2];
203 poisson_file.close();
204 mPoissonSet = true;
205 }
206 }
207 // Checking if the gauss file exists and it's not empty
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!";
212 exit(1);
213 } else {
214 gauss_file >> mGauss[0] >> mGauss[1] >> mGauss[2] >> mGauss[3];
215 gauss_file.close();
216 mGaussSet = true;
217 }
218 }
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);
225}
226
227Bool_t GenTPCLoopers::generateEvent()
228{
229 // Clear the vector of pairs
230 mGenPairs.clear();
231 // Clear the vector of compton electrons
232 mGenElectrons.clear();
233 if (mFlatGas) {
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();
239 // With flat gas the number of loopers are adapted based on time interval widths
240 // The denominator is either the LHC orbit (if mFlatGasOrbit is true) or the mean interaction time record interval
241 nLoopers = mFlatGasOrbit ? (mFlatGasNumber * (mTimeLimit / o2::constants::lhc::LHCOrbitNS)) : (mFlatGasNumber * (mTimeLimit / mIntTimeRecMean));
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);
247 mCurrentEvent++;
248 } else {
249 // Set number of loopers if poissonian params are available
250 if (mPoissonSet) {
251 mNLoopersPairs = static_cast<unsigned int>(std::round(mMultiplier[0] * PoissonPairs()));
252 LOG(debug) << "Generated loopers pairs (Poisson): " << mNLoopersPairs;
253 }
254 if (mGaussSet) {
255 mNLoopersCompton = static_cast<unsigned int>(std::round(mMultiplier[1] * GaussianElectrons()));
256 LOG(debug) << "Generated compton electrons (Gauss): " << mNLoopersCompton;
257 }
258 // Generate pairs
259 for (int i = 0; i < mNLoopersPairs; ++i) {
260 std::vector<double> pair = mONNX_pair->generate_sample();
261 // Apply the inverse transformation using the scaler
262 std::vector<double> transformed_pair = mScaler_pair->inverse_transform(pair);
263 mGenPairs.push_back(transformed_pair);
264 }
265 // Generate compton electrons
266 for (int i = 0; i < mNLoopersCompton; ++i) {
267 std::vector<double> electron = mONNX_compton->generate_sample();
268 // Apply the inverse transformation using the scaler
269 std::vector<double> transformed_electron = mScaler_compton->inverse_transform(electron);
270 mGenElectrons.push_back(transformed_electron);
271 }
272 }
273 return true;
274}
275
276Bool_t GenTPCLoopers::generateEvent(double time_limit)
277{
278 LOG(info) << "Time constraint for loopers: " << time_limit << " ns";
279 // Generate pairs
280 for (int i = 0; i < mNLoopersPairs; ++i) {
281 std::vector<double> pair = mONNX_pair->generate_sample();
282 // Apply the inverse transformation using the scaler
283 std::vector<double> transformed_pair = mScaler_pair->inverse_transform(pair);
284 transformed_pair[9] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds
285 mGenPairs.push_back(transformed_pair);
286 }
287 // Generate compton electrons
288 for (int i = 0; i < mNLoopersCompton; ++i) {
289 std::vector<double> electron = mONNX_compton->generate_sample();
290 // Apply the inverse transformation using the scaler
291 std::vector<double> transformed_electron = mScaler_compton->inverse_transform(electron);
292 transformed_electron[6] = gRandom->Uniform(0., time_limit); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds
293 mGenElectrons.push_back(transformed_electron);
294 }
295 LOG(info) << "Generated Particles with time limit";
296 return true;
297}
298
299std::vector<TParticle> GenTPCLoopers::importParticles()
300{
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();
304 mNSkippedPairs = 0;
305 mNSkippedCompton = 0;
306 // Get looper pairs from the event
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;
311 // The generative model is not currently fully constrained to the TPC geometry, so it places
312 // significant fraction of the vertices outside the drift gas.
313 // These are now dropped before they reach the transport.
314 if (mGeomProtection && !isInTPCActiveVolume(pair[6], pair[7])) {
315 mNSkippedPairs++;
316 continue;
317 }
318 px_e = pair[0];
319 py_e = pair[1];
320 pz_e = pair[2];
321 px_p = pair[3];
322 py_p = pair[4];
323 pz_p = pair[5];
324 vx = pair[6];
325 vy = pair[7];
326 vz = pair[8];
327 time = pair[9];
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);
330 // Push the electron
331 TParticle electron(11, 1, -1, -1, -1, -1, px_e, py_e, pz_e, e_etot, vx, vy, vz, time / 1e9);
332 // Setting HepMC status code != 1 to avoid detecting the loopers as physical primaries
333 electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(2, 0).fullEncoding);
334 electron.SetBit(ParticleStatus::kToBeDone, true);
335 particles.push_back(electron);
336 // Push the positron
337 TParticle positron(-11, 1, -1, -1, -1, -1, px_p, py_p, pz_p, p_etot, vx, vy, vz, time / 1e9);
338 positron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(2, 0).fullEncoding);
339 positron.SetBit(ParticleStatus::kToBeDone, true);
340 particles.push_back(positron);
341 }
342 // Get compton electrons from the event
343 for (auto& compton : mGenElectrons) {
344 double px, py, pz;
345 double vx, vy, vz, time;
346 double etot;
347 if (mGeomProtection && !isInTPCActiveVolume(compton[3], compton[4])) {
348 mNSkippedCompton++;
349 continue;
350 }
351 px = compton[0];
352 py = compton[1];
353 pz = compton[2];
354 vx = compton[3];
355 vy = compton[4];
356 vz = compton[5];
357 time = compton[6];
358 etot = TMath::Sqrt(px * px + py * py + pz * pz + mass_e * mass_e);
359 // Push the electron
360 TParticle electron(11, 1, -1, -1, -1, -1, px, py, pz, etot, vx, vy, vz, time / 1e9);
361 // Setting HepMC status code != 1 to avoid detecting the loopers as physical primaries
362 electron.SetStatusCode(o2::mcgenstatus::MCGenStatusEncoding(2, 0).fullEncoding);
363 electron.SetBit(ParticleStatus::kToBeDone, true);
364 particles.push_back(electron);
365 }
366
367 return particles;
368}
369
370unsigned int GenTPCLoopers::PoissonPairs()
371{
372 unsigned int poissonValue;
373 do {
374 // Generate a Poisson-distributed random number with mean mPoisson[0]
375 poissonValue = mRandGen.Poisson(mPoisson[0]);
376 } while (poissonValue < mPoisson[1] || poissonValue > mPoisson[2]); // Regenerate if out of range
377
378 return poissonValue;
379}
380
381unsigned int GenTPCLoopers::GaussianElectrons()
382{
383 unsigned int gaussValue;
384 do {
385 // Generate a Normal-distributed random number with mean mGass[0] and stddev mGauss[1]
386 gaussValue = mRandGen.Gaus(mGauss[0], mGauss[1]);
387 } while (gaussValue < mGauss[2] || gaussValue > mGauss[3]); // Regenerate if out of range
388
389 return gaussValue;
390}
391
392void GenTPCLoopers::SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton)
393{
394 if (mFlatGas) {
395 mNLoopersPairs = nsig_pair;
396 mNLoopersCompton = nsig_compton;
397 } else {
398 if (mPoissonSet) {
399 LOG(info) << "Poissonian parameters correctly loaded.";
400 } else {
401 mNLoopersPairs = nsig_pair;
402 }
403 if (mGaussSet) {
404 LOG(info) << "Gaussian parameters correctly loaded.";
405 } else {
406 mNLoopersCompton = nsig_compton;
407 }
408 }
409}
410
411void GenTPCLoopers::SetMultiplier(const std::array<float, 2>& mult)
412{
413 // Multipliers will work only if the poissonian and gaussian parameters are set
414 // otherwise they will be ignored
415 if (mult[0] < 0 || mult[1] < 0) {
416 LOG(fatal) << "Error: Multiplier values must be non-negative!";
417 exit(1);
418 } else {
419 LOG(info) << "Multiplier values set to: Pair = " << mult[0] << ", Compton = " << mult[1];
420 mMultiplier[0] = mult[0];
421 mMultiplier[1] = mult[1];
422 }
423}
424
425void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit)
426{
427 mFlatGas = flat;
428 if (mFlatGas) {
429 if (nloopers_orbit > 0) {
430 mFlatGasOrbit = true;
431 mFlatGasNumber = nloopers_orbit;
432 LOG(info) << "Flat gas loopers will be generated using orbit reference.";
433 } else {
434 mFlatGasOrbit = false;
435 if (number < 0) {
436 LOG(warn) << "Warning: Number of loopers per event must be non-negative! Switching option off.";
437 mFlatGas = false;
438 mFlatGasNumber = -1;
439 } else {
440 mFlatGasNumber = number;
441 }
442 }
443 if (mFlatGas) {
444 mContextFile = std::filesystem::exists("collisioncontext.root") ? TFile::Open("collisioncontext.root") : nullptr;
445 mCollisionContext = mContextFile ? (o2::steer::DigitizationContext*)mContextFile->Get("DigitizationContext") : 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!";
449 exit(1);
450 } else {
451 LOG(info) << "Interaction Time records has " << mInteractionTimeRecords.size() << " entries.";
452 mCollisionContext->printCollisionSummary();
453 }
454 for (int c = 0; c < mInteractionTimeRecords.size() - 1; c++) {
455 mIntTimeRecMean += mInteractionTimeRecords[c + 1].bc2ns() - mInteractionTimeRecords[c].bc2ns();
456 }
457 mIntTimeRecMean /= (mInteractionTimeRecords.size() - 1); // Average interaction time record used as reference
458 const auto& hbfUtils = o2::raw::HBFUtils::Instance();
459 // Get the start time of the second orbit after the last interaction record
460 const auto& lastIR = mInteractionTimeRecords.back();
461 o2::InteractionRecord finalOrbitIR(0, lastIR.orbit + 2); // Final orbit, BC = 0
462 mTimeEnd = finalOrbitIR.bc2ns();
463 LOG(debug) << "Final orbit start time: " << mTimeEnd << " ns while last interaction record time is " << mInteractionTimeRecords.back().bc2ns() << " ns";
464 }
465 } else {
466 mFlatGasNumber = -1;
467 }
468 LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber;
469}
470
471void GenTPCLoopers::setFractionPairs(float fractionPairs)
472{
473 if (fractionPairs < 0 || fractionPairs > 1) {
474 LOG(fatal) << "Error: Loops fraction for pairs must be in the range [0, 1].";
475 exit(1);
476 }
477 mLoopsFractionPairs = fractionPairs;
478 LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs;
479}
480
481void GenTPCLoopers::SetRate(const std::string& rateFile, bool isPbPb = true, int intRate)
482{
483 // Checking if the rate file exists and is not empty
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!";
487 exit(1);
488 }
489 const char* fitName = isPbPb ? "fitPbPb" : "fitpp";
490 auto fit = (TF1*)rate_file.Get(fitName);
491 if (!fit) {
492 LOG(fatal) << "Error: Could not find fit function '" << fitName << "' in rate file!";
493 exit(1);
494 }
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!";
500 exit(1);
501 }
502 mCollisionContext = (o2::steer::DigitizationContext*)mContextFile->Get("DigitizationContext");
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!";
507 exit(1);
508 }
509 }
510 auto ref = static_cast<int>(std::floor(fit->Eval(mInteractionRate / 1000.))); // fit expects rate in kHz
511 rate_file.Close();
512 if (ref <= 0) {
513 LOG(fatal) << "Computed flat gas number reference per orbit is <=0";
514 exit(1);
515 } else {
516 LOG(info) << "Set flat gas number to " << ref << " loopers per orbit using " << fitName << " from " << mInteractionRate << " Hz interaction rate.";
517 auto flat = true;
518 setFlatGas(flat, -1, ref);
519 }
520}
521
522void GenTPCLoopers::SetAdjust(float adjust)
523{
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;
528 }
529}
530
531} // namespace eventgen
532} // namespace o2
std::ostringstream debug
int16_t time
Definition RawEventData.h:4
int32_t i
void output(const std::map< std::string, ChannelStat > &channels)
Definition rawdump.cxx:197
@ kToBeDone
uint32_t c
Definition RawData.h:2
Ort::Env global_env(ORT_LOGGING_LEVEL_WARNING, "GlobalEnv")
const GLdouble * v
Definition glcorearb.h:832
GLdouble GLdouble GLdouble z
Definition glcorearb.h:843
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)
Definition common.h:52
std::string filename()
std::vector< o2::ctf::BufferType > vec
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"