/** @file CbmDaq.cxx ** @author Volker Friese ** @date 20 July 2012 **/ #include "CbmDaq.h" #include #include #include #include #include "TClonesArray.h" #include "FairEventHeader.h" #include "FairLogger.h" #include "FairRunAna.h" #include "CbmDigi.h" #include "CbmDigitize.h" #include "CbmMatch.h" #include "CbmTimeSlice.h" #include "CbmTofDigi.h" #include "CbmTofDigiExp.h" using std::fixed; using std::map; using std::pair; using std::right; using std::setprecision; using std::setw; using std::string; using std::stringstream; // ===== Constructor ===================================================== CbmDaq::CbmDaq(Double_t interval) : FairTask("Daq"), fEventMode(kFALSE), fTimeSliceLength(interval), fBufferTime(2000.), fStoreEmptySlices(kFALSE), fTimeEventPrevious(-1.), fNofEvents(0), fNofDigis(0), fNofDigisIgnored(0), fNofTimeSlices(0), fNofTimeSlicesEmpty(0), fTimeDigiFirst(-1.), fTimeDigiLast(-1.), fTimeSliceFirst(-1.), fTimeSliceLast(-1.), fTimer(), fDigis(), fDigitizers(), fTimeSlice(nullptr), fBuffer(), fEventList(), fEventsCurrent(nullptr), fEventRange() { } // =========================================================================== // ===== Destructor ====================================================== CbmDaq::~CbmDaq() { } // =========================================================================== // ===== Close the current time slice and fill it to the tree ============ void CbmDaq::CloseTimeSlice() { // --- Time slice status if ( fTimeSlice->IsEmpty() ) { LOG(debug) << "----- " << fName << ": Closing " << fTimeSlice->ToString(); fNofTimeSlicesEmpty++; } else LOG(info) << "----- " << fName << ": Closing " << fTimeSlice->ToString(); // --- Fill current time slice into tree (if required) if ( fStoreEmptySlices || (! fTimeSlice->IsEmpty()) ) { Int_t nMCEvents = CopyEventList(); fEventsCurrent->Sort(); LOG(debug) << GetName() << ": " << nMCEvents << " MC " << (nMCEvents == 1 ? "event" : "events" ) << " for this time slice"; FairRootManager::Instance()->Fill(); } if ( fEventMode || fTimeSliceLength < 0. ) fTimeSliceLast = fTimeSlice->GetStartTime(); else fTimeSliceLast = fTimeSlice->GetEndTime(); fNofTimeSlices++; if ( gLogger->IsLogNeeded(fair::Severity::debug) ) PrintCurrentEventRange(); // --- Reset time slice with new time interval Double_t startTime = fTimeSlice->GetStartTime() + fTimeSliceLength; fTimeSlice->Reset(startTime, fTimeSliceLength); fEventRange.clear(); fEventsCurrent->Clear(""); // --- Clear data output arrays for ( Int_t detector = kMvd; detector < kNofSystems; detector++) { if ( fDigis[detector] ) fDigis[detector]->Delete(); } // --- Call ResetArrays from digitizers for (auto it = fDigitizers.begin(); it != fDigitizers.end(); it++) it->second->ResetArrays(); } // =========================================================================== // ===== Copy event list to output branch ================================ Int_t CbmDaq::CopyEventList() { Int_t nMCEvents = 0; for (auto fileIt = fEventRange.begin(); fileIt != fEventRange.end(); fileIt++) { Int_t file = fileIt->first; Int_t firstEvent = fileIt->second.first; Int_t lastEvent = fileIt->second.second; for (Int_t event = firstEvent; event <= lastEvent; event++) { Double_t time = fEventList.GetEventTime(event, file); fEventsCurrent->Insert(event, file, time); nMCEvents++; } //# events } //# files return nMCEvents; } // =========================================================================== // ===== Task execution ================================================== void CbmDaq::Exec(Option_t*) { // Start timer and digi counter fTimer.Start(); Int_t nDigis = 0; // Event info Int_t file = FairRunAna::Instance()->GetEventHeader()->GetInputFileId(); Int_t event = FairRootManager::Instance()->GetEntryNr(); Double_t eventTime = FairRunAna::Instance()->GetEventHeader()->GetEventTime(); fEventList.Insert(event, file, eventTime); // Status LOG(debug) << fName << ": Event " << event << " at " << eventTime << " ns, previous event time " << fTimeEventPrevious << " ns"; LOG(debug) << fBuffer.ToString(); // Time-based mode if ( ! fEventMode ) { // Time-slices of equal duration if ( fTimeSliceLength > 0. ) { // The time slice can only be filled up to the previous event time // because digitizers may generate noise between the previous event // time and the current event time. Double_t fillTime = fTimeEventPrevious - fBufferTime; // Copy data from the buffer to the time slice up to the fill time. // If this includes data after the time slice end, close the time // slice and open a new one. while ( kTRUE ) { // Fill time is within time slice: fill data into time slice and exit // the loop. if ( fillTime < fTimeSlice->GetEndTime() ) { nDigis += FillTimeSlice(fillTime); break; } //? fill time within time slice // Fill time is beyond time slice: fill data up to the end of the // time slice, close the time slice and start a new one. else { nDigis += FillTimeSlice(fTimeSlice->GetEndTime()); CloseTimeSlice(); } } //# time slices } //? time-slices of equal duration else { // all data into one time-slice // Fill data up to the previous event time minus the buffering interval Double_t fillTime = fTimeEventPrevious - fBufferTime; nDigis = FillTimeSlice(fillTime); } //? all data into one time-slice } //? time-based mode else { // event-by-event mode // Fill all data from the buffer into the time slice and close the // time slice. nDigis = FillTimeSlice(-1., kFALSE); fTimeSlice->SetStartTime(eventTime); if ( fNofEvents == 0 ) fTimeSliceFirst = eventTime; CloseTimeSlice(); } //? event-by-event mode // Buffer Status LOG(debug) << GetName() << ": " << fBuffer.ToString(); // --- Save event time for next execution fTimeEventPrevious = eventTime; // --- Event log fTimer.Stop(); LOG(info) << "+ " << setw(15) << GetName() << ": Event " << setw(6) << right << event << " at " << fixed << setprecision(3) << eventTime << " ns, " << nDigis << " digis transported. Exec time " << setprecision(6) << fTimer.RealTime() << " s."; // --- Increase exec counter fNofEvents++; } // =========================================================================== // ===== Fill current time slice with data from buffers ================== Int_t CbmDaq::FillTimeSlice(Double_t fillTime, Bool_t limit) { // --- Call user-defined method FillCustomData for ( auto entry : fDigitizers ) { if ( entry.second ) entry.second->FillCustomData(fillTime, limit); } //# Digitisers Int_t nDigis = 0; if ( limit ) LOG(debug) << fName << ": Fill data up to t = " << fillTime << " into current time slice."; else LOG(debug) << fName << ": Fill all data into current time slice."; CbmDaqBuffer::Data data; // --- Loop over all detector systems for (Int_t iDet = kRef; iDet < kNofSystems; iDet++) { // --- Loop over digis from DaqBuffer and fill them into current time slice data = ( limit ? fBuffer.GetNextData(iDet, fillTime) : fBuffer.GetNextData(iDet) ); while (data.first) { // --- Ignore digis before the time slice start // This can happen in two cases: if the digi time is negative (the first // time-slice starts at t = 0), or if the buffer time is set too small. if ( (! fTimeSlice->IsFlexible()) && data.first->GetTime() < fTimeSlice->GetStartTime() ) { LOG(warn) << fName << ": ignore digi from system " << CbmModuleList::GetModuleNameCaps(data.first->GetSystemId()) << " at time " << data.first->GetTime() << " for " << fTimeSlice->ToString(); fNofDigisIgnored++; data.first.reset(); // delete digi data.second.reset(); // delete match continue; } // --- Update event list, digi counter and times of first and last digi RegisterEvent(data); nDigis++; if ( !fNofDigis && !nDigis ) { fTimeDigiFirst = data.first->GetTime(); fTimeDigiLast = data.first->GetTime(); } else { fTimeDigiFirst = TMath::Min(fTimeDigiFirst, data.first->GetTime()); fTimeDigiLast = TMath::Max(fTimeDigiLast, data.first->GetTime()); } // --- Write data to output. WriteData(data); // --- Get next data from buffer data = ( limit ? fBuffer.GetNextData(iDet, fillTime) : fBuffer.GetNextData(iDet) ); } //? Valid data from buffer } //# Detectors LOG(debug) << GetName() << ": Filled " << nDigis << " digis into " << fTimeSlice->ToString(); fNofDigis += nDigis; return nDigis; } // =========================================================================== // ===== End-of-run action =============================================== void CbmDaq::Finish() { if ( ! fEventMode ) { // time-based mode std::cout << std::endl; LOG(info) << fName << ": Finish run"; LOG(info) << fBuffer.ToString(); Int_t nDigis = 0; if ( fTimeSliceLength > 0. ) { // regular time-slices while ( fBuffer.GetSize() ) { // time slice loop until buffer is empty nDigis += FillTimeSlice(fTimeSlice->GetEndTime()); CloseTimeSlice(); } //# time slices } //? regular time slices else { // all data into one time-slice nDigis += FillTimeSlice(-1., kFALSE); CloseTimeSlice(); } //? all data into one time-slice LOG(info) << fName << ": " << nDigis << " digis transported."; LOG(info) << fBuffer.ToString(); LOG(info) << fName << ": run finished."; } //? time-based mode else { // event-by-event mode if ( fBuffer.GetSize() ) LOG(fatal) << fName << ": non-empty buffer at finish in event-by-event mode!"; } //? event-by-event mode std::cout << std::endl; LOG(info) << "====================================="; LOG(info) << GetName() << ": Run summary"; LOG(info) << "Events: " << setw(10) << right << fNofEvents; LOG(info) << "Digis: " << setw(10) << right << fNofDigis << " from " << setw(10) << right << fixed << setprecision(1) << fTimeDigiFirst << " ns to " << setw(10) << right << fixed << setprecision(1) << fTimeDigiLast << " ns"; LOG(info) << "Digis ignored: " << setw(10) << right << fNofDigisIgnored; LOG(info) << "Time slices: " << setw(10) << right << fNofTimeSlices << " from " << setw(10) << right << fixed << setprecision(1) << fTimeSliceFirst << " ns to " << setw(10) << right << fixed << setprecision(1) << fTimeSliceLast << " ns"; LOG(info) << "Empty slices: " << setw(10) << right << fNofTimeSlicesEmpty; LOG(info) << "====================================="; std::cout << std::endl; LOG(info) << fEventList.ToString(); // fEventList.Print(); } // =========================================================================== // ===== Task initialisation ============================================= InitStatus CbmDaq::Init() { std::cout << std::endl; LOG(info) << "=========================================================="; std::stringstream ss; ss << GetName() << ": Initialisation"; ss << std::endl; if ( fEventMode ) ss << fName << ": running in event mode."; else { ss << fName << ": time-slice length is "; if ( fTimeSliceLength < 0. ) ss << "flexible."; else ss << fTimeSliceLength << " ns."; LOG(info) << ss.str(); } //? time-based mode // Set initial times fTimeEventPrevious = -1.; if ( fEventMode ) fTimeSliceLength = -1.; if ( fTimeSliceLength < 0. ) fTimeSlice = new CbmTimeSlice(); // flexible TS else fTimeSlice = new CbmTimeSlice(0., fTimeSliceLength); // fixed-length TS fTimeSliceFirst = fTimeSlice->GetStartTime(); // Register output branch TimeSlice FairRootManager::Instance()->Register("TimeSlice.", "DAQ", fTimeSlice, kTRUE); // Register output branch MCEventList fEventsCurrent = new CbmMCEventList(); FairRootManager::Instance()->Register("MCEventList.", "DAQ", fEventsCurrent, kTRUE); LOG(info) << GetName() << ": Initialisation successful"; LOG(info) << "=========================================================="; std::cout << std::endl; return kSUCCESS; } // =========================================================================== // ===== Print current event range ======================================= void CbmDaq::PrintCurrentEventRange() const { std::stringstream ss; ss << GetName() << ": Current MC event range: "; if ( fEventRange.empty() ) { ss << "empty"; LOG(info) << ss.str(); return; } auto it = fEventRange.begin(); while ( it != fEventRange.end() ) { Int_t file = it->first; Int_t firstEvent = it->second.first; Int_t lastEvent = it->second.second; ss << "\n Input file " << file << ", first event " << firstEvent << ", last event " << lastEvent; it++; } //# inputs LOG(info) << ss.str(); } // =========================================================================== // ===== Receive data ==================================================== void CbmDaq::ReceiveData(up_CbmDigi digi, up_CbmMatch match) { assert(digi); // should be a valid digi fBuffer.InsertData(std::move(digi), std::move(match)); } // =========================================================================== // ===== Register MC event from CbmDigi ================================== void CbmDaq::RegisterEvent(CbmDigi* digi) { // TODO: Legacy to maintain compatibility with TOF development branch CbmMatch* match = nullptr; // Check whether it is a CbmTofDigi CbmTofDigi* tofDigi = dynamic_cast(digi); if ( tofDigi ) match = tofDigi->GetMatch(); // Else check whether it is a CbmTofDigiExp else { CbmTofDigiExp* tofDigiExp = dynamic_cast(digi); if ( tofDigiExp ) match = tofDigiExp->GetMatch(); } // Use match object to update event list if ( match ) RegisterEvent(*match); } // =========================================================================== // ===== Register MC event =============================================== void CbmDaq::RegisterEvent(const CbmMatch& match) { for (Int_t iLink = 0; iLink < match.GetNofLinks(); iLink++) { Int_t file = match.GetLink(iLink).GetFile(); Int_t event = match.GetLink(iLink).GetEntry(); if (fEventRange.find(file) == fEventRange.end()) { fEventRange[file] = pair(event, event); } //? First entry for this input else { Int_t firstEvent = fEventRange[file].first; Int_t lastEvent = fEventRange[file].second; if ( event < firstEvent ) { firstEvent = event; fEventRange[file] = pair(firstEvent, lastEvent); } if ( event > lastEvent ) { lastEvent = event; fEventRange[file] = pair(firstEvent, lastEvent); } } //? Compare with existing input } //# links } // =========================================================================== // ===== Register MC event =============================================== void CbmDaq::RegisterEvent(const CbmDaqBuffer::Data& data) { // Legacy: if no match pointer, register from digi if ( data.second == nullptr ) { RegisterEvent(data.first.get()); } //? No match pointer else RegisterEvent(*(data.second.get())); // TODO: When the legacy implementation with digi* argument is removed, // the code from RegisterEvent(CbmMatch&) can be moved to here. } // =========================================================================== // ===== Set the digitizer for a given system ============================ void CbmDaq::SetDigitizer(Int_t system, CbmDigitize* digitizer) { assert(digitizer); fDigitizers[system] = digitizer; digitizer->SetDaq(this); } // =========================================================================== // ===== Write data to output ============================================ void CbmDaq::WriteData(CbmDaqBuffer::Data& data) { // --- Get corresponding system assert(data.first); Int_t system = data.first->GetSystemId(); if ( fDigitizers.find(system) == fDigitizers.end() ) LOG(fatal) << fName << ": No corresponding digitizer for system ID " << system; // --- Call write method of corresponding digitiser CbmDigitize* digitizer = fDigitizers.at(system); assert(digitizer); fTimeSlice->AddData(system, data.first->GetTime()); digitizer->WriteDigi(data.first.get(), data.second.get()); // --- The managed objects have to be deleted, since this is not (yet) // --- done by CbmDigitizer::WriteData(). data.first.reset(); data.second.reset(); } // =========================================================================== ClassImp(CbmDaq)