1 #include "artdaq/ArtModules/RootDAQOutput-s81/RootDAQOutFile.h"
4 #include "art/Framework/Core/OutputFileGranularity.h"
5 #include "art/Framework/IO/ClosingCriteria.h"
6 #include "art/Framework/IO/FileStatsCollector.h"
7 #include "art/Framework/Principal/EventPrincipal.h"
8 #include "art/Framework/Principal/ResultsPrincipal.h"
9 #include "art/Framework/Principal/RunPrincipal.h"
10 #include "art/Framework/Principal/SubRunPrincipal.h"
11 #include "art/Framework/Services/System/DatabaseConnection.h"
12 #include "art/Persistency/Provenance/ProcessHistoryRegistry.h"
13 #include "art/Version/GetReleaseVersion.h"
14 #include "art_root_io/DropMetaData.h"
15 #include "art_root_io/GetFileFormatEra.h"
16 #include "art_root_io/GetFileFormatVersion.h"
17 #include "art_root_io/RootDB/SQLErrMsg.h"
18 #include "art_root_io/RootDB/TKeyVFSOpenPolicy.h"
19 #include "art_root_io/RootFileBlock.h"
20 #include "art_root_io/checkDictionaries.h"
21 #include "art_root_io/detail/KeptProvenance.h"
22 #include "art_root_io/detail/getObjectRequireDict.h"
23 #include "artdaq/DAQdata/Globals.hh"
24 #include "boost/date_time/posix_time/posix_time.hpp"
25 #include "canvas/Persistency/Provenance/BranchChildren.h"
26 #include "canvas/Persistency/Provenance/BranchType.h"
27 #include "canvas/Persistency/Provenance/EventAuxiliary.h"
28 #include "canvas/Persistency/Provenance/EventID.h"
29 #include "canvas/Persistency/Provenance/FileFormatVersion.h"
30 #include "canvas/Persistency/Provenance/History.h"
31 #include "canvas/Persistency/Provenance/ParameterSetBlob.h"
32 #include "canvas/Persistency/Provenance/Parentage.h"
33 #include "canvas/Persistency/Provenance/ParentageRegistry.h"
34 #include "canvas/Persistency/Provenance/ProcessHistoryID.h"
35 #include "canvas/Persistency/Provenance/ProductStatus.h"
36 #include "canvas/Persistency/Provenance/ResultsAuxiliary.h"
37 #include "canvas/Persistency/Provenance/RunAuxiliary.h"
38 #include "canvas/Persistency/Provenance/SubRunAuxiliary.h"
39 #include "canvas/Persistency/Provenance/rootNames.h"
40 #include "canvas/Utilities/Exception.h"
41 #include "canvas_root_io/Utilities/DictionaryChecker.h"
42 #include "cetlib/canonical_string.h"
43 #include "cetlib/container_algorithms.h"
44 #include "cetlib/exempt_ptr.h"
45 #include "cetlib/sqlite/Ntuple.h"
46 #include "cetlib/sqlite/Transaction.h"
47 #include "cetlib/sqlite/create_table.h"
48 #include "cetlib/sqlite/exec.h"
49 #include "cetlib/sqlite/insert.h"
50 #include "fhiclcpp/ParameterSet.h"
51 #include "fhiclcpp/ParameterSetID.h"
52 #include "fhiclcpp/ParameterSetRegistry.h"
54 #define TRACE_NAME (app_name + "_RootDAQOutFile").c_str()
55 #include "hep_concurrency/RecursiveMutex.h"
58 #include "TBranchElement.h"
64 #include <sys/sysinfo.h>
71 using namespace hep::concurrency;
73 using art::BranchType;
75 using art::rootNames::metaBranchRootName;
79 void create_table(sqlite3*
const db,
81 vector<string>
const& columns,
82 string const& suffix = {})
86 throw art::Exception(art::errors::LogicError)
87 <<
"Number of sqlite columns specified for table: " << name <<
'\n'
90 string ddl =
"DROP TABLE IF EXISTS " + name +
93 name +
"(" + columns.front();
94 for_each(columns.begin() + 1, columns.end(), [&ddl](
auto const& col) {
100 sqlite::exec(db, ddl);
103 void insert_eventRanges_row(sqlite3_stmt* stmt,
104 art::SubRunNumber_t
const sr,
105 art::EventNumber_t
const b,
106 art::EventNumber_t
const e)
108 sqlite3_bind_int64(stmt, 1, sr);
109 sqlite3_bind_int64(stmt, 2, b);
110 sqlite3_bind_int64(stmt, 3, e);
115 void insert_rangeSets_eventSets_row(sqlite3_stmt* stmt,
119 sqlite3_bind_int64(stmt, 1, rsid);
120 sqlite3_bind_int64(stmt, 2, esid);
126 getNewRangeSetID(sqlite3* db,
127 art::BranchType
const bt,
128 art::RunNumber_t
const r)
130 sqlite::insert_into(db, art::BranchTypeToString(bt) +
"RangeSets")
132 return sqlite3_last_insert_rowid(db);
136 getExistingRangeSetIDs(sqlite3* db, art::RangeSet
const& rs)
138 vector<unsigned> rangeSetIDs;
139 cet::transform_all(rs, back_inserter(rangeSetIDs), [db](
auto const& range) {
140 sqlite::query_result<unsigned> r;
141 r << sqlite::select(
"ROWID")
142 .from(db,
"EventRanges")
143 .where(
"SubRun=" + to_string(range.subRun()) +
146 to_string(range.begin()) +
149 to_string(range.end()));
150 return unique_value(r);
155 void insertIntoEventRanges(sqlite3* db, art::RangeSet
const& rs)
157 sqlite::Transaction txn{db};
158 sqlite3_stmt* stmt{
nullptr};
160 "INSERT INTO EventRanges(SubRun, begin, end) "
162 sqlite3_prepare_v2(db, ddl.c_str(), -1, &stmt,
nullptr);
163 for (
auto const& range : rs)
165 insert_eventRanges_row(stmt, range.subRun(), range.begin(), range.end());
167 sqlite3_finalize(stmt);
171 void insertIntoJoinTable(sqlite3* db,
172 art::BranchType
const bt,
174 vector<unsigned>
const& eventRangesIDs)
176 sqlite::Transaction txn{db};
177 sqlite3_stmt* stmt{
nullptr};
179 "INSERT INTO " + art::BranchTypeToString(bt) +
180 "RangeSets_EventRanges(RangeSetsID, EventRangesID) Values(?,?);"};
181 sqlite3_prepare_v2(db, ddl.c_str(), -1, &stmt,
nullptr);
182 cet::for_all(eventRangesIDs, [stmt, rsID](
auto const eventRangeID) {
183 insert_rangeSets_eventSets_row(stmt, rsID, eventRangeID);
185 sqlite3_finalize(stmt);
189 void maybeInvalidateRangeSet(BranchType
const bt,
190 art::RangeSet
const& principalRS,
191 art::RangeSet& productRS)
193 assert(principalRS.is_sorted());
194 assert(productRS.is_sorted());
195 if (!productRS.is_valid())
199 if (bt == art::InRun && productRS.is_full_run())
203 if (bt == art::InSubRun && productRS.is_full_subRun())
207 if (productRS.ranges().empty())
211 auto const r = productRS.run();
212 auto const& productFront = productRS.ranges().front();
213 if (!principalRS.contains(r, productFront.subRun(), productFront.begin()))
215 productRS = art::RangeSet::invalid();
258 template<BranchType BT>
260 getRangeSet(art::OutputHandle
const& oh,
261 art::RangeSet
const& principalRS,
262 bool const producedInThisProcess)
264 if constexpr (!art::detail::range_sets_supported(BT))
268 return art::RangeSet::invalid();
271 auto rs = oh.isValid() ? oh.rangeOfValidity() : art::RangeSet::invalid();
282 if (!producedInThisProcess)
284 maybeInvalidateRangeSet(BT, principalRS, rs);
289 template<BranchType BT>
290 void setProductRangeSetID(art::RangeSet
const& rs,
292 art::EDProduct* product,
293 map<unsigned, unsigned>& checksumToIndexLookup)
295 if constexpr (!art::detail::range_sets_supported(BT))
307 auto it = checksumToIndexLookup.find(rs.checksum());
308 if (it != checksumToIndexLookup.cend())
310 product->setRangeSetID(it->second);
314 unsigned const rsID = getNewRangeSetID(db, BT, rs.run());
315 product->setRangeSetID(rsID);
316 checksumToIndexLookup.emplace(rs.checksum(), rsID);
317 insertIntoEventRanges(db, rs);
318 auto const& eventRangesIDs = getExistingRangeSetIDs(db, rs);
319 insertIntoJoinTable(db, BT, rsID, eventRangesIDs);
323 bool maxCriterionSpecified(art::ClosingCriteria
const& cc)
325 auto fp = mem_fn(&art::ClosingCriteria::fileProperties);
326 return (fp(cc).nEvents() !=
327 art::ClosingCriteria::Defaults::unsigned_max()) ||
328 (fp(cc).nSubRuns() !=
329 art::ClosingCriteria::Defaults::unsigned_max()) ||
330 (fp(cc).nRuns() != art::ClosingCriteria::Defaults::unsigned_max()) ||
331 (fp(cc).size() != art::ClosingCriteria::Defaults::size_max()) ||
332 (fp(cc).age().count() !=
333 art::ClosingCriteria::Defaults::seconds_max());
340 RootDAQOutFile::OutputItem::~OutputItem() =
default;
342 RootDAQOutFile::OutputItem::OutputItem(BranchDescription bd)
343 : branchDescription_{std::move(bd)}, product_{
nullptr}
347 RootDAQOutFile::OutputItem::branchName()
const
349 return branchDescription_.branchName();
352 bool RootDAQOutFile::OutputItem::operator<(OutputItem
const& rh)
const
354 return branchDescription_ < rh.branchDescription_;
358 bool RootDAQOutFile::shouldFastClone(
bool const fastCloningSet,
359 bool const fastCloning,
360 bool const wantAllEvents,
361 ClosingCriteria
const& cc)
363 bool result = fastCloning;
364 mf::LogInfo(
"FastCloning")
365 <<
"Initial fast cloning configuration "
366 << (fastCloningSet ?
"(user-set): " :
"(from default): ") << boolalpha
368 if (fastCloning && !wantAllEvents)
371 mf::LogWarning(
"FastCloning")
372 <<
"Fast cloning deactivated due to presence of\n"
373 <<
"event selection configuration.";
375 if (fastCloning && maxCriterionSpecified(cc) &&
376 cc.granularity() < Granularity::InputFile)
379 mf::LogWarning(
"FastCloning")
380 <<
"Fast cloning deactivated due to request to allow\n"
381 <<
"output file switching at an Event, SubRun, or Run boundary.";
386 RootDAQOutFile::RootDAQOutFile(OutputModule* om,
387 string const& fileName,
388 ClosingCriteria
const& fileSwitchCriteria,
389 int const compressionLevel,
390 unsigned freePercent,
392 int64_t
const saveMemoryObjectThreshold,
393 int64_t
const treeMaxVirtualSize,
394 int const splitLevel,
395 int const basketSize,
396 DropMetaData dropMetaData,
397 bool const dropMetaDataForDroppedData,
398 bool const fastCloningRequested)
399 : mutex_{
"RootDAQOutFile::mutex_"}
400 , compressionLevel_{compressionLevel}
401 , freePercent_{freePercent}
403 , saveMemoryObjectThreshold_{saveMemoryObjectThreshold}
404 , treeMaxVirtualSize_{treeMaxVirtualSize}
405 , splitLevel_{splitLevel}
406 , basketSize_{basketSize}
407 , dropMetaData_{dropMetaData}
408 , descriptionsToPersist_{{}}
409 , selectedOutputItemList_{{}}
413 fileSwitchCriteria_ = fileSwitchCriteria;
414 status_ = OutputFileStatus::Closed;
415 dropMetaDataForDroppedData_ = dropMetaDataForDroppedData;
416 fastCloningEnabledAtConstruction_ = fastCloningRequested;
417 wasFastCloned_ =
false;
419 TFile::Open(file_.c_str(),
"recreate",
"", compressionLevel));
421 metaDataTree_ = RootOutputTree::makeTTree(
422 filePtr_.get(), rootNames::metaDataTreeName(), 0);
423 fileIndexTree_ = RootOutputTree::makeTTree(
424 filePtr_.get(), rootNames::fileIndexTreeName(), 0);
425 parentageTree_ = RootOutputTree::makeTTree(
426 filePtr_.get(), rootNames::parentageTreeName(), 0);
428 eventHistoryTree_ = RootOutputTree::makeTTree(
429 filePtr_.get(), rootNames::eventHistoryTreeName(), splitLevel);
430 if (eventHistoryTree_ ==
nullptr)
432 throw Exception(errors::FatalRootError)
433 <<
"Failed to create the tree for History objects\n";
435 pEventAux_ =
nullptr;
436 pSubRunAux_ =
nullptr;
438 pResultsAux_ =
nullptr;
439 pEventProductProvenanceVector_ = &eventProductProvenanceVector_;
440 pSubRunProductProvenanceVector_ = &subRunProductProvenanceVector_;
441 pRunProductProvenanceVector_ = &runProductProvenanceVector_;
442 pResultsProductProvenanceVector_ = &resultsProductProvenanceVector_;
443 pHistory_ =
new History;
444 if (eventHistoryTree_->Branch(rootNames::eventHistoryBranchName().c_str(),
449 throw Exception(errors::FatalRootError)
450 <<
"Failed to create a branch for History in the output file\n";
455 make_unique<RootOutputTree>(filePtr_.get(),
458 pEventProductProvenanceVector_,
462 saveMemoryObjectThreshold);
464 make_unique<RootOutputTree>(filePtr_.get(),
467 pSubRunProductProvenanceVector_,
471 saveMemoryObjectThreshold);
472 treePointers_[2] = make_unique<RootOutputTree>(filePtr_.get(),
475 pRunProductProvenanceVector_,
479 saveMemoryObjectThreshold);
481 make_unique<RootOutputTree>(filePtr_.get(),
484 pResultsProductProvenanceVector_,
488 saveMemoryObjectThreshold);
489 dataTypeReported_ =
false;
491 ServiceHandle<DatabaseConnection> {}->get<TKeyVFSOpenPolicy>(
494 SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE));
497 beginTime_ = chrono::steady_clock::now();
499 root::DictionaryChecker checker;
500 checker.checkDictionaries<EventAuxiliary>();
501 checker.checkDictionaries<SubRunAuxiliary>();
502 checker.checkDictionaries<RunAuxiliary>();
503 checker.checkDictionaries<ResultsAuxiliary>();
504 checker.reportMissingDictionaries();
506 createDatabaseTables();
507 TLOG(TLVL_DEBUG + 0) <<
"RootDAQOutFile ctor complete";
510 art::RootDAQOutFile::~RootDAQOutFile()
513 int sts = sysinfo(&info);
514 auto free_percent =
static_cast<unsigned>(info.freeram * 100 / info.totalram);
515 auto free_MB =
static_cast<unsigned>(info.freeram * info.mem_unit >> 20);
516 TRACE(TLVL_DEBUG + 0,
"~RootDAQOutFile free %%%u %.1fMB (%u) buffers=%fGB mem_unit=%u",
517 free_percent, static_cast<float>(info.freeram * info.mem_unit / (1024 * 1024.0)),
518 free_MB, static_cast<float>(info.bufferram * info.mem_unit / (1024 * 1024 * 1024.0)), info.mem_unit);
519 if (free_percent < freePercent_ || free_MB < freeMB_)
521 TLOG(TLVL_DEBUG + 0) <<
"RootDAQOutFile Flush/DONTNEED";
523 sts = posix_fadvise(filePtr_->GetFd(), 0, 0 , POSIX_FADV_DONTNEED);
525 TLOG(TLVL_DEBUG + 0) <<
"~RootDAQOutFile complete sts=" << sts;
528 void art::RootDAQOutFile::createDatabaseTables()
531 create_table(*rootFileDB_,
536 "UNIQUE (SubRun,begin,end) ON CONFLICT IGNORE"});
538 using namespace cet::sqlite;
539 create_table(*rootFileDB_,
"SubRunRangeSets", column<int>{
"Run"});
540 create_table(*rootFileDB_,
541 "SubRunRangeSets_EventRanges",
542 {
"RangeSetsID INTEGER",
543 "EventRangesID INTEGER",
544 "PRIMARY KEY(RangeSetsID,EventRangesID)"},
547 create_table(*rootFileDB_,
"RunRangeSets", column<int>{
"Run"});
548 create_table(*rootFileDB_,
549 "RunRangeSets_EventRanges",
550 {
"RangeSetsID INTEGER",
551 "EventRangesID INTEGER",
552 "PRIMARY KEY(RangeSetsID,EventRangesID)"},
556 void RootDAQOutFile::setFileStatus(OutputFileStatus
const ofs)
558 RecursiveMutexSentry sentry{mutex_, __func__};
563 RootDAQOutFile::currentFileName()
const
565 RecursiveMutexSentry sentry{mutex_, __func__};
569 void RootDAQOutFile::selectProducts()
571 RecursiveMutexSentry sentry{mutex_, __func__};
572 auto selectProductsToWrite = [
this](BranchType
const bt) {
573 auto& items = selectedOutputItemList_[bt];
574 for (
auto const& pr : om_->keptProducts()[bt])
576 auto const& pd = pr.second;
579 if (bt == InResults && !pd.produced())
583 checkDictionaries(pd);
594 for (
auto const& val : items)
596 treePointers_[bt]->addOutputBranch(val.branchDescription_,
600 for_each_branch_type(selectProductsToWrite);
603 void RootDAQOutFile::beginInputFile(RootFileBlock
const* rfb,
604 bool const fastCloneFromOutputModule)
606 RecursiveMutexSentry sentry{mutex_, __func__};
608 bool shouldFastClone{fastCloningEnabledAtConstruction_ &&
609 fastCloneFromOutputModule && (rfb !=
nullptr)};
613 if (shouldFastClone &&
614 !treePointers_[InEvent]->checkSplitLevelAndBasketSize(rfb->tree()))
616 mf::LogWarning(
"FastCloning")
617 <<
"Fast cloning deactivated for this input file due to "
618 <<
"splitting level and/or basket size.";
619 shouldFastClone =
false;
621 else if ((rfb !=
nullptr) && rfb->tree() &&
622 rfb->tree()->GetCurrentFile()->GetVersion() < 60001)
624 mf::LogWarning(
"FastCloning")
625 <<
"Fast cloning deactivated for this input file due to "
626 <<
"ROOT version used to write it (< 6.00/01)\n"
627 "having a different splitting policy.";
628 shouldFastClone =
false;
630 if (shouldFastClone && rfb->fileFormatVersion().value_ < 10)
632 mf::LogWarning(
"FastCloning")
633 <<
"Fast cloning deactivated for this input file due to "
634 <<
"reading in file that has a different ProductID schema.";
635 shouldFastClone =
false;
637 if (shouldFastClone && !fastCloningEnabledAtConstruction_)
639 mf::LogWarning(
"FastCloning")
640 <<
"Fast cloning reactivated for this input file.";
642 treePointers_[InEvent]->beginInputFile(shouldFastClone);
643 auto tree = ((rfb !=
nullptr) && rfb->tree()) ? rfb->tree() :
nullptr;
644 wasFastCloned_ = treePointers_[InEvent]->fastCloneTree(tree);
647 void RootDAQOutFile::incrementInputFileNumber()
649 RecursiveMutexSentry sentry{mutex_, __func__};
650 fp_.update_inputFile();
653 void RootDAQOutFile::respondToCloseInputFile(FileBlock
const& )
655 RecursiveMutexSentry sentry{mutex_, __func__};
656 cet::for_all(treePointers_, [](
auto const& p) { p->setEntries(); });
659 bool RootDAQOutFile::requestsToCloseFile()
661 RecursiveMutexSentry sentry{mutex_, __func__};
662 using namespace chrono;
663 unsigned int constexpr oneK{1024u};
664 fp_.updateSize(filePtr_->GetSize() / oneK);
665 fp_.updateAge(duration_cast<seconds>(steady_clock::now() - beginTime_));
666 return fileSwitchCriteria_.should_close(fp_);
669 void RootDAQOutFile::writeOne(EventPrincipal
const& e)
671 RecursiveMutexSentry sentry{mutex_, __func__};
672 TLOG(TLVL_TRACE) <<
"Start of RootDAQOutFile::writeOne";
676 pEventAux_ = &e.eventAux();
680 fillBranches<InEvent>(e, pEventProductProvenanceVector_);
682 History historyForOutput{e.history()};
683 historyForOutput.addEventSelectionEntry(om_->selectorConfig());
684 pHistory_ = &historyForOutput;
685 int sz = eventHistoryTree_->Fill();
688 throw Exception(errors::FatalRootError)
689 <<
"Failed to fill the History tree for event: " << e.eventID()
690 <<
"\nTTree::Fill() returned " << sz <<
" bytes written." << endl;
693 if (!dataTypeReported_)
695 string dataType{
"MC"};
696 if (pEventAux_->isRealData())
700 dataTypeReported_ =
true;
702 pHistory_ = &e.history();
704 fileIndex_.addEntry(pEventAux_->eventID(), fp_.eventEntryNumber());
706 TLOG(TLVL_TRACE) <<
"End of RootDAQOutFile::writeOne";
709 void RootDAQOutFile::writeSubRun(SubRunPrincipal
const& sr)
711 RecursiveMutexSentry sentry{mutex_, __func__};
712 pSubRunAux_ = &sr.subRunAux();
713 pSubRunAux_->setRangeSetID(subRunRSID_);
714 fillBranches<InSubRun>(sr, pSubRunProductProvenanceVector_);
715 fileIndex_.addEntry(EventID::invalidEvent(pSubRunAux_->subRunID()),
716 fp_.subRunEntryNumber());
717 fp_.update_subRun(status_);
720 void RootDAQOutFile::writeRun(RunPrincipal
const& r)
722 RecursiveMutexSentry sentry{mutex_, __func__};
723 pRunAux_ = &r.runAux();
724 pRunAux_->setRangeSetID(runRSID_);
725 fillBranches<InRun>(r, pRunProductProvenanceVector_);
726 fileIndex_.addEntry(EventID::invalidEvent(pRunAux_->runID()),
727 fp_.runEntryNumber());
728 fp_.update_run(status_);
731 void RootDAQOutFile::writeParentageRegistry()
733 RecursiveMutexSentry sentry{mutex_, __func__};
734 auto pid = root::getObjectRequireDict<ParentageID>();
735 ParentageID
const* hash = &pid;
736 if (parentageTree_->Branch(
737 rootNames::parentageIDBranchName().c_str(), &hash, basketSize_, 0) ==
nullptr)
739 throw Exception(errors::FatalRootError)
740 <<
"Failed to create a branch for ParentageIDs in the output file";
743 auto par = root::getObjectRequireDict<Parentage>();
744 Parentage
const* desc = ∥
745 if (parentageTree_->Branch(
746 rootNames::parentageBranchName().c_str(), &desc, basketSize_, 0) ==
nullptr)
748 throw Exception(errors::FatalRootError)
749 <<
"Failed to create a branch for Parentages in the output file";
752 for (
auto const& pr : ParentageRegistry::get())
756 parentageTree_->Fill();
758 parentageTree_->SetBranchAddress(rootNames::parentageIDBranchName().c_str(),
760 parentageTree_->SetBranchAddress(rootNames::parentageBranchName().c_str(),
764 void RootDAQOutFile::writeFileFormatVersion()
766 RecursiveMutexSentry sentry{mutex_, __func__};
767 FileFormatVersion
const ver{getFileFormatVersion(), getFileFormatEra()};
768 auto const* pver = &ver;
769 TBranch* b = metaDataTree_->Branch(
770 metaBranchRootName<FileFormatVersion>(), &pver, basketSize_, 0);
776 void RootDAQOutFile::writeFileIndex()
778 RecursiveMutexSentry sentry{mutex_, __func__};
779 fileIndex_.sortBy_Run_SubRun_Event();
780 FileIndex::Element elem{};
781 auto const* findexElemPtr = &elem;
782 TBranch* b = fileIndexTree_->Branch(
783 metaBranchRootName<FileIndex::Element>(), &findexElemPtr, basketSize_, 0);
786 for (
auto& entry : fileIndex_)
788 findexElemPtr = &entry;
791 b->SetAddress(
nullptr);
794 void RootDAQOutFile::writeEventHistory()
796 RecursiveMutexSentry sentry{mutex_, __func__};
797 RootOutputTree::writeTTree(eventHistoryTree_);
800 void RootDAQOutFile::writeProcessConfigurationRegistry()
806 void RootDAQOutFile::writeProcessHistoryRegistry()
808 RecursiveMutexSentry sentry{mutex_, __func__};
809 ProcessHistoryMap pHistMap;
810 for (
auto const& pr : ProcessHistoryRegistry::get())
812 pHistMap.emplace(pr);
814 auto const* p = &pHistMap;
815 TBranch* b = metaDataTree_->Branch(
816 metaBranchRootName<ProcessHistoryMap>(), &p, basketSize_, 0);
819 throw Exception(errors::LogicError)
820 <<
"Unable to locate required "
821 "ProcessHistoryMap branch in output "
827 void RootDAQOutFile::writeFileCatalogMetadata(
828 FileStatsCollector
const& stats,
829 FileCatalogMetadata::collection_type
const& md,
830 FileCatalogMetadata::collection_type
const& ssmd)
832 RecursiveMutexSentry sentry{mutex_, __func__};
833 using namespace cet::sqlite;
834 Ntuple<string, string> fileCatalogMetadata{
835 *rootFileDB_,
"FileCatalog_metadata", {{
"Name",
"Value"}},
true};
836 Transaction txn{*rootFileDB_};
837 for (
auto const& kv : md)
839 fileCatalogMetadata.insert(kv.first, kv.second);
843 fileCatalogMetadata.insert(
"file_format",
"\"artroot\"");
846 namespace bpt = boost::posix_time;
847 auto formatted_time = [](
auto const& t) {
848 return cet::canonical_string(bpt::to_iso_extended_string(t));
850 fileCatalogMetadata.insert(
"start_time",
851 formatted_time(stats.outputFileOpenTime()));
853 fileCatalogMetadata.insert(
855 formatted_time(boost::posix_time::second_clock::universal_time()));
857 if (!stats.seenSubRuns().empty())
859 auto I = find_if(md.crbegin(), md.crend(), [](
auto const& p) {
860 return p.first ==
"art.run_type";
866 for (
auto const& srid : stats.seenSubRuns())
868 buf <<
"[ " << srid.run() <<
", " << srid.subRun() <<
", "
869 << cet::canonical_string(I->second) <<
" ], ";
872 buf.seekp(-2, ios_base::cur);
874 fileCatalogMetadata.insert(
"runs", buf.str());
878 fileCatalogMetadata.insert(
"event_count",
879 std::to_string(stats.eventsThisFile()));
880 fileCatalogMetadata.insert(
"first_event",
881 std::to_string(stats.lowestEventID().event()));
882 fileCatalogMetadata.insert(
"last_event",
883 std::to_string(stats.highestEventID().event()));
885 if (!stats.parents().empty())
887 ostringstream pstring;
889 for (
auto const& parent : stats.parents())
891 pstring << cet::canonical_string(parent) <<
", ";
894 pstring.seekp(-2, ios_base::cur);
896 fileCatalogMetadata.insert(
"parents", pstring.str());
901 auto eidToTuple = [](EventID
const& eid) ->
string {
902 ostringstream eidStr;
903 eidStr <<
"[ " << eid.run() <<
", " << eid.subRun() <<
", " << eid.event()
907 fileCatalogMetadata.insert(
"art.first_event",
908 eidToTuple(stats.lowestEventID()));
909 fileCatalogMetadata.insert(
"art.last_event",
910 eidToTuple(stats.highestEventID()));
911 fileCatalogMetadata.insert(
"art.file_format_era",
912 cet::canonical_string(getFileFormatEra()));
913 fileCatalogMetadata.insert(
"art.file_format_version",
914 std::to_string(getFileFormatVersion()));
917 for (
auto const& kv : ssmd)
919 fileCatalogMetadata.insert(kv.first, kv.second);
924 void RootDAQOutFile::writeParameterSetRegistry()
926 RecursiveMutexSentry sentry{mutex_, __func__};
927 fhicl::ParameterSetRegistry::exportTo(*rootFileDB_);
930 void RootDAQOutFile::writeProductDescriptionRegistry()
932 RecursiveMutexSentry sentry{mutex_, __func__};
936 auto productDescriptionsToWrite = [
this, ®](BranchType
const bt) {
937 for (
auto const& pr : descriptionsToPersist_[bt])
939 auto const& desc = pr.second;
940 reg.productList_.emplace(BranchKey{desc}, desc);
943 for_each_branch_type(productDescriptionsToWrite);
944 ProductRegistry
const* regp = ®
945 TBranch* b = metaDataTree_->Branch(
946 metaBranchRootName<ProductRegistry>(), ®p, basketSize_, 0);
952 void RootDAQOutFile::writeProductDependencies()
954 RecursiveMutexSentry sentry{mutex_, __func__};
955 BranchChildren
const* ppDeps = &om_->branchChildren();
956 TBranch* b = metaDataTree_->Branch(
957 metaBranchRootName<BranchChildren>(), &ppDeps, basketSize_, 0);
963 void RootDAQOutFile::writeResults(ResultsPrincipal& resp)
965 RecursiveMutexSentry sentry{mutex_, __func__};
966 pResultsAux_ = &resp.resultsAux();
967 fillBranches<InResults>(resp, pResultsProductProvenanceVector_);
970 void RootDAQOutFile::writeTTrees()
972 TLOG(TLVL_TRACE) <<
"Start of RootDAQOutFile::writeTTrees";
973 RecursiveMutexSentry sentry{mutex_, __func__};
974 RootOutputTree::writeTTree(metaDataTree_);
975 TLOG(TLVL_TRACE) <<
"RootDAQOutFile::writeTTrees after writing metaDataTree_";
976 RootOutputTree::writeTTree(fileIndexTree_);
977 TLOG(TLVL_TRACE) <<
"RootDAQOutFile::writeTTrees after writing fileIndexTree_";
978 RootOutputTree::writeTTree(parentageTree_);
979 TLOG(TLVL_TRACE) <<
"RootDAQOutFile::writeTTrees after writing parentageTree_";
980 for_each_branch_type(
981 [
this](BranchType
const bt) { treePointers_[bt]->writeTree(); });
982 TLOG(TLVL_TRACE) <<
"End of RootDAQOutFile::writeTTrees";
985 void RootDAQOutFile::setSubRunAuxiliaryRangeSetID(RangeSet
const& ranges)
987 RecursiveMutexSentry sentry{mutex_, __func__};
988 subRunRSID_ = getNewRangeSetID(*rootFileDB_, InSubRun, ranges.run());
989 insertIntoEventRanges(*rootFileDB_, ranges);
990 auto const& eventRangesIDs = getExistingRangeSetIDs(*rootFileDB_, ranges);
991 insertIntoJoinTable(*rootFileDB_, InSubRun, subRunRSID_, eventRangesIDs);
994 void RootDAQOutFile::setRunAuxiliaryRangeSetID(RangeSet
const& ranges)
996 RecursiveMutexSentry sentry{mutex_, __func__};
997 runRSID_ = getNewRangeSetID(*rootFileDB_, InRun, ranges.run());
998 insertIntoEventRanges(*rootFileDB_, ranges);
999 auto const& eventRangesIDs = getExistingRangeSetIDs(*rootFileDB_, ranges);
1000 insertIntoJoinTable(*rootFileDB_, InRun, runRSID_, eventRangesIDs);
1003 template<BranchType BT>
1005 RootDAQOutFile::getProduct(OutputHandle
const& oh,
1006 RangeSet
const& prunedProductRS,
1007 string const& wrappedName)
1009 RecursiveMutexSentry sentry{mutex_, __func__};
1010 if constexpr (detail::range_sets_supported(BT))
1014 if (!prunedProductRS.is_valid())
1016 return dummyProductCache_.product(wrappedName);
1019 return oh.isValid() ? oh.wrapper() : dummyProductCache_.product(wrappedName);
1022 template<BranchType BT>
1023 void RootDAQOutFile::fillBranches(Principal
const& principal,
1024 vector<ProductProvenance>* vpp)
1026 TLOG(TLVL_TRACE) <<
"Start of RootDAQOutFile::fillBranches";
1027 RecursiveMutexSentry sentry{mutex_, __func__};
1028 bool const fastCloning = ((BT == InEvent) && wasFastCloned_);
1029 map<unsigned, unsigned> checksumToIndex;
1030 auto const& principalRS = principal.seenRanges();
1031 set<ProductProvenance> keptprv;
1032 for (
auto const& val : selectedOutputItemList_[BT])
1034 auto const& bd = val.branchDescription_;
1035 auto const pid = bd.productID();
1036 descriptionsToPersist_[BT].emplace(pid, bd);
1037 bool const produced = bd.produced();
1038 bool const resolveProd = (produced || !fastCloning ||
1039 treePointers_[BT]->uncloned(bd.branchName()));
1041 bool const keepProvenance =
1042 ((dropMetaData_ == DropMetaData::DropNone) ||
1043 (produced && (dropMetaData_ == DropMetaData::DropPrior)));
1044 auto const& oh = principal.getForOutput(pid, resolveProd);
1045 auto prov = keptprv.begin();
1048 if (oh.productProvenance())
1050 prov = keptprv.insert(*oh.productProvenance()).first;
1051 if ((dropMetaData_ != DropMetaData::DropAll) &&
1052 !dropMetaDataForDroppedData_)
1055 vector<ProductProvenance const*> stacked_pp;
1056 stacked_pp.push_back(&*oh.productProvenance());
1059 if (stacked_pp.empty())
1063 auto current_pp = stacked_pp.back();
1064 stacked_pp.pop_back();
1065 for (
auto const parent_bid :
1066 current_pp->parentage().parents())
1079 auto parent_bd = principal.getProductDescription(parent_bid);
1085 descriptionsToPersist_[BT].emplace(parent_bid, *parent_bd);
1086 if (!parent_bd->produced())
1092 principal.branchToProductProvenance(parent_bid);
1093 if (!parent_pp || (dropMetaData_ != DropMetaData::DropNone))
1097 if (!keptprv.insert(*parent_pp).second)
1102 if ((dropMetaData_ != DropMetaData::DropAll) &&
1103 !dropMetaDataForDroppedData_)
1105 stacked_pp.push_back(parent_pp.get());
1116 auto status = productstatus::dropped();
1119 status = productstatus::neverCreated();
1121 prov = keptprv.emplace(pid, status).first;
1131 auto const& rs = getRangeSet<BT>(oh, principalRS, produced);
1132 if (detail::range_sets_supported(BT) && !rs.is_valid())
1143 auto prov_bid = prov->productID();
1144 if (keptprv.erase(*prov) != 1ull)
1146 throw Exception(errors::LogicError,
"KeptProvenance::setStatus")
1147 <<
"Attempt to set product status for product whose provenance "
1148 "is not being recorded.\n";
1152 .emplace(prov_bid, productstatus::dummyToPreventDoubleCount())
1155 auto const* product = getProduct<BT>(oh, rs, bd.wrappedName());
1156 setProductRangeSetID<BT>(
1157 rs, *rootFileDB_,
const_cast<EDProduct*
>(product), checksumToIndex);
1158 val.product_ = product;
1161 vpp->assign(keptprv.begin(), keptprv.end());
1162 for (
auto const& val : *vpp)
1164 if (val.productStatus() == productstatus::uninitialized())
1166 throw Exception(errors::LogicError,
1167 "RootDAQOutFile::fillBranches(principal, vpp):")
1168 <<
"Attempt to write a product with uninitialized provenance!\n";
1172 TLOG(TLVL_TRACE) <<
"RootDAQOutFile::fillBranches before fillTree call";
1173 treePointers_[BT]->fillTree();
1174 TLOG(TLVL_TRACE) <<
"RootDAQOutFile::fillBranches after fillTree call";
1176 TLOG(TLVL_TRACE) <<
"End of RootDAQOutFile::fillBranches";