@@ -207,6 +207,17 @@ class GenTPCLoopers : public Generator
207207 mScaler_compton -> load (scaler_compton );
208208 Generator ::setTimeUnit (1.0 );
209209 Generator ::setPositionUnit (1.0 );
210+ mContextFile = std ::filesystem ::exists ("collisioncontext.root" ) ? TFile ::Open ("collisioncontext.root" ) : nullptr ;
211+ mCollisionContext = mContextFile ? (o2 ::steer ::DigitizationContext * )mContextFile -> Get ("DigitizationContext" ) : nullptr ;
212+ mInteractionTimeRecords = mCollisionContext ? mCollisionContext -> getEventRecords () : std ::vector < o2 ::InteractionTimeRecord > {};
213+ if (mInteractionTimeRecords .empty ())
214+ {
215+ LOG (warn ) << "Error: No interaction time records found in the collision context!" ;
216+ exit (1 );
217+ } else {
218+ LOG (info ) << "Interaction Time records has " << mInteractionTimeRecords .size () << " entries." ;
219+ mCollisionContext -> printCollisionSummary ();
220+ }
210221 }
211222
212223 Bool_t generateEvent () override
@@ -215,21 +226,61 @@ class GenTPCLoopers : public Generator
215226 mGenPairs .clear ();
216227 // Clear the vector of compton electrons
217228 mGenElectrons .clear ();
218- // Set number of loopers if poissonian params are available
219- if (mPoissonSet )
220- {
221- mNLoopersPairs = static_cast < short int > (std ::round (mMultiplier [0 ] * PoissonPairs ()));
222- }
223- if (mGaussSet )
229+ if (mFlatGas )
224230 {
225- mNLoopersCompton = static_cast < short int > (std ::round (mMultiplier [1 ] * GaussianElectrons ()));
226- }
231+ unsigned int nLoopers , nLoopersPairs , nLoopersCompton ;
232+ LOG (info ) << "mCurrentEvent is " << mCurrentEvent ;
233+ LOG (info ) << "Current event time: " << ((mCurrentEvent < mInteractionTimeRecords .size () - 1 ) ? std ::to_string (mInteractionTimeRecords [mCurrentEvent + 1 ].bc2ns () - mInteractionTimeRecords [mCurrentEvent ].bc2ns ()) : "Final Event till the end" ) << " ns" ;
234+ LOG (info ) << "Current time offset wrt BC: " << mInteractionTimeRecords [mCurrentEvent ].getTimeOffsetWrtBC () << " ns" ;
235+ mTimeLimit = (mCurrentEvent < mInteractionTimeRecords .size () - 1 ) ? mInteractionTimeRecords [mCurrentEvent + 1 ].bc2ns () - mInteractionTimeRecords [mCurrentEvent ].bc2ns () : mInteractionTimeRecords [1 ].bc2ns () - mInteractionTimeRecords [0 ].bc2ns ();
236+ // With Flat Gas number of loopers are adapted based on time interval widths
237+ nLoopers = mFlatGasNumber * (mTimeLimit / mStartingTime );
238+ nLoopersPairs = static_cast < unsigned int > (std ::round (nLoopers * mLoopsFractionPairs ));
239+ nLoopersCompton = nLoopers - nLoopersPairs ;
240+ SetNLoopers (nLoopersPairs , nLoopersCompton );
241+ LOG (info ) << "Flat gas loopers: " << nLoopers << " (pairs: " << nLoopersPairs << ", compton: " << nLoopersCompton << ")" ;
242+ generateEvent (mTimeLimit );
243+ mCurrentEvent ++ ;
244+ } else {
245+ // Set number of loopers if poissonian params are available
246+ if (mPoissonSet )
247+ {
248+ mNLoopersPairs = static_cast < unsigned int > (std ::round (mMultiplier [0 ] * PoissonPairs ()));
249+ }
250+ if (mGaussSet )
251+ {
252+ mNLoopersCompton = static_cast < unsigned int > (std ::round (mMultiplier [1 ] * GaussianElectrons ()));
253+ }
254+ // Generate pairs
255+ for (int i = 0 ; i < mNLoopersPairs ; ++ i )
256+ {
257+ std ::vector < double > pair = mONNX_pair -> generate_sample ();
258+ // Apply the inverse transformation using the scaler
259+ std ::vector < double > transformed_pair = mScaler_pair -> inverse_transform (pair );
260+ mGenPairs .push_back (transformed_pair );
261+ }
262+ // Generate compton electrons
263+ for (int i = 0 ; i < mNLoopersCompton ; ++ i )
264+ {
265+ std ::vector < double > electron = mONNX_compton -> generate_sample ();
266+ // Apply the inverse transformation using the scaler
267+ std ::vector < double > transformed_electron = mScaler_compton -> inverse_transform (electron );
268+ mGenElectrons .push_back (transformed_electron );
269+ }
270+ }
271+ return true;
272+ }
273+
274+ Bool_t generateEvent (double & time_limit )
275+ {
276+ LOG (info ) << "Time constraint for loopers: " << time_limit << " ns" ;
227277 // Generate pairs
228278 for (int i = 0 ; i < mNLoopersPairs ; ++ i )
229279 {
230280 std ::vector < double > pair = mONNX_pair -> generate_sample ();
231281 // Apply the inverse transformation using the scaler
232282 std ::vector < double > transformed_pair = mScaler_pair -> inverse_transform (pair );
283+ transformed_pair [9 ] = gRandom -> Uniform (0. , time_limit ); // Regenerate time, scaling is not needed because time_limit is already in nanoseconds
233284 mGenPairs .push_back (transformed_pair );
234285 }
235286 // Generate compton electrons
@@ -238,8 +289,10 @@ class GenTPCLoopers : public Generator
238289 std ::vector < double > electron = mONNX_compton -> generate_sample ();
239290 // Apply the inverse transformation using the scaler
240291 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
241293 mGenElectrons .push_back (transformed_electron );
242294 }
295+ LOG (info ) << "Generated Particles with time limit" ;
243296 return true;
244297 }
245298
@@ -301,9 +354,9 @@ class GenTPCLoopers : public Generator
301354 return true;
302355 }
303356
304- short int PoissonPairs ()
357+ unsigned int PoissonPairs ()
305358 {
306- short int poissonValue ;
359+ unsigned int poissonValue ;
307360 do
308361 {
309362 // Generate a Poisson-distributed random number with mean mPoisson[0]
@@ -313,9 +366,9 @@ class GenTPCLoopers : public Generator
313366 return poissonValue ;
314367 }
315368
316- short int GaussianElectrons ()
369+ unsigned int GaussianElectrons ()
317370 {
318- short int gaussValue ;
371+ unsigned int gaussValue ;
319372 do
320373 {
321374 // Generate a Normal-distributed random number with mean mGass[0] and stddev mGauss[1]
@@ -325,7 +378,7 @@ class GenTPCLoopers : public Generator
325378 return gaussValue ;
326379 }
327380
328- void SetNLoopers (short int & nsig_pair , short int & nsig_compton )
381+ void SetNLoopers (unsigned int & nsig_pair , unsigned int & nsig_compton )
329382 {
330383 if (mPoissonSet ) {
331384 LOG (info ) << "Poissonian parameters correctly loaded." ;
@@ -354,6 +407,41 @@ class GenTPCLoopers : public Generator
354407 }
355408 }
356409
410+ void setFlatGas (Bool_t & flat , const Int_t & number = -1 )
411+ {
412+ mFlatGas = flat ;
413+ if (mFlatGas )
414+ {
415+ if (number < 0 )
416+ {
417+ LOG (warn ) << "Warning: Number of loopers per event must be non-negative! Switching option off." ;
418+ mFlatGas = false;
419+ mFlatGasNumber = -1 ;
420+ }
421+ else
422+ {
423+ mFlatGasNumber = number ;
424+ mStartingTime = mInteractionTimeRecords [1 ].bc2ns () - mInteractionTimeRecords [0 ].bc2ns (); // Time width of first event
425+ }
426+ }
427+ else
428+ {
429+ mFlatGasNumber = -1 ;
430+ }
431+ LOG (info ) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF" ) << ", Number of loopers per event: " << mFlatGasNumber ;
432+ }
433+
434+ void setFractionPairs (float & fractionPairs )
435+ {
436+ if (fractionPairs < 0 || fractionPairs > 1 )
437+ {
438+ LOG (fatal ) << "Error: Loops fraction for pairs must be in the range [0, 1]." ;
439+ exit (1 );
440+ }
441+ mLoopsFractionPairs = fractionPairs ;
442+ LOG (info ) << "Pairs fraction set to: " << mLoopsFractionPairs ;
443+ }
444+
357445 private :
358446 std ::unique_ptr < ONNXGenerator > mONNX_pair = nullptr ;
359447 std ::unique_ptr < ONNXGenerator > mONNX_compton = nullptr ;
@@ -363,8 +451,8 @@ class GenTPCLoopers : public Generator
363451 double mGauss [4 ] = {0.0 , 0.0 , 0.0 , 0.0 }; // Mean, Std, Min, Max
364452 std ::vector < std ::vector < double >> mGenPairs ;
365453 std ::vector < std ::vector < double >> mGenElectrons ;
366- short int mNLoopersPairs = -1 ;
367- short int mNLoopersCompton = -1 ;
454+ unsigned int mNLoopersPairs = -1 ;
455+ unsigned int mNLoopersCompton = -1 ;
368456 std ::array < float , 2 > mMultiplier = {1. , 1. };
369457 bool mPoissonSet = false;
370458 bool mGaussSet = false;
@@ -374,6 +462,15 @@ class GenTPCLoopers : public Generator
374462 TDatabasePDG * mPDG = TDatabasePDG ::Instance ();
375463 double mMass_e = mPDG -> GetParticle (11 )-> Mass ();
376464 double mMass_p = mPDG -> GetParticle (-11 )-> Mass ();
465+ int mCurrentEvent = 0 ; // Current event number, used for adaptive loopers
466+ TFile * mContextFile = nullptr ; // Input collision context file
467+ o2 ::steer ::DigitizationContext * mCollisionContext = nullptr ; // Pointer to the digitization context
468+ std ::vector < o2 ::InteractionTimeRecord > mInteractionTimeRecords ; // Interaction time records from collision context
469+ Bool_t mFlatGas = false; // Flag to indicate if flat gas loopers are used
470+ Int_t mFlatGasNumber = -1 ; // Number of flat gas loopers per event
471+ double mStartingTime = 1.0 ; // First BC time interval in ns used for reference in adaptive loopers
472+ double mTimeLimit = 0.0 ; // Time limit for the current event
473+ float mLoopsFractionPairs = 0.08 ; // Fraction of loopers from Pairs
377474};
378475
379476} // namespace eventgen
@@ -387,8 +484,8 @@ class GenTPCLoopers : public Generator
387484FairGenerator *
388485 Generator_TPCLoopers (std ::string model_pairs = "tpcloopmodel.onnx" , std ::string model_compton = "tpcloopmodelcompton.onnx" ,
389486 std ::string poisson = "poisson.csv" , std ::string gauss = "gauss.csv" , std ::string scaler_pair = "scaler_pair.json" ,
390- std ::string scaler_compton = "scaler_compton.json" , std ::array < float , 2 > mult = {1. , 1. }, short int nloopers_pairs = 1 ,
391- short int nloopers_compton = 1 )
487+ std ::string scaler_compton = "scaler_compton.json" , std ::array < float , 2 > mult = {1. , 1. }, unsigned int nloopers_pairs = 1 ,
488+ unsigned int nloopers_compton = 1 )
392489{
393490 // Expand all environment paths
394491 model_pairs = gSystem -> ExpandPathName (model_pairs .c_str ()) ;
@@ -450,4 +547,75 @@ FairGenerator *
450547 generator -> SetNLoopers (nloopers_pairs , nloopers_compton );
451548 generator -> SetMultiplier (mult );
452549 return generator ;
550+ }
551+
552+ // Generator with flat gas loopers. Number of loopers starts from a reference value and changes
553+ // based on the BC time intervals in each event.
554+ FairGenerator *
555+ Generator_TPCLoopersFlat (std ::string model_pairs = "tpcloopmodel.onnx" , std ::string model_compton = "tpcloopmodelcompton.onnx" ,
556+ std ::string scaler_pair = "scaler_pair.json" , std ::string scaler_compton = "scaler_compton.json" ,
557+ bool flat_gas = true, const int loops_num = 100 , float fraction_pairs = 0.08 )
558+ {
559+ // Expand all environment paths
560+ model_pairs = gSystem -> ExpandPathName (model_pairs .c_str ()) ;
561+ model_compton = gSystem -> ExpandPathName (model_compton .c_str ());
562+ scaler_pair = gSystem -> ExpandPathName (scaler_pair .c_str ());
563+ scaler_compton = gSystem -> ExpandPathName (scaler_compton .c_str ());
564+ const std ::array < std ::string , 2 > models = {model_pairs , model_compton };
565+ const std ::array < std ::string , 2 > local_names = {"WGANpair.onnx" , "WGANcompton.onnx" };
566+ const std ::array < bool , 2 > isAlien = {models [0 ].starts_with ("alien://" ), models [1 ].starts_with ("alien://" )};
567+ const std ::array < bool , 2 > isCCDB = {models [0 ].starts_with ("ccdb://" ), models [1 ].starts_with ("ccdb://" )};
568+ if (std ::any_of (isAlien .begin (), isAlien .end (), [](bool v )
569+ { return v ; }))
570+ {
571+ if (!gGrid )
572+ {
573+ TGrid ::Connect ("alien://" );
574+ if (!gGrid )
575+ {
576+ LOG (fatal ) << "AliEn connection failed, check token." ;
577+ exit (1 );
578+ }
579+ }
580+ for (size_t i = 0 ; i < models .size (); ++ i )
581+ {
582+ if (isAlien [i ] && !TFile ::Cp (models [i ].c_str (), local_names [i ].c_str ()))
583+ {
584+ LOG (fatal ) << "Error: Model file " << models [i ] << " does not exist!" ;
585+ exit (1 );
586+ }
587+ }
588+ }
589+ if (std ::any_of (isCCDB .begin (), isCCDB .end (), [](bool v )
590+ { return v ; }))
591+ {
592+ o2 ::ccdb ::CcdbApi ccdb_api ;
593+ ccdb_api .init ("http://alice-ccdb.cern.ch" );
594+ for (size_t i = 0 ; i < models .size (); ++ i )
595+ {
596+ if (isCCDB [i ])
597+ {
598+ auto model_path = models [i ].substr (7 ); // Remove "ccdb://"
599+ // Treat filename if provided in the CCDB path
600+ auto extension = model_path .find (".onnx" );
601+ if (extension != std ::string ::npos )
602+ {
603+ auto last_slash = model_path .find_last_of ('/' );
604+ model_path = model_path .substr (0 , last_slash );
605+ }
606+ std ::map < std ::string , std ::string > filter ;
607+ if (!ccdb_api .retrieveBlob (model_path , "./" , filter , o2 ::ccdb ::getCurrentTimestamp (), false, local_names [i ].c_str ()))
608+ {
609+ LOG (fatal ) << "Error: issues in retrieving " << model_path << " from CCDB!" ;
610+ exit (1 );
611+ }
612+ }
613+ }
614+ }
615+ model_pairs = isAlien [0 ] || isCCDB [0 ] ? local_names [0 ] : model_pairs ;
616+ model_compton = isAlien [1 ] || isCCDB [1 ] ? local_names [1 ] : model_compton ;
617+ auto generator = new o2 ::eventgen ::GenTPCLoopers (model_pairs , model_compton , "" , "" , scaler_pair , scaler_compton );
618+ generator -> setFractionPairs (fraction_pairs );
619+ generator -> setFlatGas (flat_gas , loops_num );
620+ return generator ;
453621}
0 commit comments