4444#include " lardata/DetectorInfoServices/DetectorClocksService.h"
4545#include " lardata/DetectorInfoServices/DetectorPropertiesService.h"
4646
47+ // Calibration database includes for electron lifetime
48+ #include " larevt/CalibrationDBI/Providers/DBFolder.h"
49+ #include " wda.h"
50+
4751// SBND includes
4852#include " sbndcode/OpDetSim/sbndPDMapAlg.hh"
4953#include " sbnobj/Common/Reco/LightCalo.h"
@@ -121,6 +125,15 @@ class sbnd::LightCaloProducer : public art::EDProducer {
121125 // Returns the weighted mean of the light vector
122126 double CalcMean (std::vector<double > total_light, std::vector<double > total_err);
123127
128+ // Struct to hold electron lifetime data from database
129+ struct ELifetimeInfo {
130+ double tau_tpc0;
131+ double tau_tpc1;
132+ };
133+
134+ // Helper to get electron lifetime from database
135+ ELifetimeInfo GetELifetimeFromDB (uint64_t run);
136+
124137 // fcl parameters
125138 std::vector<std::string> fopflash_producer_v;
126139 std::vector<std::string> fopflash_ara_producer_v;
@@ -159,6 +172,13 @@ class sbnd::LightCaloProducer : public art::EDProducer {
159172 double fxarapucavuv_viseff;
160173 double fxarapucavis_eff;
161174
175+ // Electron lifetime database parameters
176+ bool fuse_elifetime_db;
177+ std::string felifetime_db_file;
178+ std::string felifetime_db_tag;
179+ std::unique_ptr<lariov::DBFolder> felifetime_db;
180+ std::map<uint32_t , ELifetimeInfo> felifetime_cache;
181+
162182 std::vector<float > fopdet_vuv_eff;
163183 std::vector<float > fopdet_vis_eff;
164184 std::vector<int > fopdet_mask;
@@ -237,6 +257,14 @@ sbnd::LightCaloProducer::LightCaloProducer(fhicl::ParameterSet const& p)
237257 fxarapucavuv_viseff = p.get <double >(" XArapucaVUVVISEff" );
238258 fxarapucavis_eff = p.get <double >(" XArapucaVISEff" );
239259
260+ // Electron lifetime database configuration
261+ fuse_elifetime_db = p.get <bool >(" UseELifetimeDB" , false );
262+ if (fuse_elifetime_db) {
263+ felifetime_db_file = p.get <std::string>(" ELifetimeDBFile" );
264+ felifetime_db_tag = p.get <std::string>(" ELifetimeDBTag" );
265+ felifetime_db = std::make_unique<lariov::DBFolder>(felifetime_db_file, " " , " " , felifetime_db_tag, true , false );
266+ }
267+
240268 // fill efficiency vectors
241269 fopdet_vuv_eff.resize (nchan,0 .);
242270 fopdet_vis_eff.resize (nchan,0 .);
@@ -466,10 +494,23 @@ void sbnd::LightCaloProducer::CalculateCalorimetry(art::Event& e,
466494 bool has_sp0 = false ;
467495 bool has_sp1 = false ;
468496
497+ // Get electron lifetime (from database if enabled, otherwise from detector properties)
498+ double elifetime_tpc0, elifetime_tpc1;
499+ if (fuse_elifetime_db) {
500+ ELifetimeInfo elife_info = GetELifetimeFromDB (e.id ().run ());
501+ elifetime_tpc0 = elife_info.tau_tpc0 ;
502+ elifetime_tpc1 = elife_info.tau_tpc1 ;
503+ } else {
504+ elifetime_tpc0 = det_prop.ElectronLifetime ();
505+ elifetime_tpc1 = det_prop.ElectronLifetime ();
506+ }
507+
469508 for (size_t i=0 ; i < slice_hits_v.size (); i++){
470509 auto hit = slice_hits_v[i];
471510 auto drift_time = clock_data.TPCTick2TrigTime (hit->PeakTime ());
472- double atten_correction = std::exp (drift_time/det_prop.ElectronLifetime ()); // exp(us/us)
511+ // Use TPC-specific electron lifetime
512+ double elifetime = (hit->WireID ().TPC == 0 ) ? elifetime_tpc0 : elifetime_tpc1;
513+ double atten_correction = std::exp (drift_time/elifetime); // exp(us/us)
473514 auto hit_plane = hit->View ();
474515 plane_charge.at (hit_plane) += hit->Integral ()*atten_correction*(1 /fcal_area_const.at (hit_plane));
475516 plane_hits.at (hit_plane)++;
@@ -495,9 +536,10 @@ void sbnd::LightCaloProducer::CalculateCalorimetry(art::Event& e,
495536 if (hit->View () !=bestHits) continue ;
496537 const auto &position (sp->XYZ ());
497538 geo::Point_t xyz (position[0 ],position[1 ],position[2 ]);
498- // correct for e- attenuation
539+ // correct for e- attenuation using TPC-specific lifetime
499540 auto drift_time = clock_data.TPCTick2TrigTime (hit->PeakTime ());
500- double atten_correction = std::exp (drift_time/det_prop.ElectronLifetime ()); // exp(us/us)
541+ double elifetime = (hit->WireID ().TPC == 0 ) ? elifetime_tpc0 : elifetime_tpc1;
542+ double atten_correction = std::exp (drift_time/elifetime); // exp(us/us)
501543 double charge = (1 /fcal_area_const.at (hit->View ()))*atten_correction*hit->Integral ();
502544 if (xyz.X () < 0 ) has_sp0 = true ;
503545 else has_sp1 = true ;
@@ -779,4 +821,34 @@ double sbnd::LightCaloProducer::CalcMean(std::vector<double> total_gamma, std::v
779821 return total_mean;
780822}
781823
824+ sbnd::LightCaloProducer::ELifetimeInfo sbnd::LightCaloProducer::GetELifetimeFromDB (uint64_t run) {
825+ // Check cache first
826+ if (felifetime_cache.count (run)) {
827+ return felifetime_cache.at (run);
828+ }
829+
830+ // Query database - translate run into fake "timestamp"
831+ // (same convention as NormalizeDriftSQLite)
832+ felifetime_db->UpdateData ((run + 1000000000 ) * 1000000000 );
833+
834+ ELifetimeInfo info;
835+ double tau_E, tau_W;
836+ felifetime_db->GetNamedChannelData (0 , " etau_sce_spatial_east" , tau_E);
837+ felifetime_db->GetNamedChannelData (0 , " etau_sce_spatial_west" , tau_W);
838+
839+ // TPC0 is East, TPC1 is West
840+ info.tau_tpc0 = tau_E;
841+ info.tau_tpc1 = tau_W;
842+
843+ if (fverbose) {
844+ std::cout << " [LightCaloProducer] : Electron lifetime from DB for run " << run << std::endl;
845+ std::cout << " [LightCaloProducer] : TPC0 (East): " << info.tau_tpc0 << " us" << std::endl;
846+ std::cout << " [LightCaloProducer] : TPC1 (West): " << info.tau_tpc1 << " us" << std::endl;
847+ }
848+
849+ // Cache the result
850+ felifetime_cache[run] = info;
851+ return info;
852+ }
853+
782854DEFINE_ART_MODULE (sbnd::LightCaloProducer)
0 commit comments