Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions Detectors/Raw/TFReaderDD/include/TFReaderDD/SubTimeFrameFile.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <vector>

#include <Headers/DataHeader.h>
#include "Framework/DataSpecUtils.h"
#include "Framework/OutputSpec.h"
#include "Framework/Logger.h"

namespace o2
Expand Down Expand Up @@ -151,13 +153,13 @@ struct SubTimeFrameFileMeta {
///
std::uint64_t mWriteTimeMs;

auto getTimePoint()
auto getTimePoint() const
{
using namespace std::chrono;
return time_point<system_clock, milliseconds>{milliseconds{mWriteTimeMs}};
}

std::string getTimeString()
std::string getTimeString() const
{
using namespace std::chrono;
std::time_t lTime = system_clock::to_time_t(getTimePoint());
Expand All @@ -167,6 +169,11 @@ struct SubTimeFrameFileMeta {
return lTimeStream.str();
}

const std::string info() const
{
return fmt::format("Size in file: {} Time: {} Version: {}", mStfSizeInFile, getTimeString(), mStfFileVersion);
}

SubTimeFrameFileMeta(const std::uint64_t pStfSize)
: SubTimeFrameFileMeta()
{
Expand Down Expand Up @@ -220,6 +227,11 @@ struct SubTimeFrameFileDataIndex {
static_assert(sizeof(DataIndexElem) == 48,
"DataIndexElem changed -> Binary compatibility is lost!");
}

const std::string info() const
{
return fmt::format("DH: {} Cnt:{} Size:{} Offset:{}", o2::framework::DataSpecUtils::describe(o2::framework::OutputSpec{mDataOrigin, mDataDescription, mSubSpecification}), mDataBlockCnt, mSize, mOffset);
}
};

SubTimeFrameFileDataIndex() = default;
Expand All @@ -240,6 +252,8 @@ struct SubTimeFrameFileDataIndex {
return sizeof(o2::header::DataHeader) + (sizeof(DataIndexElem) * mDataIndex.size());
}

const std::vector<DataIndexElem>& getDataIndex() const { return mDataIndex; }

friend std::ostream& operator<<(std::ostream& pStream, const SubTimeFrameFileDataIndex& pIndex);

private:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@ class SubTimeFrameFileReader
public:

SubTimeFrameFileReader() = delete;
SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask);
SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask, int verb, bool sup0xccdb, bool repaireHeaders, bool rejectDistSTF);
~SubTimeFrameFileReader();

/// Read a single TF from the file
std::unique_ptr<MessagesPerRoute> read(fair::mq::Device* device, const std::vector<o2f::OutputRoute>& outputRoutes, const std::string& rawChannel, size_t slice, bool sup0xccdb, int verbosity);
std::unique_ptr<MessagesPerRoute> read(fair::mq::Device* device, const std::vector<o2f::OutputRoute>& outputRoutes, const std::string& rawChannel, size_t slice);

/// Tell the current position of the file
inline std::uint64_t position() const { return mFileMapOffset; }
Expand All @@ -76,6 +76,13 @@ class SubTimeFrameFileReader
std::uint64_t mFileMapOffset = 0;
std::uint64_t mFileSize = 0;

int mVerbosity = 0;
bool mSup0xccdb = true;
bool mRepaireHeaders = true;
bool mRejectDistSTF = true;

const std::string describeHeader(const o2::header::DataHeader& hd, bool full = false) const;

// helper to make sure written chunks are buffered, only allow pointers
template <typename pointer,
typename = std::enable_if_t<std::is_pointer<pointer>::value>>
Expand Down
21 changes: 17 additions & 4 deletions Detectors/Raw/TFReaderDD/src/RawTFDumpSpec.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class RawTFDump : public Task
bool mCreateRunEnvDir = true;
bool mAcceptCurrentTF = false;
bool mRejectDEADBEEF = false;
bool mRejectDistSTF = true;
int mVerbose = 0;
std::vector<uint32_t> mTFOrbits{}; // 1st orbits of TF accumulated in current file
o2::framework::DataTakingContext mDataTakingContext{};
Expand Down Expand Up @@ -185,6 +186,7 @@ void RawTFDump::init(InitContext& ic)
mWriteTF = false;
mStoreMetaFile = false;
}
mRejectDistSTF = !ic.options().get<bool>("include-dist-stf");
mRejectDEADBEEF = !ic.options().get<bool>("include-deadbeef");
mCreateRunEnvDir = !ic.options().get<bool>("ignore-partition-run-dir");
mMinFileSize = ic.options().get<int64_t>("min-file-size");
Expand Down Expand Up @@ -253,6 +255,7 @@ void RawTFDump::run(ProcessingContext& pc)
try {
size_t lTFSizeInFile = getTFSizeInFile();
SubTimeFrameFileMeta lTFFileMeta(lTFSizeInFile);
lTFFileMeta.mWriteTimeMs = mTimingInfo.creation;

mFile << lTFFileMeta; // Write DataHeader + SubTimeFrameFileMeta
mFile << mTFDataIndex; // Write DataHeader + SubTimeFrameFileDataIndex
Expand All @@ -263,6 +266,10 @@ void RawTFDump::run(ProcessingContext& pc)
const auto& dataPtr = mTFData[lEntry + part];
DataHeader hdToWrite = *reinterpret_cast<const DataHeader*>(dataPtr.first); // make a local DataHeader copy to clear flagsNextHeader bit
hdToWrite.flagsNextHeader = 0;
hdToWrite.splitPayloadIndex = part;
if (mVerbose > 2) {
LOGP(info, "Writing part:{}/{} of {} | TFCounter:{} part{}/{}", part, lCnt, DataSpecUtils::describe(OutputSpec{hdToWrite.dataOrigin, hdToWrite.dataDescription, hdToWrite.subSpecification}), hdToWrite.firstTForbit, hdToWrite.splitPayloadIndex, hdToWrite.splitPayloadParts);
}
buffered_write(reinterpret_cast<const char*>(&hdToWrite), sizeof(DataHeader));
buffered_write(dataPtr.second, hdToWrite.payloadSize);
}
Expand Down Expand Up @@ -517,7 +524,11 @@ void RawTFDump::prepareTFForWriting(ProcessingContext& pc)
LOGP(error, "Failed to extract header");
continue;
}
if (dh->subSpecification == 0xdeadbeef && mRejectDEADBEEF) {
if ((dh->subSpecification == 0xdeadbeef && mRejectDEADBEEF) ||
(dh->dataOrigin == o2::header::gDataOriginFLP && dh->dataDescription == o2::header::gDataDescriptionDISTSTF && mRejectDistSTF)) {
if (mVerbose > 2) {
LOGP(info, "Rejecting {}", DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}));
}
continue;
}
const auto lHdrDataSize = sizeof(DataHeader) + dh->payloadSize;
Expand All @@ -531,9 +542,10 @@ void RawTFDump::prepareTFForWriting(ProcessingContext& pc)
lCnt++;
mTFData.push_back({ref.header, ref.payload});
if (mVerbose > 2) {
LOGP(info, "{}, part: {} of {}, payload {}, 1stTFOrbit: {} TF: {}",
const auto* dph = DataRefUtils::getHeader<DataProcessingHeader*>(ref);
LOGP(info, "{}, part: {} of {}, payload {}, 1stTFOrbit: {} TF: {}, creation: {} | counter:{} size:{} entry:{}",
DataSpecUtils::describe(OutputSpec{dh->dataOrigin, dh->dataDescription, dh->subSpecification}),
dh->splitPayloadIndex, dh->splitPayloadParts, dh->payloadSize, dh->firstTForbit, dh->tfCounter);
dh->splitPayloadIndex, dh->splitPayloadParts, dh->payloadSize, dh->firstTForbit, dh->tfCounter, dph ? dph->creation : -1UL, lCnt, lSize, lEntry);
}
}

Expand All @@ -548,7 +560,7 @@ void RawTFDump::prepareTFForWriting(ProcessingContext& pc)

OutputSpec spec{eq.mDataOrigin, eq.mDataDescription, eq.mSubSpecification};
if (mVerbose > 1) {
LOGP(info, "{} : {} parts of size {} | offset: {}", DataSpecUtils::describe(spec), lCnt, lSize, lCurrOff);
LOGP(info, "{} : {} parts of size {} entry {}| offset: {}", DataSpecUtils::describe(spec), lCnt, lSize, lEntry, lCurrOff);
}
mTFDataIndex.AddStfElement(eq, lCnt, lCurrOff, lSize);
lCurrOff += lSize;
Expand Down Expand Up @@ -577,6 +589,7 @@ DataProcessorSpec getRawTFDumpSpec(const std::string& inpconfig, const std::stri
AlgorithmSpec{adaptFromTask<RawTFDump>(trigger)},
Options{
{"include-deadbeef", VariantType::Bool, false, {"Include DPL-generated 0xdeadbeef subspecs for missing data"}},
{"include-dist-stf", VariantType::Bool, false, {"Include FLP/DISTSUBTIMEFRAME input"}},
{"exclude-trigger-specs", VariantType::String, "", {"Ignore trigger seen in these inputs of triggerspec"}},
{"max-dump-rate", VariantType::Float, 0.f, {"%-age of TFs to dump. W/o external trigger: random(>0) or periodic(<0) rejection, with: max limit"}},
{"rate-est-conf-limit", VariantType::Float, 0.05f, {"quantile for the lowest rate estimate confidence limit"}},
Expand Down
73 changes: 61 additions & 12 deletions Detectors/Raw/TFReaderDD/src/SubTimeFrameFileReader.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ namespace o2f = o2::framework;
/// SubTimeFrameFileReader
////////////////////////////////////////////////////////////////////////////////

SubTimeFrameFileReader::SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask)
: mFileName(pFileName)
SubTimeFrameFileReader::SubTimeFrameFileReader(const std::string& pFileName, o2::detectors::DetID::mask_t detMask, int verb, bool sup0xccdb, bool repaireHeaders, bool rejectDistSTF)
: mFileName(pFileName), mVerbosity(verb), mSup0xccdb(sup0xccdb), mRepaireHeaders(repaireHeaders), mRejectDistSTF(rejectDistSTF)
{
mFileMap.open(mFileName);
if (!mFileMap.is_open()) {
Expand Down Expand Up @@ -178,13 +178,21 @@ Stack SubTimeFrameFileReader::getHeaderStack(std::size_t& pOrigsize)
return Stack(lStackMem);
}

const std::string SubTimeFrameFileReader::describeHeader(const o2::header::DataHeader& hd, bool full) const
{
std::string res = fmt::format("{}", o2f::DataSpecUtils::describe(o2::framework::OutputSpec{hd.dataOrigin, hd.dataDescription, hd.subSpecification}));
if (full) {
res += fmt::format(" part:{}/{} sz:{} TF:{} Orb:{} Run:{}", hd.splitPayloadIndex, hd.splitPayloadParts, hd.payloadSize, hd.tfCounter, hd.firstTForbit, hd.runNumber);
}
return res;
}

std::uint32_t sRunNumber = 0; // TODO: add id to files metadata
std::uint32_t sFirstTForbit = 0; // TODO: add id to files metadata
std::uint64_t sCreationTime = 0;
std::mutex stfMtx;

std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device* device, const std::vector<o2f::OutputRoute>& outputRoutes,
const std::string& rawChannel, size_t slice, bool sup0xccdb, int verbosity)
std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device* device, const std::vector<o2f::OutputRoute>& outputRoutes, const std::string& rawChannel, size_t slice)
{
std::unique_ptr<MessagesPerRoute> messagesPerRoute = std::make_unique<MessagesPerRoute>();
auto& msgMap = *messagesPerRoute.get();
Expand Down Expand Up @@ -252,10 +260,15 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
return nullptr;
}
lStfMetaDataHdr = o2::header::DataHeader::Get(lMetaHdrStack.first());
LOGP(debug, "read filemeta, pos = {}, size = {}", position(), sizeof(SubTimeFrameFileMeta));
if (mVerbosity > 0) {
LOGP(info, "read filemeta, pos = {}, size = {}", position(), sizeof(SubTimeFrameFileMeta));
}
if (!read_advance(&lStfFileMeta, sizeof(SubTimeFrameFileMeta))) {
return nullptr;
}
if (mVerbosity > 0) {
LOGP(info, "TFMeta : {}", lStfFileMeta.info());
}
if (lStfFileMeta.mWriteTimeMs == 0 && creationFallBack != 0) {
if (!creation0Notified) {
creation0Notified = true;
Expand Down Expand Up @@ -319,6 +332,7 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*

std::int64_t lLeftToRead = lStfDataSize;
STFHeader stfHeader{tfID, -1u, -1u};
DataHeader prevHeader;
// read <hdrStack + data> pairs
while (lLeftToRead > 0) {
// allocate and read the Headers
Expand All @@ -335,6 +349,25 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
return nullptr;
}
DataHeader locDataHeader(*lDataHeader);

if (mRepaireHeaders) {
if (locDataHeader == prevHeader) {
if (prevHeader.tfCounter == locDataHeader.tfCounter && (prevHeader.splitPayloadIndex + 1) != locDataHeader.splitPayloadIndex) {
if (mVerbosity > 3) {
LOGP(warn, "Repairing wrong part index for {} to {}", describeHeader(locDataHeader, true), (prevHeader.splitPayloadIndex + 1) % prevHeader.splitPayloadParts);
}
locDataHeader.splitPayloadIndex = (++prevHeader.splitPayloadIndex) % prevHeader.splitPayloadParts;
}
} else { // new header
if (locDataHeader.splitPayloadIndex != 0) {
if (mVerbosity > 2) {
LOGP(warn, "Repairing wrong part index for new {} to {}", describeHeader(locDataHeader, true), (prevHeader.splitPayloadIndex + 1) % prevHeader.splitPayloadParts);
}
locDataHeader.splitPayloadIndex = 0;
}
}
prevHeader = locDataHeader;
}
// sanity check
if (int(locDataHeader.firstTForbit) == -1) {
if (!negativeOrbitNotified) {
Expand All @@ -350,6 +383,18 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
}
locDataHeader.runNumber = runNumberFallBack;
}
const std::uint64_t lDataSize = locDataHeader.payloadSize;

if (locDataHeader.dataOrigin == o2::header::gDataOriginFLP && locDataHeader.dataDescription == o2::header::gDataDescriptionDISTSTF && mRejectDistSTF) {
if (mVerbosity > 0) {
LOGP(warn, "Ignoring stored {}", describeHeader(locDataHeader));
}
if (!ignore_nbytes(lDataSize)) {
return nullptr;
}
lLeftToRead -= (lDataHeaderStackSize + lDataSize); // update the counter
continue;
}
o2::header::Stack headerStack{locDataHeader, o2f::DataProcessingHeader{tfID, 1, lStfFileMeta.mWriteTimeMs}};
if (stfHeader.runNumber == -1) {
stfHeader.id = locDataHeader.tfCounter;
Expand All @@ -359,8 +404,6 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
sRunNumber = stfHeader.runNumber;
sFirstTForbit = stfHeader.firstOrbit;
}

const std::uint64_t lDataSize = locDataHeader.payloadSize;
// do we accept these data?
auto detOrigStatus = mDetOrigMap.find(locDataHeader.dataOrigin);
if (detOrigStatus != mDetOrigMap.end() && !detOrigStatus->second) { // this is a detector data and we don't want to read it
Expand Down Expand Up @@ -403,17 +446,20 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
if (!read_advance(lDataMsg->GetData(), lDataSize)) {
return nullptr;
}
if (verbosity > 0) {
if (verbosity > 1 || locDataHeader.splitPayloadIndex == 0) {
if (mVerbosity > 0) {
if (mVerbosity > 1 || locDataHeader.splitPayloadIndex == 0) {
printStack(headerStack);
if (o2::raw::RDHUtils::checkRDH(lDataMsg->GetData()) && verbosity > 2) {
if (o2::raw::RDHUtils::checkRDH(lDataMsg->GetData()) && mVerbosity > 2) {
o2::raw::RDHUtils::printRDH(lDataMsg->GetData());
}
}
}
#ifdef _RUN_TIMING_MEASUREMENT_
addPartSW.Start(false);
#endif
if (mVerbosity > 2) {
LOGP(info, "addPart {} to {} | HdrSize:{} DataSize:{}", describeHeader(locDataHeader, true), fmqChannel, lHdrStackMsg->GetSize(), lDataMsg->GetSize());
}
addPart(std::move(lHdrStackMsg), std::move(lDataMsg), fmqChannel);
#ifdef _RUN_TIMING_MEASUREMENT_
addPartSW.Stop();
Expand All @@ -435,7 +481,7 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
}

unsigned stfSS[2] = {0, 0xccdb};
for (int iss = 0; iss < (sup0xccdb ? 1 : 2); iss++) {
for (int iss = 0; iss < (mSup0xccdb ? 1 : 2); iss++) {
o2::header::DataHeader stfDistDataHeader(o2::header::gDataDescriptionDISTSTF, o2::header::gDataOriginFLP, stfSS[iss], sizeof(STFHeader), 0, 1);
stfDistDataHeader.payloadSerializationMethod = o2::header::gSerializationMethodNone;
stfDistDataHeader.firstTForbit = stfHeader.firstOrbit;
Expand All @@ -445,7 +491,7 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
if (!fmqChannel.empty()) { // no output channel
auto fmqFactory = device->GetChannel(fmqChannel, 0).Transport();
o2::header::Stack headerStackSTF{stfDistDataHeader, o2f::DataProcessingHeader{tfID, 1, lStfFileMeta.mWriteTimeMs}};
if (verbosity > 0) {
if (mVerbosity > 0) {
printStack(headerStackSTF);
}
auto hdMessageSTF = fmqFactory->CreateMessage(headerStackSTF.size(), fair::mq::Alignment{64});
Expand All @@ -455,6 +501,9 @@ std::unique_ptr<MessagesPerRoute> SubTimeFrameFileReader::read(fair::mq::Device*
#ifdef _RUN_TIMING_MEASUREMENT_
addPartSW.Start(false);
#endif
if (mVerbosity > 2) {
LOGP(info, "addPart forced {} to {} | HdrSize:{} DataSize:{}", describeHeader(stfDistDataHeader, true), fmqChannel, hdMessageSTF->GetSize(), plMessageSTF->GetSize());
}
addPart(std::move(hdMessageSTF), std::move(plMessageSTF), fmqChannel);
#ifdef _RUN_TIMING_MEASUREMENT_
addPartSW.Stop();
Expand Down
13 changes: 11 additions & 2 deletions Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ void TFReaderSpec::init(o2f::InitContext& ic)
mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff;
mInput.maxTFCache = std::max(1, ic.options().get<int>("max-cached-tf"));
mInput.maxFileCache = std::max(1, ic.options().get<int>("max-cached-files"));
mInput.repairHeaders = !ic.options().get<bool>("ignore-repair-headers");
mInput.rejectDistSTF = !ic.options().get<bool>("read-dist-stf");

if (!mInput.fileRunTimeSpans.empty()) {
loadRunTimeSpans(mInput.fileRunTimeSpans);
}
Expand Down Expand Up @@ -263,7 +266,11 @@ void TFReaderSpec::run(o2f::ProcessingContext& ctx)
setTimingInfo(*tfPtr.get());
size_t nparts = 0, dataSize = 0;
if (mInput.sendDummyForMissing) {
int cntAck = 0;
for (auto& msgIt : *tfPtr.get()) { // complete with empty output for the specs which were requested but were not seen in the data
if (mInput.verbosity > 0) {
LOGP(info, "acknowledgeOutput {}", cntAck++);
}
acknowledgeOutput(*msgIt.second.get(), true);
}
addMissingParts(*tfPtr.get());
Expand Down Expand Up @@ -409,7 +416,7 @@ void TFReaderSpec::TFBuilder()
}

LOG(info) << "Processing file " << tfFileName;
SubTimeFrameFileReader reader(tfFileName, mInput.detMask);
SubTimeFrameFileReader reader(tfFileName, mInput.detMask, mInput.verbosity, mInput.sup0xccdb, mInput.repairHeaders, mInput.rejectDistSTF);
size_t locID = 0;
// try
{
Expand All @@ -421,7 +428,7 @@ void TFReaderSpec::TFBuilder()
std::this_thread::sleep_for(sleepTime);
continue;
}
auto tf = reader.read(mDevice, mOutputRoutes, mInput.rawChannelConfig, mAccTFCounter, mInput.sup0xccdb, mInput.verbosity);
auto tf = reader.read(mDevice, mOutputRoutes, mInput.rawChannelConfig, mAccTFCounter);
bool acceptTF = true;
if (tf) {
if (mRunTimeRanges.size()) {
Expand Down Expand Up @@ -675,6 +682,8 @@ o2f::DataProcessorSpec o2::rawdd::getTFReaderSpec(o2::rawdd::TFReaderInp& rinp)
}
spec.options.emplace_back(o2f::ConfigParamSpec{"select-tf-ids", o2f::VariantType::String, "", {"comma-separated list TF IDs to inject (from cumulative counter of TFs seen)"}});
spec.options.emplace_back(o2f::ConfigParamSpec{"fetch-failure-threshold", o2f::VariantType::Float, 0.f, {"Fatil if too many failures( >0: fraction, <0: abs number, 0: no threshold)"}});
spec.options.emplace_back(o2f::ConfigParamSpec{"ignore-repair-headers", o2f::VariantType::Bool, false, {"do not check/repair headers"}});
spec.options.emplace_back(o2f::ConfigParamSpec{"read-dist-stf", o2f::VariantType::Bool, false, {"do not ignore stored FLP/DISTSUBTIMEFRAME (will clash with injected one)"}});
spec.options.emplace_back(o2f::ConfigParamSpec{"max-tf", o2f::VariantType::Int, -1, {"max TF ID to process (<= 0 : infinite)"}});
spec.options.emplace_back(o2f::ConfigParamSpec{"max-tf-per-file", o2f::VariantType::Int, -1, {"max TFs to process per raw-tf file (<= 0 : infinite)"}});
spec.options.emplace_back(o2f::ConfigParamSpec{"max-cached-tf", o2f::VariantType::Int, 3, {"max TFs to cache in memory"}});
Expand Down
2 changes: 2 additions & 0 deletions Detectors/Raw/TFReaderDD/src/TFReaderSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ struct TFReaderInp {
bool sendDummyForMissing = true;
bool sup0xccdb = false;
bool invertIRFramesSelection = false;
bool repairHeaders = true;
bool rejectDistSTF = true;
std::vector<o2::header::DataHeader> hdVec;
std::vector<int> tfIDs{};
};
Expand Down
Loading