00001 #include "artdaq/ArtModules/RootDAQOutput/RootDAQOutFile.h"
00002
00003
00004 #include "Rtypes.h"
00005 #include "TBranchElement.h"
00006 #include "TClass.h"
00007 #include "TFile.h"
00008 #include "TTree.h"
00009 #include "art/Framework/Core/FileBlock.h"
00010 #if ART_HEX_VERSION >= 0x20703
00011 # include "art/Framework/Core/OutputFileGranularity.h"
00012 # include "art/Framework/Services/System/DatabaseConnection.h"
00013 # include "art/Persistency/RootDB/TKeyVFSOpenPolicy.h"
00014 # include "cetlib/sqlite/Ntuple.h"
00015 # include "cetlib/sqlite/Transaction.h"
00016 # include "cetlib/sqlite/create_table.h"
00017 # include "cetlib/sqlite/exec.h"
00018 # include "cetlib/sqlite/insert.h"
00019 #else
00020 # include "art/Framework/Services/Registry/ServiceHandle.h"
00021 # include "cetlib/Ntuple/Transaction.h"
00022 # include "art/Persistency/RootDB/SQLite3Wrapper.h"
00023 #endif
00024 #include "art/Framework/IO/FileStatsCollector.h"
00025 #include "art/Framework/IO/Root/DropMetaData.h"
00026 #include "art/Framework/IO/Root/GetFileFormatEra.h"
00027 #include "art/Framework/IO/Root/GetFileFormatVersion.h"
00028 #include "artdaq/ArtModules/RootDAQOutput/detail/KeptProvenance.h"
00029 #include "artdaq/ArtModules/RootDAQOutput/RootDAQOutFile.h"
00030 #include "art/Framework/Principal/EventPrincipal.h"
00031 #include "art/Framework/Principal/ResultsPrincipal.h"
00032 #include "art/Framework/Principal/RunPrincipal.h"
00033 #include "art/Framework/Principal/SubRunPrincipal.h"
00034 #include "art/Persistency/Provenance/BranchIDListRegistry.h"
00035 #include "art/Persistency/Provenance/ProcessHistoryRegistry.h"
00036 #include "art/Persistency/Provenance/ProductMetaData.h"
00037 #include "art/Persistency/RootDB/SQLErrMsg.h"
00038 #include "art/Version/GetReleaseVersion.h"
00039 #include "boost/date_time/posix_time/posix_time.hpp"
00040 #include "canvas/Persistency/Provenance/rootNames.h"
00041 #include "canvas/Persistency/Provenance/BranchChildren.h"
00042 #include "canvas/Persistency/Provenance/BranchID.h"
00043 #include "canvas/Persistency/Provenance/BranchIDList.h"
00044 #include "canvas/Persistency/Provenance/BranchType.h"
00045 #include "canvas/Persistency/Provenance/EventAuxiliary.h"
00046 #include "canvas/Persistency/Provenance/EventID.h"
00047 #include "canvas/Persistency/Provenance/FileFormatVersion.h"
00048 #include "canvas/Persistency/Provenance/History.h"
00049 #include "canvas/Persistency/Provenance/ParameterSetBlob.h"
00050 #include "canvas/Persistency/Provenance/Parentage.h"
00051 #include "canvas/Persistency/Provenance/ParentageRegistry.h"
00052 #include "canvas/Persistency/Provenance/ProcessHistoryID.h"
00053 #include "canvas/Persistency/Provenance/ProductStatus.h"
00054 #include "canvas/Persistency/Provenance/ResultsAuxiliary.h"
00055 #include "canvas/Persistency/Provenance/RunAuxiliary.h"
00056 #include "canvas/Persistency/Provenance/SubRunAuxiliary.h"
00057 #include "canvas/Utilities/Exception.h"
00058 #include "cetlib/canonical_string.h"
00059 #include "cetlib/container_algorithms.h"
00060 #include "cetlib/exempt_ptr.h"
00061 #include "fhiclcpp/ParameterSet.h"
00062 #include "fhiclcpp/ParameterSetID.h"
00063 #include "fhiclcpp/ParameterSetRegistry.h"
00064 #define TRACE_NAME "RootDAQOutFile"
00065 #include "trace.h"
00066
00067 #include <algorithm>
00068 #include <iomanip>
00069 #include <sstream>
00070 #include <utility>
00071 #include <utility>
00072 #include <vector>
00073
00074 using namespace cet;
00075 using namespace std;
00076 using art::BranchType;
00077 using art::RootDAQOutFile;
00078 using art::rootNames::metaBranchRootName;
00079
00080 namespace {
00081
00082 void create_table(sqlite3* const db,
00083 std::string const& name,
00084 std::vector<std::string> const& columns,
00085 std::string const& suffix = {})
00086 {
00087 if (columns.empty())
00088 throw art::Exception(art::errors::LogicError)
00089 << "Number of sqlite columns specified for table: "
00090 << name << '\n'
00091 << "is zero.\n";
00092
00093 sqlite::Transaction txn {db};
00094 art::SQLErrMsg errMsg;
00095 std::string ddl =
00096 "DROP TABLE IF EXISTS " + name + "; "
00097 "CREATE TABLE " + name +
00098 "("+columns.front();
00099 std::for_each(columns.begin()+1, columns.end(),
00100 [&ddl](auto const& col) {
00101 ddl += ","+col;
00102 } );
00103 ddl += ") ";
00104 ddl += suffix;
00105 ddl += ";";
00106 sqlite3_exec(db, ddl.c_str(), nullptr, nullptr, errMsg);
00107 errMsg.throwIfError();
00108 txn.commit();
00109 }
00110
00111 void
00112 insert_eventRanges_row(sqlite3_stmt* stmt,
00113 art::SubRunNumber_t const sr,
00114 art::EventNumber_t const b,
00115 art::EventNumber_t const e)
00116 {
00117 sqlite3_bind_int64(stmt, 1, sr);
00118 sqlite3_bind_int64(stmt, 2, b);
00119 sqlite3_bind_int64(stmt, 3, e);
00120 sqlite3_step(stmt);
00121 sqlite3_reset(stmt);
00122 }
00123
00124 void
00125 insert_rangeSets_row(sqlite3_stmt* stmt,
00126 art::RunNumber_t const r)
00127 {
00128 sqlite3_bind_int64(stmt, 1, r);
00129 sqlite3_step(stmt);
00130 sqlite3_reset(stmt);
00131 }
00132
00133 void
00134 insert_rangeSets_eventSets_row(sqlite3_stmt* stmt,
00135 unsigned const rsid,
00136 unsigned const esid)
00137 {
00138 sqlite3_bind_int64(stmt, 1, rsid);
00139 sqlite3_bind_int64(stmt, 2, esid);
00140 sqlite3_step(stmt);
00141 sqlite3_reset(stmt);
00142 }
00143
00144 int
00145 found_rowid(sqlite3_stmt* stmt)
00146 {
00147 return sqlite3_step(stmt) == SQLITE_ROW ?
00148 sqlite3_column_int64(stmt,0) :
00149 throw art::Exception(art::errors::SQLExecutionError)
00150 << "ROWID not found for EventRanges.\n"
00151 << "Contact artists@fnal.gov.\n";
00152 }
00153
00154 unsigned
00155 getNewRangeSetID(sqlite3* db,
00156 art::BranchType const bt,
00157 art::RunNumber_t const r)
00158 {
00159 sqlite::Transaction txn {db};
00160 sqlite3_stmt* stmt {nullptr};
00161 std::string const ddl {"INSERT INTO " + art::BranchTypeToString(bt) + "RangeSets(Run) VALUES(?);"};
00162 sqlite3_prepare_v2(db, ddl.c_str(), -1, &stmt, nullptr);
00163 insert_rangeSets_row(stmt, r);
00164 unsigned const rsID = sqlite3_last_insert_rowid(db);
00165 sqlite3_finalize(stmt);
00166 txn.commit();
00167 return rsID;
00168 }
00169
00170 vector<unsigned>
00171 getExistingRangeSetIDs(sqlite3* db, art::RangeSet const& rs)
00172 {
00173 vector<unsigned> rangeSetIDs;
00174 for (auto const& range : rs) {
00175 sqlite::Transaction txn {db};
00176 sqlite3_stmt* stmt {nullptr};
00177 std::string const ddl {"SELECT ROWID FROM EventRanges WHERE "
00178 "SubRun=" + std::to_string(range.subRun()) + " AND "
00179 "begin=" + std::to_string(range.begin()) + " AND "
00180 "end=" + std::to_string(range.end()) + ";"};
00181 sqlite3_prepare_v2(db, ddl.c_str(), -1, &stmt, nullptr);
00182 rangeSetIDs.push_back(found_rowid(stmt));
00183 sqlite3_finalize(stmt);
00184 txn.commit();
00185 }
00186 return rangeSetIDs;
00187 }
00188
00189 void
00190 insertIntoEventRanges(sqlite3* db, art::RangeSet const& rs)
00191 {
00192 sqlite::Transaction txn {db};
00193 sqlite3_stmt* stmt {nullptr};
00194 std::string const ddl {"INSERT INTO EventRanges(SubRun, begin, end) "
00195 "VALUES(?, ?, ?);"};
00196 sqlite3_prepare_v2(db, ddl.c_str(), -1, &stmt, nullptr);
00197 for (auto const& range : rs) {
00198 insert_eventRanges_row(stmt, range.subRun(), range.begin(), range.end());
00199 }
00200 sqlite3_finalize(stmt);
00201 txn.commit();
00202 }
00203
00204 void
00205 insertIntoJoinTable(sqlite3* db,
00206 art::BranchType const bt,
00207 unsigned const rsID,
00208 vector<unsigned> const& eventRangesIDs)
00209 {
00210 sqlite::Transaction txn {db};
00211 sqlite3_stmt* stmt {nullptr};
00212 std::string const ddl {"INSERT INTO "+art::BranchTypeToString(bt) +
00213 "RangeSets_EventRanges(RangeSetsID, EventRangesID) Values(?,?);"};
00214 sqlite3_prepare_v2(db, ddl.c_str(), -1, &stmt, nullptr);
00215 cet::for_all(eventRangesIDs,
00216 [stmt,rsID](auto const eventRangeID) {
00217 insert_rangeSets_eventSets_row(stmt, rsID, eventRangeID);
00218 });
00219 sqlite3_finalize(stmt);
00220 txn.commit();
00221 }
00222
00223 void
00224 maybeInvalidateRangeSet(BranchType const bt,
00225 art::RangeSet const& principalRS,
00226 art::RangeSet& productRS)
00227 {
00228 if (!productRS.is_valid())
00229 return;
00230
00231 assert(principalRS.is_sorted());
00232 assert(productRS.is_sorted());
00233
00234 if (bt == art::InRun && productRS.is_full_run()) return;
00235 if (bt == art::InSubRun && productRS.is_full_subRun()) return;
00236 assert(!productRS.ranges().empty());
00237
00238 auto const r = productRS.run();
00239 auto const& productFront = productRS.ranges().front();
00240 if (!principalRS.contains(r, productFront.subRun(), productFront.begin()))
00241 productRS = art::RangeSet::invalid();
00242 }
00243
00244 using art::detail::RangeSetsSupported;
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285 template <BranchType BT>
00286 std::enable_if_t<RangeSetsSupported<BT>::value, art::RangeSet>
00287 getRangeSet(art::OutputHandle const& oh,
00288 art::RangeSet const& principalRS,
00289 bool const producedInThisProcess)
00290 {
00291 auto rs = oh.isValid() ? oh.rangeOfValidity() : art::RangeSet::invalid();
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302 if (!producedInThisProcess) {
00303 maybeInvalidateRangeSet(BT, principalRS, rs);
00304 }
00305 return rs;
00306 }
00307
00308 template <BranchType BT>
00309 std::enable_if_t<!RangeSetsSupported<BT>::value, art::RangeSet>
00310 getRangeSet(art::OutputHandle const&,
00311 art::RangeSet const& ,
00312 bool const )
00313 {
00314 return art::RangeSet::invalid();
00315 }
00316
00317 template <BranchType BT>
00318 std::enable_if_t<!RangeSetsSupported<BT>::value>
00319 setProductRangeSetID(art::RangeSet const& ,
00320 sqlite3*,
00321 art::EDProduct*,
00322 std::map<unsigned,unsigned>& )
00323 {}
00324
00325 template <BranchType BT>
00326 std::enable_if_t<RangeSetsSupported<BT>::value>
00327 setProductRangeSetID(art::RangeSet const& rs,
00328 sqlite3* db,
00329 art::EDProduct* product,
00330 std::map<unsigned,unsigned>& checksumToIndexLookup)
00331 {
00332 if (!rs.is_valid())
00333 return;
00334
00335
00336 auto it = checksumToIndexLookup.find(rs.checksum());
00337 if (it != checksumToIndexLookup.cend()) {
00338 product->setRangeSetID(it->second);
00339 }
00340 else {
00341 unsigned const rsID = getNewRangeSetID(db, BT, rs.run());
00342 product->setRangeSetID(rsID);
00343 checksumToIndexLookup.emplace(rs.checksum(), rsID);
00344 insertIntoEventRanges(db, rs);
00345 auto const& eventRangesIDs = getExistingRangeSetIDs(db, rs);
00346 insertIntoJoinTable(db, BT, rsID, eventRangesIDs);
00347 }
00348 }
00349
00350 }
00351
00352 art::
00353 RootDAQOutFile::
00354 RootDAQOutFile(OutputModule* om,
00355 string const& fileName,
00356 ClosingCriteria const& fileSwitchCriteria,
00357 int const compressionLevel,
00358 int64_t const saveMemoryObjectThreshold,
00359 int64_t const treeMaxVirtualSize,
00360 int const splitLevel,
00361 int const basketSize,
00362 DropMetaData dropMetaData,
00363 bool const dropMetaDataForDroppedData,
00364 bool const fastCloning)
00365 : om_{om}
00366 , file_{fileName}
00367 , fileSwitchCriteria_{fileSwitchCriteria}
00368 , compressionLevel_{compressionLevel}
00369 , saveMemoryObjectThreshold_{saveMemoryObjectThreshold}
00370 , treeMaxVirtualSize_{treeMaxVirtualSize}
00371 , splitLevel_{splitLevel}
00372 , basketSize_{basketSize}
00373 , dropMetaData_{dropMetaData}
00374 , dropMetaDataForDroppedData_{dropMetaDataForDroppedData}
00375 , fastCloning_{fastCloning}
00376 , filePtr_{TFile::Open(file_.c_str(), "recreate", "", compressionLevel)}
00377 , treePointers_ {
00378 std::make_unique<RootOutputTree>(
00379 # if ART_HEX_VERSION < 0x20703
00380 static_cast<EventPrincipal*>(nullptr),
00381 # endif
00382 filePtr_.get(), InEvent, pEventAux_,
00383 pEventProductProvenanceVector_, basketSize, splitLevel,
00384 treeMaxVirtualSize, saveMemoryObjectThreshold),
00385 std::make_unique<RootOutputTree>(
00386 # if ART_HEX_VERSION < 0x20703
00387 static_cast<SubRunPrincipal*>(nullptr),
00388 # endif
00389 filePtr_.get(), InSubRun, pSubRunAux_,
00390 pSubRunProductProvenanceVector_, basketSize, splitLevel,
00391 treeMaxVirtualSize, saveMemoryObjectThreshold),
00392 std::make_unique<RootOutputTree>(
00393 # if ART_HEX_VERSION < 0x20703
00394 static_cast<RunPrincipal*>(nullptr),
00395 # endif
00396 filePtr_.get(), InRun, pRunAux_,
00397 pRunProductProvenanceVector_, basketSize, splitLevel,
00398 treeMaxVirtualSize, saveMemoryObjectThreshold),
00399 std::make_unique<RootOutputTree>(
00400 # if ART_HEX_VERSION < 0x20703
00401 static_cast<ResultsPrincipal*>(nullptr),
00402 # endif
00403 filePtr_.get(), InResults, pResultsAux_,
00404 pResultsProductProvenanceVector_, basketSize, splitLevel,
00405 treeMaxVirtualSize, saveMemoryObjectThreshold) }
00406 # if ART_HEX_VERSION < 0x20703
00407 , rootFileDB_{filePtr_.get(), "RootFileDB", SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE}
00408 # else
00409 , rootFileDB_{ServiceHandle<DatabaseConnection>{}->get<TKeyVFSOpenPolicy>("RootFileDB",
00410 filePtr_.get(),
00411 SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)}
00412 # endif
00413 {
00414
00415 metaDataTree_ = RootOutputTree::makeTTree(filePtr_.get(), rootNames::metaDataTreeName(), 0);
00416 fileIndexTree_ = RootOutputTree::makeTTree(filePtr_.get(), rootNames::fileIndexTreeName(), 0);
00417 parentageTree_ = RootOutputTree::makeTTree(filePtr_.get(), rootNames::parentageTreeName(), 0);
00418
00419 eventHistoryTree_ = RootOutputTree::makeTTree(filePtr_.get(), rootNames::eventHistoryTreeName(), splitLevel);
00420 if (!eventHistoryTree_) {
00421 throw art::Exception(art::errors::FatalRootError)
00422 << "Failed to create the tree for History objects\n";
00423 }
00424
00425
00426 pHistory_ = new History;
00427 if (!eventHistoryTree_->Branch(rootNames::eventHistoryBranchName().c_str(), &pHistory_, basketSize, 0)) {
00428 throw art::Exception(art::errors::FatalRootError)
00429 << "Failed to create a branch for History in the output file\n";
00430 }
00431 delete pHistory_;
00432 pHistory_ = nullptr;
00433
00434 createDatabaseTables();
00435 }
00436
00437 art::
00438 RootDAQOutFile::OutputItem::Sorter::
00439 Sorter(TTree* tree)
00440 {
00441
00442
00443 if (!tree) {
00444 return;
00445 }
00446 auto branches = tree->GetListOfBranches();
00447 for (int i = 0, sz = branches->GetEntries(); i != sz; ++i) {
00448 auto br = reinterpret_cast<TBranchElement*>(branches->At(i));
00449 treeMap_.emplace(string(br->GetName()), i);
00450 }
00451 }
00452
00453 bool
00454 art::
00455 RootDAQOutFile::OutputItem::Sorter::
00456 operator()(OutputItem const& lh, OutputItem const& rh) const
00457 {
00458
00459
00460
00461
00462 if (treeMap_.empty()) {
00463 return lh < rh;
00464 }
00465 auto const& lname = lh.branchDescription_->branchName();
00466 auto const& rname = rh.branchDescription_->branchName();
00467 auto lit = treeMap_.find(lname);
00468 auto rit = treeMap_.find(rname);
00469 bool lfound = (lit != treeMap_.end());
00470 bool rfound = (rit != treeMap_.end());
00471 if (lfound && rfound) {
00472 return lit->second < rit->second;
00473 }
00474 if (lfound) {
00475 return true;
00476 }
00477 if (rfound) {
00478 return false;
00479 }
00480 return lh < rh;
00481 }
00482
00483 void
00484 art::RootDAQOutFile::createDatabaseTables()
00485 {
00486
00487
00488
00489
00490
00491
00492 create_table(rootFileDB_, "EventRanges",
00493 {"SubRun INTEGER", "begin INTEGER", "end INTEGER", "UNIQUE (SubRun,begin,end) ON CONFLICT IGNORE"});
00494
00495
00496 create_table(rootFileDB_, "SubRunRangeSets",
00497 {"Run INTEGER"});
00498 create_table(rootFileDB_, "SubRunRangeSets_EventRanges",
00499 {"RangeSetsID INTEGER", "EventRangesID INTEGER", "PRIMARY KEY(RangeSetsID,EventRangesID)"},
00500 "WITHOUT ROWID");
00501
00502
00503 create_table(rootFileDB_, "RunRangeSets",
00504 {"Run INTEGER"});
00505 create_table(rootFileDB_, "RunRangeSets_EventRanges",
00506 {"RangeSetsID INTEGER", "EventRangesID INTEGER", "PRIMARY KEY(RangeSetsID,EventRangesID)"},
00507 "WITHOUT ROWID");
00508 }
00509
00510 void
00511 art::
00512 RootDAQOutFile::
00513 selectProducts(FileBlock const& fb)
00514 {
00515 for (int i = InEvent; i < NumBranchTypes; ++i) {
00516 auto bt = static_cast<BranchType>(i);
00517 auto& items = selectedOutputItemList_[bt];
00518 for (auto const& val : items) {
00519 treePointers_[bt]->resetOutputBranchAddress(*val.branchDescription_);
00520 }
00521 items.clear();
00522 for (auto const& bd : om_->keptProducts()[bt]) {
00523 if ((bt < InResults) || bd->produced()) {
00524 items.emplace_back(bd);
00525 }
00526 }
00527 if ((bt == InEvent) && (fb.tree() != nullptr)) {
00528
00529
00530 sort(items.begin(), items.end(), OutputItem::Sorter(fb.tree()));
00531 }
00532 for (auto const& val : items) {
00533 treePointers_[bt]->addOutputBranch(*val.branchDescription_, val.product_);
00534 }
00535 }
00536 }
00537
00538 void
00539 art::
00540 RootDAQOutFile::
00541 beginInputFile(FileBlock const& fb, bool fastClone)
00542 {
00543 selectProducts(fb);
00544 auto const origCurrentlyFastCloning = currentlyFastCloning_;
00545 currentlyFastCloning_ = fastCloning_ && fastClone;
00546 if (currentlyFastCloning_ &&
00547 !treePointers_[InEvent]->checkSplitLevelAndBasketSize(fb.tree())) {
00548 mf::LogWarning("FastCloning")
00549 << "Fast cloning deactivated for this input file due to "
00550 << "splitting level and/or basket size.";
00551 currentlyFastCloning_ = false;
00552 } else if (fb.tree() && fb.tree()->GetCurrentFile()->GetVersion() < 60001) {
00553 mf::LogWarning("FastCloning")
00554 << "Fast cloning deactivated for this input file due to "
00555 << "ROOT version used to write it (< 6.00/01)\n"
00556 "having a different splitting policy.";
00557 currentlyFastCloning_ = false;
00558 }
00559
00560 if (currentlyFastCloning_ && fb.fileFormatVersion().value_ < 9) {
00561 mf::LogWarning("FastCloning")
00562 << "Fast cloning deactivated for this input file due to "
00563 << "reading in file that does not support RangeSets.";
00564 currentlyFastCloning_ = false;
00565 }
00566
00567 if (currentlyFastCloning_ && !origCurrentlyFastCloning) {
00568 mf::LogWarning("FastCloning")
00569 << "Fast cloning reactivated for this input file.";
00570 }
00571 treePointers_[InEvent]->beginInputFile(currentlyFastCloning_);
00572 treePointers_[InEvent]->fastCloneTree(fb.tree());
00573 }
00574
00575 void
00576 art::RootDAQOutFile::incrementInputFileNumber()
00577 {
00578 # if ART_HEX_VERSION >= 0x20703
00579 fp_.update<Granularity::InputFile>();
00580 # else
00581 fp_.update<Boundary::InputFile>();
00582 # endif
00583 }
00584
00585 void
00586 art::
00587 RootDAQOutFile::
00588 respondToCloseInputFile(FileBlock const&)
00589 {
00590 cet::for_all(treePointers_, [](auto const& p){ p->setEntries(); });
00591 }
00592
00593 bool
00594 art::
00595 RootDAQOutFile::
00596 requestsToCloseFile()
00597 {
00598 using namespace std::chrono;
00599 unsigned int constexpr oneK {1024u};
00600 TLOG(10) << "RootDAQOutFile::requestsToCloseFile start" ;
00601 unsigned sz=filePtr_->GetSize();
00602 TLOG(10) << "RootDAQOutFile::requestsToCloseFile after filePtr_->GetSize()";
00603 fp_.updateSize(sz/oneK);
00604 fp_.updateAge(duration_cast<seconds>(steady_clock::now() - beginTime_));
00605 bool ret=fileSwitchCriteria_.should_close(fp_);
00606 TLOG(10) << "RootDAQOutFile::requestsToCloseFile done/return" ;
00607 return ret;
00608 }
00609
00610 void art::RootDAQOutFile::writeOne(EventPrincipal const& e)
00611 {
00612 TLOG(10) << "RootDAQOutFile::writeOne begin" ;
00613
00614
00615
00616 pEventAux_ = &e.aux();
00617
00618
00619
00620 fillBranches<InEvent>(e, pEventProductProvenanceVector_);
00621
00622 History historyForOutput {e.history()};
00623 historyForOutput.addEventSelectionEntry(om_->selectorConfig());
00624 pHistory_ = &historyForOutput;
00625 int sz = eventHistoryTree_->Fill();
00626 if (sz <= 0) {
00627 throw art::Exception(art::errors::FatalRootError)
00628 << "Failed to fill the History tree for event: "
00629 << e.id()
00630 << "\nTTree::Fill() returned "
00631 << sz
00632 << " bytes written."
00633 << endl;
00634 }
00635
00636 if (!dataTypeReported_) {
00637 string dataType {"MC"};
00638 if (pEventAux_->isRealData()) {
00639 dataType = "Data";
00640 }
00641 dataTypeReported_ = true;
00642 }
00643 pHistory_ = &e.history();
00644
00645 fileIndex_.addEntry(pEventAux_->id(), fp_.eventEntryNumber());
00646 # if ART_HEX_VERSION >= 0x20703
00647 fp_.update<Granularity::Event>(status_);
00648 # else
00649 fp_.update<Boundary::Event>(status_);
00650 # endif
00651 TLOG(10) << "RootDAQOutFile::writeOne done/return" ;
00652 }
00653
00654 void
00655 art::
00656 RootDAQOutFile::
00657 writeSubRun(SubRunPrincipal const& sr)
00658 {
00659 pSubRunAux_ = &sr.aux();
00660 pSubRunAux_->setRangeSetID(subRunRSID_);
00661 fillBranches<InSubRun>(sr, pSubRunProductProvenanceVector_);
00662 fileIndex_.addEntry(EventID::invalidEvent(pSubRunAux_->id()), fp_.subRunEntryNumber());
00663 # if ART_HEX_VERSION >= 0x20703
00664 fp_.update<Granularity::SubRun>(status_);
00665 # else
00666 fp_.update<Boundary::SubRun>(status_);
00667 # endif
00668 }
00669
00670 void
00671 art::
00672 RootDAQOutFile::
00673 writeRun(RunPrincipal const& r)
00674 {
00675 pRunAux_ = &r.aux();
00676 pRunAux_->setRangeSetID(runRSID_);
00677 fillBranches<InRun>(r, pRunProductProvenanceVector_);
00678 fileIndex_.addEntry(EventID::invalidEvent(pRunAux_->id()), fp_.runEntryNumber());
00679 # if ART_HEX_VERSION >= 0x20703
00680 fp_.update<Granularity::Run>(status_);
00681 # else
00682 fp_.update<Boundary::Run>(status_);
00683 # endif
00684 }
00685
00686 void
00687 art::
00688 RootDAQOutFile::
00689 writeParentageRegistry()
00690 {
00691 ParentageID const* hash = new ParentageID;
00692 if (!parentageTree_->Branch(rootNames::parentageIDBranchName().c_str(),
00693 &hash, basketSize_, 0)) {
00694 throw art::Exception(art::errors::FatalRootError)
00695 << "Failed to create a branch for ParentageIDs in the output file";
00696 }
00697 delete hash;
00698 hash = nullptr;
00699 Parentage const* desc = new Parentage;
00700 if (!parentageTree_->Branch(rootNames::parentageBranchName().c_str(), &desc,
00701 basketSize_, 0)) {
00702 throw art::Exception(art::errors::FatalRootError)
00703 << "Failed to create a branch for Parentages in the output file";
00704 }
00705 delete desc;
00706 desc = nullptr;
00707 for (auto const& pr : ParentageRegistry::get()) {
00708 hash = &pr.first;
00709 desc = &pr.second;
00710 parentageTree_->Fill();
00711 }
00712 parentageTree_->SetBranchAddress(rootNames::parentageIDBranchName().c_str(), nullptr);
00713 parentageTree_->SetBranchAddress(rootNames::parentageBranchName().c_str(), nullptr);
00714 }
00715
00716 void
00717 art::
00718 RootDAQOutFile::
00719 writeFileFormatVersion()
00720 {
00721 FileFormatVersion ver(getFileFormatVersion(), getFileFormatEra());
00722 FileFormatVersion* pver = &ver;
00723 TBranch* b = metaDataTree_->Branch(metaBranchRootName<FileFormatVersion>(),
00724 &pver, basketSize_, 0);
00725
00726 assert(b);
00727 b->Fill();
00728 }
00729
00730 void
00731 art::
00732 RootDAQOutFile::
00733 writeFileIndex()
00734 {
00735 fileIndex_.sortBy_Run_SubRun_Event();
00736 FileIndex::Element elem {};
00737 auto findexElemPtr = &elem;
00738 TBranch* b = fileIndexTree_->Branch(metaBranchRootName<FileIndex::Element>(),
00739 &findexElemPtr, basketSize_, 0);
00740
00741 assert(b);
00742 for (auto& entry: fileIndex_) {
00743 findexElemPtr = &entry;
00744 b->Fill();
00745 }
00746 b->SetAddress(0);
00747 }
00748
00749 void
00750 art::
00751 RootDAQOutFile::
00752 writeEventHistory()
00753 {
00754 RootOutputTree::writeTTree(eventHistoryTree_);
00755 }
00756
00757 void
00758 art::
00759 RootDAQOutFile::
00760 writeProcessConfigurationRegistry()
00761 {
00762
00763
00764 }
00765
00766 void
00767 art::
00768 RootDAQOutFile::
00769 writeProcessHistoryRegistry()
00770 {
00771 # if ART_HEX_VERSION >= 0x20703
00772 ProcessHistoryMap pHistMap;
00773 for (auto const& pr : ProcessHistoryRegistry::get()) {
00774 pHistMap.emplace(pr);
00775 }
00776 auto const* p = &pHistMap;
00777 # else
00778 ProcessHistoryMap const& r = ProcessHistoryRegistry::get();
00779 ProcessHistoryMap* p = &const_cast<ProcessHistoryMap&>(r);
00780 # endif
00781 TBranch* b = metaDataTree_->Branch(metaBranchRootName<ProcessHistoryMap>(),
00782 &p, basketSize_, 0);
00783 if (b != nullptr) {
00784 b->Fill();
00785 } else {
00786 throw Exception(errors::LogicError)
00787 << "Unable to locate required ProcessHistoryMap branch in output metadata tree.\n";
00788 }
00789 }
00790
00791 void
00792 art::
00793 RootDAQOutFile::
00794 writeBranchIDListRegistry()
00795 {
00796 # if ART_HEX_VERSION >= 0x20703
00797 BranchIDLists const* p = &BranchIDListRegistry::instance().data();
00798 # else
00799 BranchIDLists* p = &BranchIDListRegistry::instance()->data();
00800 # endif
00801 TBranch* b = metaDataTree_->Branch(metaBranchRootName<BranchIDLists>(), &p,
00802 basketSize_, 0);
00803
00804 assert(b);
00805 b->Fill();
00806 }
00807
00808 void
00809 art::
00810 RootDAQOutFile::
00811 writeFileCatalogMetadata(FileStatsCollector const& stats,
00812 FileCatalogMetadata::collection_type const& md,
00813 FileCatalogMetadata::collection_type const& ssmd)
00814 {
00815 # if ART_HEX_VERSION >= 0x20703
00816 using namespace cet::sqlite;
00817 Ntuple<std::string,std::string> fileCatalogMetadata {rootFileDB_, "FileCatalog_metadata", {"Name","Value"}, true};
00818 Transaction txn {rootFileDB_};
00819 for (auto const& kv : md) {
00820 fileCatalogMetadata.insert(kv.first, kv.second);
00821 }
00822
00823 fileCatalogMetadata.insert("file_format", "\"artroot\"");
00824 fileCatalogMetadata.insert("file_format_era", cet::canonical_string(getFileFormatEra()));
00825 fileCatalogMetadata.insert("file_format_version", to_string(getFileFormatVersion()));
00826 # else
00827 ntuple::Ntuple<std::string,std::string> fileCatalogMetadata {rootFileDB_,
00828 "FileCatalog_metadata",
00829 {"Name","Value"},
00830 true};
00831 using namespace sqlite;
00832 Transaction txn {rootFileDB_};
00833 for (auto const& kv : md) {
00834 insert_into(fileCatalogMetadata).values(kv.first, kv.second);
00835 }
00836
00837 insert_into(fileCatalogMetadata).values("file_format", "\"artroot\"");
00838 insert_into(fileCatalogMetadata).values("file_format_era", cet::canonical_string(getFileFormatEra()));
00839 insert_into(fileCatalogMetadata).values("file_format_version", to_string(getFileFormatVersion()));
00840 # endif
00841
00842
00843 namespace bpt = boost::posix_time;
00844 auto formatted_time = [](auto const& t){ return cet::canonical_string(bpt::to_iso_extended_string(t)); };
00845 # if ART_HEX_VERSION >= 0x20703
00846 fileCatalogMetadata.insert("start_time", formatted_time(stats.outputFileOpenTime()));
00847
00848 fileCatalogMetadata.insert("end_time", formatted_time(boost::posix_time::second_clock::universal_time()));
00849 # else
00850 insert_into(fileCatalogMetadata).values("start_time", formatted_time(stats.outputFileOpenTime()));
00851
00852 insert_into(fileCatalogMetadata).values("end_time", formatted_time(boost::posix_time::second_clock::universal_time()));
00853 # endif
00854
00855
00856 if (!stats.seenSubRuns().empty()) {
00857
00858 auto I = find_if(md.crbegin(), md.crend(),
00859 [](auto const& p){ return p.first == "run_type"; } );
00860
00861 if (I != md.crend()) {
00862 ostringstream buf;
00863 buf << "[ ";
00864 for (auto const& srid : stats.seenSubRuns()) {
00865 buf << "[ "
00866 << srid.run()
00867 << ", "
00868 << srid.subRun()
00869 << ", "
00870 << cet::canonical_string(I->second)
00871 << " ], ";
00872 }
00873
00874 buf.seekp(-2, ios_base::cur);
00875 buf << " ]";
00876 # if ART_HEX_VERSION >= 0x20703
00877 fileCatalogMetadata.insert("runs", buf.str());
00878 # else
00879 insert_into(fileCatalogMetadata).values("runs", buf.str());
00880 # endif
00881 }
00882 }
00883
00884 # if ART_HEX_VERSION >= 0x20703
00885 fileCatalogMetadata.insert("event_count", to_string(stats.eventsThisFile()));
00886 # else
00887 insert_into(fileCatalogMetadata).values("event_count", to_string(stats.eventsThisFile()));
00888 # endif
00889
00890 auto eidToTuple = [](EventID const & eid)->string {
00891 ostringstream eidStr;
00892 eidStr << "[ "
00893 << eid.run()
00894 << ", "
00895 << eid.subRun()
00896 << ", "
00897 << eid.event()
00898 << " ]";
00899 return eidStr.str();
00900 };
00901 # if ART_HEX_VERSION >= 0x20703
00902 fileCatalogMetadata.insert("first_event", eidToTuple(stats.lowestEventID()));
00903 fileCatalogMetadata.insert("last_event", eidToTuple(stats.highestEventID()));
00904 # else
00905 insert_into(fileCatalogMetadata).values("first_event", eidToTuple(stats.lowestEventID()));
00906 insert_into(fileCatalogMetadata).values("last_event", eidToTuple(stats.highestEventID()));
00907 # endif
00908
00909 if (!stats.parents().empty()) {
00910 ostringstream pstring;
00911 pstring << "[ ";
00912 for (auto const& parent : stats.parents()) {
00913 pstring << cet::canonical_string(parent) << ", ";
00914 }
00915
00916 pstring.seekp(-2, ios_base::cur);
00917 pstring << " ]";
00918 # if ART_HEX_VERSION >= 0x20703
00919 fileCatalogMetadata.insert("parents", pstring.str());
00920 # else
00921 insert_into(fileCatalogMetadata).values("parents", pstring.str());
00922 # endif
00923 }
00924
00925 for (auto const& kv : ssmd) {
00926 # if ART_HEX_VERSION >= 0x20703
00927 fileCatalogMetadata.insert(kv.first, kv.second);
00928 # else
00929 insert_into(fileCatalogMetadata).values(kv.first, kv.second);
00930 # endif
00931 }
00932 txn.commit();
00933 }
00934
00935 void
00936 art::
00937 RootDAQOutFile::
00938 writeParameterSetRegistry()
00939 {
00940 fhicl::ParameterSetRegistry::exportTo(rootFileDB_);
00941 }
00942
00943 void
00944 art::
00945 RootDAQOutFile::
00946 writeProductDescriptionRegistry()
00947 {
00948
00949
00950 auto end = branchesWithStoredHistory_.end();
00951
00952 ProductRegistry reg;
00953 for ( auto const& pr : ProductMetaData::instance().productList() ) {
00954 if ( branchesWithStoredHistory_.find(pr.second.branchID()) == end ){
00955 continue;
00956 }
00957 reg.productList_.emplace_hint(reg.productList_.end(),pr);
00958 }
00959
00960 auto* regp = ®
00961 TBranch* b = metaDataTree_->Branch(metaBranchRootName<ProductRegistry>(),
00962 ®p, basketSize_, 0);
00963
00964 assert(b);
00965 b->Fill();
00966 }
00967
00968 void
00969 art::
00970 RootDAQOutFile::
00971 writeProductDependencies()
00972 {
00973 BranchChildren& pDeps = const_cast<BranchChildren&>(om_->branchChildren());
00974 BranchChildren* ppDeps = &pDeps;
00975 TBranch* b = metaDataTree_->Branch(metaBranchRootName<BranchChildren>(),
00976 &ppDeps, basketSize_, 0);
00977
00978 assert(b);
00979 b->Fill();
00980 }
00981
00982 void
00983 art::
00984 RootDAQOutFile::
00985 writeResults(ResultsPrincipal & resp)
00986 {
00987 pResultsAux_ = &resp.aux();
00988 fillBranches<InResults>(resp, pResultsProductProvenanceVector_);
00989 }
00990
00991 void
00992 # if ART_HEX_VERSION >= 0x20703
00993 art::RootDAQOutFile::writeTTrees()
00994 # else
00995 RootDAQOutFile::
00996 finishEndFile()
00997 # endif
00998 {
00999 # if ART_HEX_VERSION < 0x20703
01000 metaDataTree_->SetEntries(-1);
01001 # endif
01002 RootOutputTree::writeTTree(metaDataTree_);
01003 RootOutputTree::writeTTree(fileIndexTree_);
01004 RootOutputTree::writeTTree(parentageTree_);
01005
01006 for (int i = InEvent; i < NumBranchTypes; ++i) {
01007 auto const branchType = static_cast<BranchType>(i);
01008 treePointers_[branchType]->writeTree();
01009 }
01010 # if ART_HEX_VERSION < 0x20703
01011
01012
01013
01014 rootFileDB_.reset();
01015
01016 filePtr_->Close();
01017 filePtr_.reset();
01018 # endif
01019 }
01020
01021 template <art::BranchType BT>
01022 void art::RootDAQOutFile::fillBranches(Principal const& principal,
01023 vector<ProductProvenance>* vpp)
01024 {
01025 TLOG(11) << "RootDAQOutFile::fillBranches begin" ;
01026 bool const fastCloning = (BT == InEvent) && currentlyFastCloning_;
01027 detail::KeptProvenance keptProvenance {dropMetaData_, dropMetaDataForDroppedData_, branchesWithStoredHistory_};
01028 map<unsigned,unsigned> checksumToIndex;
01029
01030 auto const& principalRS = principal.seenRanges();
01031
01032 for (auto const& val : selectedOutputItemList_[BT]) {
01033 auto const* bd = val.branchDescription_;
01034 auto const bid = bd->branchID();
01035 branchesWithStoredHistory_.insert(bid);
01036 bool const produced {bd->produced()};
01037 bool const resolveProd = (produced || !fastCloning ||
01038 treePointers_[BT]->uncloned(bd->branchName()));
01039
01040
01041 bool const keepProvenance = (dropMetaData_ == DropMetaData::DropNone ||
01042 (dropMetaData_ == DropMetaData::DropPrior &&
01043 produced));
01044 auto const& oh = principal.getForOutput(bid, resolveProd);
01045
01046 unique_ptr<ProductProvenance> prov {nullptr};
01047 if (keepProvenance) {
01048 if (oh.productProvenance()) {
01049 prov = std::make_unique<ProductProvenance>(keptProvenance.insert(*oh.productProvenance()));
01050 keptProvenance.insertAncestors(*oh.productProvenance(), principal);
01051 }
01052 else {
01053
01054
01055 auto const status = produced ? productstatus::neverCreated() : productstatus::dropped();
01056 prov = std::make_unique<ProductProvenance>(keptProvenance.emplace(bid, status));
01057 }
01058 }
01059
01060
01061 if (resolveProd) {
01062 auto const& rs = getRangeSet<BT>(oh, principalRS, produced);
01063 if (RangeSetsSupported<BT>::value && !rs.is_valid()) {
01064
01065
01066
01067
01068 keptProvenance.setStatus(*prov, productstatus::unknown());
01069 }
01070
01071 auto const* product = getProduct<BT>(oh, rs, bd->wrappedName());
01072 setProductRangeSetID<BT>(rs,
01073 rootFileDB_,
01074 const_cast<EDProduct*>(product),
01075 checksumToIndex);
01076 val.product_ = product;
01077 }
01078 }
01079 vpp->assign(keptProvenance.begin(), keptProvenance.end());
01080 treePointers_[BT]->fillTree();
01081 vpp->clear();
01082 TLOG(11) << "RootDAQOutFile::fillBranches done/return" ;
01083 }
01084
01085 void
01086 art::
01087 RootDAQOutFile::
01088 setSubRunAuxiliaryRangeSetID(RangeSet const& ranges)
01089 {
01090 subRunRSID_ = getNewRangeSetID(rootFileDB_, InSubRun, ranges.run());
01091 insertIntoEventRanges(rootFileDB_, ranges);
01092 auto const& eventRangesIDs = getExistingRangeSetIDs(rootFileDB_, ranges);
01093 insertIntoJoinTable(rootFileDB_, InSubRun, subRunRSID_, eventRangesIDs);
01094 }
01095
01096 void
01097 art::
01098 RootDAQOutFile::
01099 setRunAuxiliaryRangeSetID(RangeSet const& ranges)
01100 {
01101 runRSID_ = getNewRangeSetID(rootFileDB_, InRun, ranges.run());
01102 insertIntoEventRanges(rootFileDB_, ranges);
01103 auto const& eventRangesIDs = getExistingRangeSetIDs(rootFileDB_, ranges);
01104 insertIntoJoinTable(rootFileDB_, InRun, runRSID_, eventRangesIDs);
01105 }
01106
01107 template <BranchType BT>
01108 std::enable_if_t<!RangeSetsSupported<BT>::value, art::EDProduct const*>
01109 art::RootDAQOutFile::getProduct(art::OutputHandle const& oh,
01110 art::RangeSet const& ,
01111 std::string const& wrappedName)
01112 {
01113 if (oh.isValid())
01114 return oh.wrapper();
01115 else
01116 return dummyProductCache_.product(wrappedName);
01117 }
01118
01119 template <BranchType BT>
01120 std::enable_if_t<RangeSetsSupported<BT>::value, art::EDProduct const*>
01121 art::RootDAQOutFile::getProduct(art::OutputHandle const& oh,
01122 art::RangeSet const& prunedProductRS,
01123 std::string const& wrappedName)
01124 {
01125 if (oh.isValid() && prunedProductRS.is_valid())
01126 return oh.wrapper();
01127 else
01128 return dummyProductCache_.product(wrappedName);
01129 }