1616#include " CommonConstants/PhysicsConstants.h"
1717#include " CommonDataFormat/InteractionRecord.h"
1818
19+ #include " DataFormatsFT0/LookUpTable.h"
20+ #include " FT0Base/Constants.h"
21+ #include < map>
22+ #include < array>
23+ #include < regex>
24+ #include < string>
25+
1926#include " TMath.h"
2027#include " TRandom.h"
2128#include < TH1F.h>
@@ -35,24 +42,84 @@ namespace o2::ft0
3542template <typename Float>
3643Float signalForm_i (Float x)
3744{
38- using namespace std ;
39- Float const a = -0.45458 ;
40- Float const b = -0.83344945 ;
41- return x > Float (0 ) ? -(exp (b * x) - exp (a * x)) / Float (7.8446501 ) : Float (0 );
45+ float p0, p1, p2, p3, p4, p5, p6, p7;
46+ p0 = 1.30853 ;
47+ p1 = 0.516807 ;
48+ p2 = 3.36714 ;
49+ p3 = -1.01206 ;
50+ p4 = 1.42832 ;
51+ p5 = 1.1589 ;
52+ p6 = 1.22019 ;
53+ p7 = 0.426818 ;
54+
55+ Double_t val = 0 ;
56+
57+ if (x > p3) {
58+ Double_t x1 = x - p3;
59+ Double_t arg1 = (log (x1) - p0) / p1;
60+ val += p2 * (1.0 / (x1 * p1 * sqrt (2 * TMath::Pi ()))) * exp (-0.5 * arg1 * arg1);
61+ }
62+
63+ if (x > p7) {
64+ Double_t x2 = x - p7;
65+ Double_t arg2 = (log (x2) - p4) / p5;
66+ val += p6 * (1.0 / (x2 * p5 * sqrt (2 * TMath::Pi ()))) * exp (-0.5 * arg2 * arg2);
67+ }
68+
69+ return val;
4270};
4371
4472// integrated signal shape function
4573inline float signalForm_integral (float x)
4674{
47- using namespace std ;
48- double const a = -0.45458 ;
49- double const b = -0.83344945 ;
50- if (x < 0 ) {
51- x = 0 ;
75+ float p0, p1, p2, p3, p4, p5, p6, p7;
76+ p0 = 1.30853 ;
77+ p1 = 0.516807 ;
78+ p2 = 3.36714 ;
79+ p3 = -1.01206 ;
80+ p4 = 1.42832 ;
81+ p5 = 1.1589 ;
82+ p6 = 1.22019 ;
83+ p7 = 0.426818 ;
84+ Double_t val = 0 ;
85+
86+ if (x > p3) {
87+ Double_t x1 = x - p3;
88+ Double_t z1 = (log (x1) - p0) / (sqrt (2 ) * p1);
89+ val += p2 * 0.5 * (1 + TMath::Erf (z1)); // norm1 * CDF1
5290 }
53- return -(exp (b * x) / b - exp (a * x) / a) / 7.8446501 ;
91+
92+ if (x > p7) {
93+ Double_t x2 = x - p7;
94+ Double_t z2 = (log (x2) - p4) / (sqrt (2 ) * p5);
95+ val += p6 * 0.5 * (1 + TMath::Erf (z2)); // norm2 * CDF2
96+ }
97+
98+ return val;
99+ };
100+ /*
101+ // signal shape function
102+ template <typename Float>
103+ Float signalForm_i(Float x)
104+ {
105+ using namespace std;
106+ Float const a = -0.45458;
107+ Float const b = -0.83344945;
108+ return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0);
54109};
55110
111+ // integrated signal shape function
112+ inline float signalForm_integral(float x)
113+ {
114+ using namespace std;
115+ double const a = -0.45458;
116+ double const b = -0.83344945;
117+ if (x < 0) {
118+ x = 0;
119+ }
120+ return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501;
121+ };
122+ */
56123// SIMD version of the integrated signal shape function
57124inline Vc::float_v signalForm_integralVc (Vc::float_v x)
58125{
@@ -249,8 +316,64 @@ void Digitizer::storeBC(BCCache& bc,
249316 if (bc.hits .empty ()) {
250317 return ;
251318 }
319+ // Initialize mapping channelID -> PM hash and PM side (A/C) using FT0 LUT
320+ static bool pmLutInitialized = false ;
321+ static std::array<uint8_t , o2::ft0::Constants::sNCHANNELS_PM > mChID2PMhash {};
322+ static std::map<uint8_t , bool > mMapPMhash2isAside ; // hashed PM -> is A side
323+
324+ if (!pmLutInitialized) {
325+ std::map<std::string, uint8_t > mapFEE2hash; // module name -> hashed PM id
326+ uint8_t tcmHash = 0 ;
327+
328+ const auto & lut = o2::ft0::SingleLUT::Instance ().getVecMetadataFEE ();
329+ auto lutSorted = lut;
330+ std::sort (lutSorted.begin (), lutSorted.end (),
331+ [](const auto & first, const auto & second) { return first.mModuleName < second.mModuleName ; });
332+
333+ uint8_t binPos = 0 ;
334+ for (const auto & lutEntry : lutSorted) {
335+ const auto & moduleName = lutEntry.mModuleName ;
336+ const auto & moduleType = lutEntry.mModuleType ;
337+ const auto & strChID = lutEntry.mChannelID ;
338+
339+ auto [it, inserted] = mapFEE2hash.insert ({moduleName, binPos});
340+ if (inserted) {
341+ if (moduleName.find (" PMA" ) != std::string::npos) {
342+ mMapPMhash2isAside .insert ({binPos, true });
343+ } else if (moduleName.find (" PMC" ) != std::string::npos) {
344+ mMapPMhash2isAside .insert ({binPos, false });
345+ }
346+ ++binPos;
347+ }
348+
349+ if (std::regex_match (strChID, std::regex (" ^[0-9]{1,3}$" ))) {
350+ int chID = std::stoi (strChID);
351+ if (chID < o2::ft0::Constants::sNCHANNELS_PM ) {
352+ mChID2PMhash [chID] = mapFEE2hash[moduleName];
353+ } else {
354+ LOG (fatal) << " Incorrect LUT entry: chID " << strChID << " | " << moduleName;
355+ }
356+ } else if (moduleType != " TCM" ) {
357+ LOG (fatal) << " Non-TCM module w/o numerical chID: chID " << strChID << " | " << moduleName;
358+ } else { // TCM
359+ tcmHash = mapFEE2hash[moduleName];
360+ }
361+ }
362+
363+ pmLutInitialized = true ;
364+ }
365+
252366 int n_hit_A = 0 , n_hit_C = 0 , mean_time_A = 0 , mean_time_C = 0 ;
253367 int summ_ampl_A = 0 , summ_ampl_C = 0 ;
368+ int sum_A_ampl = 0 , sum_C_ampl = 0 ;
369+ int nPMTs = mGeometry .NCellsA * 4 + mGeometry .NCellsC * 4 ;
370+ std::vector<int > sum_ampl_ipmt (nPMTs, 0 );
371+ // Per-PM summed charge (like in digits2trgFT0)
372+ std::map<uint8_t , int > mapPMhash2sumAmpl;
373+ for (const auto & entry : mMapPMhash2isAside ) {
374+ mapPMhash2sumAmpl.insert ({entry.first , 0 });
375+ }
376+
254377 int vertex_time;
255378 const auto & params = FT0DigParam::Instance ();
256379 int first = digitsCh.size (), nStored = 0 ;
@@ -297,6 +420,10 @@ void Digitizer::storeBC(BCCache& bc,
297420 if (is_time_in_signal_gate) {
298421 chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsCFDinADCgate );
299422 chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsEventInTVDC );
423+ // Sum channel charge per PM (similar logic as in digits2trgFT0)
424+ if (ipmt < o2::ft0::Constants::sNCHANNELS_PM ) {
425+ mapPMhash2sumAmpl[mChID2PMhash [static_cast <uint8_t >(ipmt)]] += static_cast <int >(amp);
426+ }
300427 }
301428 digitsCh.emplace_back (ipmt, smeared_time, int (amp), chain);
302429 nStored++;
@@ -308,6 +435,8 @@ void Digitizer::storeBC(BCCache& bc,
308435 continue ;
309436 }
310437
438+ sum_ampl_ipmt[ipmt] += amp;
439+
311440 if (is_A_side) {
312441 n_hit_A++;
313442 summ_ampl_A += amp;
@@ -318,17 +447,47 @@ void Digitizer::storeBC(BCCache& bc,
318447 mean_time_C += smeared_time;
319448 }
320449 }
450+
451+ for (size_t i = 0 ; i < sum_ampl_ipmt.size (); i++) {
452+ sum_ampl_ipmt[i] = sum_ampl_ipmt[i] >> 3 ;
453+ if (i < 4 * mGeometry .NCellsA ) {
454+ sum_A_ampl += sum_ampl_ipmt[i];
455+ } else {
456+ sum_C_ampl += sum_ampl_ipmt[i];
457+ }
458+ }
459+
460+ // Sum over PMs (using per-PM map) for debug/monitoring
461+ int sum_PM_ampl_debug = 0 ;
462+ int sum_PM_ampl_A_debug = 0 ;
463+ int sum_PM_ampl_C_debug = 0 ;
464+ for (const auto & entry : mapPMhash2sumAmpl) {
465+ int pmAmpl = (entry.second >> 3 );
466+ sum_PM_ampl_debug += pmAmpl;
467+ auto itSide = mMapPMhash2isAside .find (entry.first );
468+ if (itSide != mMapPMhash2isAside .end ()) {
469+ if (itSide->second ) {
470+ sum_PM_ampl_A_debug += pmAmpl;
471+ } else {
472+ sum_PM_ampl_C_debug += pmAmpl;
473+ }
474+ }
475+ }
476+ LOG (debug) << " Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug
477+ << " A-side=" << sum_PM_ampl_A_debug
478+ << " C-side=" << sum_PM_ampl_C_debug;
479+
321480 Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0 ;
322481 is_A = n_hit_A > 0 ;
323482 is_C = n_hit_C > 0 ;
324- is_Central = summ_ampl_A + summ_ampl_C >= params.mtrg_central_trh ;
325- is_SemiCentral = summ_ampl_A + summ_ampl_C >= params.mtrg_semicentral_trh ;
483+ is_Central = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_central_trh ;
484+ is_SemiCentral = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_semicentral_trh && !is_Central ;
326485 uint32_t amplA = is_A ? summ_ampl_A * 0.125 : -5000 ; // sum amplitude A side / 8 (hardware)
327486 uint32_t amplC = is_C ? summ_ampl_C * 0.125 : -5000 ; // sum amplitude C side / 8 (hardware)
328487 int timeA = is_A ? mean_time_A / n_hit_A : -5000 ; // average time A side
329488 int timeC = is_C ? mean_time_C / n_hit_C : -5000 ; // average time C side
330489 vertex_time = (timeC - timeA) * 0.5 ;
331- isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_gate && vertex_time < params.mTime_trg_gate );
490+ isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_vertex_gate && vertex_time < params.mTime_trg_vertex_gate );
332491 LOG (debug) << " A " << is_A << " timeA " << timeA << " mean_time_A " << mean_time_A << " n_hit_A " << n_hit_A << " C " << is_C << " timeC " << timeC << " mean_time_C " << mean_time_C << " n_hit_C " << n_hit_C << " vertex_time " << vertex_time;
333492 Triggers triggers;
334493 bool isLaser = false ;
0 commit comments