diff --git a/offline/packages/mbd/Makefile.am b/offline/packages/mbd/Makefile.am index be588c0adb..af9a9d2598 100644 --- a/offline/packages/mbd/Makefile.am +++ b/offline/packages/mbd/Makefile.am @@ -70,6 +70,7 @@ pkginclude_HEADERS = \ MbdPmtHit.h \ MbdPmtHitV1.h \ MbdPmtSimHitV1.h \ + MbdCalibReco.h \ MbdRawContainer.h \ MbdRawContainerV1.h \ MbdRawContainerV2.h \ @@ -178,6 +179,7 @@ libmbd_io_la_SOURCES = \ MbdSig.cc libmbd_la_SOURCES = \ + MbdCalibReco.cc \ MbdEvent.cc \ MbdCalib.cc \ MbdReco.cc \ diff --git a/offline/packages/mbd/MbdCalibReco.cc b/offline/packages/mbd/MbdCalibReco.cc new file mode 100644 index 0000000000..3b64e0f63e --- /dev/null +++ b/offline/packages/mbd/MbdCalibReco.cc @@ -0,0 +1,418 @@ +#include "MbdCalibReco.h" +#include "MbdCalib.h" +#include "MbdDefs.h" +#include "MbdPmtContainer.h" +#include "MbdPmtHit.h" +#include "MbdOut.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MbdCalibReco::MbdCalibReco(const std::string &name) + : SubsysReco(name) +{ +} + +int MbdCalibReco::Init(PHCompositeNode * /*topNode*/) +{ + _mbdcal = std::make_unique(); + _mbdcal->Verbosity( Verbosity() ); + return Fun4AllReturnCodes::EVENT_OK; +} + +int MbdCalibReco::InitRun(PHCompositeNode *topNode) +{ + _runheader = findNode::getClass(topNode, "RunHeader"); + if (!_runheader) + { + std::cout << PHWHERE << " RunHeader node not found, will use run number 0" << std::endl; + } + + _runnumber = _runheader ? _runheader->get_RunNumber() : 0; + + getNodes(topNode); + + // Build run directory path and create it + std::ostringstream oss; + oss << _caldir << "/" << _runnumber; + _rundir = oss.str(); + gSystem->Exec(("mkdir -p " + _rundir).c_str()); + + if (!_cdbtag.empty()) + { + // Download baseline calibrations from CDB + recoConsts::instance()->set_StringFlag("CDB_GLOBALTAG", _cdbtag); + CDBInterface* cdb = CDBInterface::instance(); + std::string url; + + url = cdb->getUrl("MBD_SAMPMAX"); + if (!url.empty()) { _mbdcal->Download_SampMax(url); } + + url = cdb->getUrl("MBD_PED"); + if (!url.empty()) { _mbdcal->Download_Ped(url); } + + url = cdb->getUrl("MBD_TIMECORR"); + if (!url.empty()) { _mbdcal->Download_TimeCorr(url); } + + url = cdb->getUrl("MBD_SLEWCORR"); + if (!url.empty()) { _mbdcal->Download_SlewCorr(url); } + + std::cout << Name() << ": loaded calibrations from CDB tag " << _cdbtag << std::endl; + } + else + { + // Load baseline calibrations from local files if they exist + std::string calfile = _rundir + "/mbd_sampmax.calib"; + if (gSystem->AccessPathName(calfile.c_str()) == 0) + { + _mbdcal->Download_SampMax(calfile); + } + calfile = _rundir + "/mbd_ped.calib"; + if (gSystem->AccessPathName(calfile.c_str()) == 0) + { + _mbdcal->Download_Ped(calfile); + } + + // Load slew correction for subpass >= 2 + if (_subpass >= 2) + { + calfile = _rundir + "/mbd_slewcorr.calib"; + if (gSystem->AccessPathName(calfile.c_str()) == 0) + { + _mbdcal->Download_SlewCorr(calfile); + std::cout << Name() << ": loaded " << calfile << std::endl; + } + else + { + std::cout << Name() << ": WARNING: " << calfile << " not found" << std::endl; + } + } + } + + // Load t0 offsets for subpass >= 1 (always from local files — outputs of previous subpass) + if (_subpass >= 1) + { + std::string prevpass = "pass" + std::to_string(_subpass - 1) + "_"; + + std::string calfile = _rundir + "/" + prevpass + "mbd_tq_t0.calib"; + if (gSystem->AccessPathName(calfile.c_str()) == 0) + { + _mbdcal->Download_TQT0(calfile); + std::cout << Name() << ": loaded " << calfile << std::endl; + } + else + { + std::cout << Name() << ": WARNING: " << calfile << " not found" << std::endl; + } + + calfile = _rundir + "/" + prevpass + "mbd_tt_t0.calib"; + if (gSystem->AccessPathName(calfile.c_str()) == 0) + { + _mbdcal->Download_TTT0(calfile); + std::cout << Name() << ": loaded " << calfile << std::endl; + } + else + { + std::cout << Name() << ": WARNING: " << calfile << " not found" << std::endl; + } + } + + // Build bitmask of scaled triggers whose names begin with "MBD N&S" + _mbias_trigger_mask = 0xfc00; + + // Open output ROOT file + TDirectory *origdir = gDirectory; + + std::string outfname = _rundir + "/calmbdpass2." + std::to_string(_subpass); + if (_subpass == 0) + { + outfname += "_time-" + std::to_string(_runnumber) + ".root"; + } + else if (_subpass == 1 || _subpass == 2) + { + outfname += "_slew-" + std::to_string(_runnumber) + ".root"; + } + else + { + outfname += "_q-" + std::to_string(_runnumber) + ".root"; + } + _outfile = std::make_unique(outfname.c_str(), "RECREATE"); + if (!_outfile || _outfile->IsZombie()) + { + std::cerr << PHWHERE << " ERROR: cannot open output file " << outfname << std::endl; + _outfile.reset(); + return Fun4AllReturnCodes::ABORTRUN; + } + std::cout << Name() << ": output file " << outfname << std::endl; + + BookHistograms(); + + origdir->cd(); + + return Fun4AllReturnCodes::EVENT_OK; +} + +int MbdCalibReco::getNodes(PHCompositeNode *topNode) +{ + _evtheader = findNode::getClass(topNode, "EventHeader"); + if (!_evtheader) + { + std::cout << PHWHERE << " EvtHeader not found, will use run number 0" << std::endl; + } + + _gl1packet = findNode::getClass(topNode,14001); + if (!_gl1packet) + { + _gl1packet = findNode::getClass(topNode, "GL1Packet"); + static int counter = 0; + if ( counter<4 ) + { + std::cout << PHWHERE << " GL1Packet not found" << std::endl; + } + } + + _mbdpmts = findNode::getClass(topNode, "MbdPmtContainer"); + if (!_mbdpmts) + { + static int counter = 0; + if ( counter<4 ) + { + std::cout << PHWHERE << " MbdPmtContainer not found" << std::endl; + } + } + + _mbdout = findNode::getClass(topNode, "MbdOut"); + if (!_mbdout) + { + static int counter = 0; + if ( counter<4 ) + { + std::cout << PHWHERE << " MbdOut not found" << std::endl; + } + } + + _mbdgeom = findNode::getClass(topNode, "MbdGeom"); + if (!_mbdgeom) + { + static int counter = 0; + if ( counter<4 ) + { + std::cout << PHWHERE << " MbdGeom not found" << std::endl; + } + } + + if ( !_mbdgeom || !_mbdout || !_mbdpmts || !_gl1packet || !_evtheader ) + { + return Fun4AllReturnCodes::ABORTRUN; + } + + return Fun4AllReturnCodes::EVENT_OK; +} + +void MbdCalibReco::BookHistograms() +{ + // Delete histograms if they have already have been booked. + if ( h2_tt ) + { + DeleteHistograms(); + return; + } + + for (int ipmt = 0; ipmt < MbdDefs::MBD_N_PMT; ipmt++) + { + std::string sn = std::to_string(ipmt); + + h_tt[ipmt] = new TH1F(("h_tt" + sn).c_str(), ("tt" + sn).c_str(), 7000, -30., 30.); + h_tt[ipmt]->SetXTitle("ns"); + + h_tq[ipmt] = new TH1F(("h_tq" + sn).c_str(), ("tq" + sn).c_str(), 7000, -150., 31. * 17.7623); + h_tq[ipmt]->SetXTitle("ns"); + + h_qp[ipmt] = new TH1F(("h_q" + sn).c_str(), ("q" + sn).c_str(), 3000, -100., 14900.); + h_qp[ipmt]->SetXTitle("ADC"); + + if (_subpass >= 1) + { + const int nbins[2] = {4000, 1100}; + const double xmin[2] = {-0.5, -5.}; + const double xmax[2] = {16000. - 0.5, 6.}; + h2_slew[ipmt] = new THnSparseF(("h2_slew" + sn).c_str(), ("slew curve, ch " + sn).c_str(), 2, nbins, xmin, xmax); + h2_slew[ipmt]->GetAxis(0)->SetTitle("ADC"); + h2_slew[ipmt]->GetAxis(1)->SetTitle("#Delta T (ns)"); + } + else + { + h2_slew[ipmt] = nullptr; + } + } + + h2_tt = new TH2F("h2_tt", "ch vs tt", 900, -150., 150., MbdDefs::MBD_N_PMT, -0.5, MbdDefs::MBD_N_PMT - 0.5); + h2_tt->SetXTitle("tt [ns]"); + h2_tt->SetYTitle("pmt ch"); + + h2_tq = new TH2F("h2_tq", "ch vs tq", 900, -150., 150., MbdDefs::MBD_N_PMT, -0.5, MbdDefs::MBD_N_PMT - 0.5); + h2_tq->SetXTitle("tq [ns]"); + h2_tq->SetYTitle("pmt ch"); +} + +void MbdCalibReco::DeleteHistograms() +{ + for (int ipmt = 0; ipmt < MbdDefs::MBD_N_PMT; ipmt++) + { + if ( h_tt[ipmt] ) + { + delete h_tt[ipmt]; + } + if ( h_tq[ipmt] ) + { + delete h_tq[ipmt]; + } + if ( h_qp[ipmt] ) + { + delete h_qp[ipmt]; + } + if ( h2_slew[ipmt] ) + { + delete h2_slew[ipmt]; + } + } + + delete h2_tt; + delete h2_tq; +} + +int MbdCalibReco::process_event(PHCompositeNode * /*topNode*/) +{ + // Require a scaled "MBD N&S" trigger + if (_mbias_trigger_mask != 0) + { + uint64_t strig = _gl1packet->getScaledVector(); // scaled trigger only + if ( (strig&_mbias_trigger_mask)==0 ) + { + return Fun4AllReturnCodes::ABORTEVENT; + } + } + + std::array armtime{}; + armtime.fill(0); + std::array nhit{}; + nhit.fill(0); + + Float_t zvtx = _mbdout->get_zvtx(); + // Vertex cut for subpass >= 1 + if ( _subpass >= 1 ) + { + if (std::abs(zvtx) > 60.) + { + return Fun4AllReturnCodes::EVENT_OK; + } + } + + for (int iarm=0; iarm<2; iarm++) + { + armtime[iarm] = _mbdout->get_time(iarm); + nhit[iarm] = _mbdout->get_npmt(iarm); + } + + for (int ipmt=0; ipmt < _mbdpmts->get_npmt(); ipmt++) + { + MbdPmtHit *pmt = _mbdpmts->get_pmt(ipmt); + if ( !pmt ) + { + continue; + } + + Short_t pmtno = pmt->get_pmt(); + if ( pmtno<0 || pmtno>=MbdDefs::MBD_N_PMT ) + { + static int counter = 0; + if ( counter<10 ) + { + std::cerr << PHWHERE << " invalide pmt no " << pmtno << std::endl; + counter++; + } + continue; + } + + Float_t q = pmt->get_q(); + Float_t tt = pmt->get_tt(); + Float_t tq = pmt->get_tq(); + + h_tt[pmtno]->Fill( tt ); + h2_tt->Fill( tt, pmtno ); + h_tq[pmtno]->Fill( tq ); + h2_tq->Fill( tq, pmtno ); + + // Fill charge histogram for in-time hits + if ( std::abs(tt)<26.0 && q > 0.) + { + h_qp[pmtno]->Fill( q ); + } + + int arm = _mbdgeom->get_arm( pmtno ); + + // Fill slew histogram for subpass >= 1 + if (_subpass >= 1 && h2_slew[pmtno]) + { + if (nhit[arm] >= 2. && q > 0.) + { + float dt = tt - armtime[arm]; + const double coords[2] = {q, dt}; + h2_slew[pmtno]->Fill(coords); + } + } + } + + return Fun4AllReturnCodes::EVENT_OK; +} + +int MbdCalibReco::EndRun(const int /*runnumber*/) +{ + if (!_outfile) + { + return Fun4AllReturnCodes::EVENT_OK; + } + + // Write histograms to output file + _outfile->cd(); + h2_tt->Write(); + h2_tq->Write(); + for (int ipmt = 0; ipmt < MbdDefs::MBD_N_PMT; ipmt++) + { + h_tt[ipmt]->Write(); + h_tq[ipmt]->Write(); + h_qp[ipmt]->Write(); + if (h2_slew[ipmt]) + { + h2_slew[ipmt]->Write(); + } + } + + _outfile->Close(); + + return Fun4AllReturnCodes::EVENT_OK; +} + diff --git a/offline/packages/mbd/MbdCalibReco.h b/offline/packages/mbd/MbdCalibReco.h new file mode 100644 index 0000000000..eed96688cd --- /dev/null +++ b/offline/packages/mbd/MbdCalibReco.h @@ -0,0 +1,73 @@ +#ifndef MBD_MBDCALIBRECO_H +#define MBD_MBDCALIBRECO_H + +#include "MbdDefs.h" + +#include + +#include +#include +#include +#include +#include + +class PHCompositeNode; +class MbdCalib; +class MbdPmtContainer; +class MbdOut; +class MbdGeom; +class Gl1Packet; +class EventHeader; +class RunHeader; +class TH1; +class TH2; +class TFile; +class TGraphErrors; + +class MbdCalibReco : public SubsysReco +{ + public: + MbdCalibReco(const std::string& name = "MbdCalibReco"); + ~MbdCalibReco() override = default; + + int Init(PHCompositeNode* topNode) override; + int InitRun(PHCompositeNode* topNode) override; + int process_event(PHCompositeNode* topNode) override; + int EndRun(const int runnumber) override; + + void SetSubPass(const int s) { _subpass = s; } + void SetCalDir(const std::string& d) { _caldir = d; } + void SetCDBTag(const std::string& t) { _cdbtag = t; } + + private: + int getNodes(PHCompositeNode* topNode); + void BookHistograms(); + void DeleteHistograms(); + + uint64_t _mbias_trigger_mask{0}; + + int _subpass{0}; + int _runnumber{0}; + std::string _caldir{"results"}; + std::string _rundir; // _caldir// + std::string _cdbtag{}; // non-empty → download from CDB instead of local files + + std::unique_ptr _mbdcal; + MbdPmtContainer* _mbdpmts{nullptr}; + MbdOut* _mbdout{nullptr}; + MbdGeom* _mbdgeom{nullptr}; + EventHeader* _evtheader{nullptr}; + RunHeader* _runheader{nullptr}; + Gl1Packet* _gl1packet{nullptr}; + + std::array h_tt{}; + std::array h_tq{}; + std::array h_qp{}; + std::array h2_slew{}; + TH2* h2_tt{nullptr}; + TH2* h2_tq{nullptr}; + + std::unique_ptr _outfile{nullptr}; +}; + +#endif // MBD_MBDCALIBRECO_H diff --git a/offline/packages/mbd/MbdEvent.cc b/offline/packages/mbd/MbdEvent.cc index b97d750f29..f768dfa16d 100644 --- a/offline/packages/mbd/MbdEvent.cc +++ b/offline/packages/mbd/MbdEvent.cc @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -165,18 +166,32 @@ int MbdEvent::InitRun() if ( _calpass>1 ) { std::string calfname = "results/"; calfname += std::to_string(_runnum); calfname += "/mbd_sampmax.calib"; - std::cout << "Loading local sampmax, " << calfname << std::endl; - _mbdcal->Download_SampMax( calfname ); + if ( std::filesystem::exists(calfname) ) + { + std::cout << "Loading local sampmax, " << calfname << std::endl; + _mbdcal->Download_SampMax( calfname ); + } + else + { + std::cout << PHWHERE << "local sampmax not found, skipping: " << calfname << std::endl; + } calfname = "results/"; calfname += std::to_string(_runnum); calfname += "/mbd_ped.calib"; - std::cout << "Loading local ped, " << calfname << std::endl; - _mbdcal->Download_Ped( calfname ); + if ( std::filesystem::exists(calfname) ) + { + std::cout << "Loading local ped, " << calfname << std::endl; + _mbdcal->Download_Ped( calfname ); + } + else + { + std::cout << PHWHERE << "local ped not found, skipping: " << calfname << std::endl; + } } // check if sampmax and ped calibs exist int scheck = _mbdcal->get_sampmax(0); - if ( (scheck<0 || _is_online) && _calpass!=1 ) + if ( (scheck<0 || _is_online) && _calpass==0 ) { _no_sampmax = 1000; // num events for on the fly calculation _calib_done = 0; @@ -281,11 +296,46 @@ int MbdEvent::InitRun() if ( _calpass == 2 ) { - // zero out the tt_t0, tq_t0, and gains to produce uncalibrated time and charge std::cout << "MBD Cal Pass 2" << std::endl; - _mbdcal->Reset_TTT0(); - _mbdcal->Reset_TQT0(); - _mbdcal->Reset_Gains(); + + // zero out the tt_t0, tq_t0, and gains to produce uncalibrated time and charge + // or load pass2 calibs from local file for calpass2+, if local files exist + std::string calfname = "results/"; calfname += std::to_string(_runnum); calfname += "/mbd_tt_t0.calib"; + if ( std::filesystem::exists(calfname) ) + { + std::cout << "Loading local mbd_tt_t0, " << calfname << std::endl; + _mbdcal->Download_TTT0( calfname ); + } + else + { + _mbdcal->Reset_TTT0(); + std::cout << PHWHERE << "local mbd_tt_t0 not found, reset to 0: " << calfname << std::endl; + } + + calfname = "results/"; calfname += std::to_string(_runnum); calfname += "/mbd_tq_t0.calib"; + if ( std::filesystem::exists(calfname) ) + { + std::cout << "Loading local mbd_tq_t0, " << calfname << std::endl; + _mbdcal->Download_TQT0( calfname ); + } + else + { + _mbdcal->Reset_TQT0(); + std::cout << PHWHERE << "local mbd_tq_t0 not found, reset to 0: " << calfname << std::endl; + } + + calfname = "results/"; calfname += std::to_string(_runnum); calfname += "/mbd_qfit.calib"; + if ( std::filesystem::exists(calfname) ) + { + std::cout << "Loading local mbd_qfit, " << calfname << std::endl; + _mbdcal->Download_Gains( calfname ); + } + else + { + _mbdcal->Reset_Gains(); + std::cout << PHWHERE << "local mbd_gains not found, reset to 1: " << calfname << std::endl; + } + TDirectory *orig_dir = gDirectory; @@ -1381,7 +1431,7 @@ int MbdEvent::FillSampMaxCalib() // _no_sampmax keeps track of how many events to use for on-the-fly calibration _no_sampmax--; - if ( _no_sampmax==0 && _calpass != 1 ) + if ( _no_sampmax==0 && _calpass==0 ) { CalcSampMaxCalib(); _calib_done = 1;