Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TPM HDF5 Usage #268

Merged
merged 8 commits into from
Jan 26, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ daq_add_library(TokenManager.cpp LivetimeCounter.cpp
iomanager::iomanager
detdataformats::detdataformats
trgdataformats::trgdataformats
hdf5libs::hdf5libs
Boost::iostreams # Boost::iostreams comes in via readoutlibs
detchannelmaps::detchannelmaps)

Expand Down Expand Up @@ -102,6 +103,7 @@ daq_add_application( set_serialization_speed set_serialization_speed.cxx TEST LI
daq_add_application( taset_serialization taset_serialization.cxx TEST LINK_LIBRARIES trigger)
daq_add_application( print_trigger_type print_trigger_type.cxx TEST LINK_LIBRARIES trigger hdf5libs::hdf5libs CLI11::CLI11)
daq_add_application( print_ds_fragments print_ds_fragments.cxx TEST LINK_LIBRARIES trigger hdf5libs::hdf5libs CLI11::CLI11)
daq_add_application( generate_tpset_from_tpm generate_tpset_from_tpm.cxx TEST LINK_LIBRARIES trigger hdf5libs::hdf5libs CLI11::CLI11)

##############################################################################
# Unit Tests
Expand Down
83 changes: 49 additions & 34 deletions plugins/TriggerPrimitiveMaker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,54 +133,69 @@ TriggerPrimitiveMaker::do_scrap(const nlohmann::json& /*args*/)
std::vector<TPSet>
TriggerPrimitiveMaker::read_tpsets(std::string filename, int element)
{
std::ifstream file(filename);
if (!file || file.bad()) {
throw BadTPInputFile(ERS_HERE, get_name(), filename);
}

TriggerPrimitive tp;
TPSet tpset;
std::vector<TPSet> tpsets;

uint64_t prev_tpset_number = 0; // NOLINT(build/unsigned)
uint32_t seqno = 0; // NOLINT(build/unsigned)
uint64_t old_time_start = 0; // NOLINT(build/unsigned)

// Prepare input file
std::unique_ptr<hdf5libs::HDF5RawDataFile> input_file = std::make_unique<hdf5libs::HDF5RawDataFile>(filename);

// Check that the file is a TimeSlice type
if (!input_file->is_timeslice_type()) {
throw BadTPInputFile(ERS_HERE, get_name(), filename);
}

std::vector<std::string> fragment_paths = input_file->get_all_fragment_dataset_paths();

// Read in the file and place the TPs in TPSets. TPSets have time
// boundaries ( n*tpset_time_width + tpset_time_offset ), and TPs are placed
// in TPSets based on the TP start time
//
// This loop assumes the input file is sorted by TP start time
while (file >> tp.time_start >> tp.time_over_threshold >> tp.time_peak >> tp.channel >> tp.adc_integral >>
tp.adc_peak >> tp.detid >> tp.type) {
if (tp.time_start >= old_time_start) {
// NOLINTNEXTLINE(build/unsigned)
uint64_t current_tpset_number = (tp.time_start + m_conf.tpset_time_offset) / m_conf.tpset_time_width;
old_time_start = tp.time_start;

// If we crossed a time boundary, push the current TPSet and reset it
if (current_tpset_number > prev_tpset_number) {
tpset.start_time = prev_tpset_number * m_conf.tpset_time_width + m_conf.tpset_time_offset;
tpset.end_time = tpset.start_time + m_conf.tpset_time_width;
tpset.seqno = seqno;
++seqno;

// 12-Jul-2021, KAB: setting origin fields from configuration
tpset.origin.id = element;

tpset.type = TPSet::Type::kPayload;

if (!tpset.objects.empty()) {
// We don't send empty TPSets, so there's no point creating them
tpsets.push_back(tpset);
for (std::string& fragment_path : fragment_paths) {
std::unique_ptr<daqdataformats::Fragment> frag = input_file->get_frag_ptr(fragment_path);
// Make sure this fragment is a TriggerPrimitive
if (frag->get_fragment_type() != daqdataformats::FragmentType::kTriggerPrimitive) continue;

// Prepare TP buffer
size_t num_tps = frag->get_data_size() / sizeof(trgdataformats::TriggerPrimitive);

trgdataformats::TriggerPrimitive* tp_array = static_cast<trgdataformats::TriggerPrimitive*>(frag->get_data());

for (size_t i(0); i < num_tps; i++) {
auto& tp = tp_array[i];
if (tp.time_start >= old_time_start) {
// NOLINTNEXTLINE(build/unsigned)
uint64_t current_tpset_number = (tp.time_start + m_conf.tpset_time_offset) / m_conf.tpset_time_width;
old_time_start = tp.time_start;

// If we crossed a time boundary, push the current TPSet and reset it
if (current_tpset_number > prev_tpset_number) {
tpset.start_time = prev_tpset_number * m_conf.tpset_time_width + m_conf.tpset_time_offset;
tpset.end_time = tpset.start_time + m_conf.tpset_time_width;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this how this is set in the readout? Does the config have a default value that's the same as readout's default value for TPSet creation?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not refer to how TPSets are created in readout. The changes here were to make use of reading from HDF5 files on the legacy code. There were no logic or definition changes otherwise.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case this might not be the final PR for HDF5 reading. I think as much as possible we want to emulate how the readout creates TPSets, so that the replay app is really trying to replay identical conditions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. There will likely be more changes necessary to catch up to readout.

tpset.seqno = seqno;
++seqno;

// 12-Jul-2021, KAB: setting origin fields from configuration
tpset.origin.id = element;

tpset.type = TPSet::Type::kPayload;

if (!tpset.objects.empty()) {
// We don't send empty TPSets, so there's no point creating them
tpsets.push_back(tpset);
}
prev_tpset_number = current_tpset_number;

tpset.objects.clear();
}
prev_tpset_number = current_tpset_number;

tpset.objects.clear();
tpset.objects.push_back(tp);
} else {
ers::warning(UnsortedTP(ERS_HERE, get_name(), tp.time_start));
}
tpset.objects.push_back(tp);
} else {
ers::warning(UnsortedTP(ERS_HERE, get_name(), tp.time_start));
}
}
if (!tpset.objects.empty()) {
Expand Down
1 change: 1 addition & 0 deletions plugins/TriggerPrimitiveMaker.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "trigger/TPSet.hpp"
#include "trigger/triggerprimitivemaker/Nljs.hpp"

#include "hdf5libs/HDF5RawDataFile.hpp"
#include "appfwk/DAQModule.hpp"
#include "iomanager/Sender.hpp"
#include "triggeralgs/TriggerPrimitive.hpp"
Expand Down
107 changes: 107 additions & 0 deletions test/apps/generate_tpset_from_tpm.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* @file generate_tpset_from_hdf5.cxx Read TP fragments from file and generate a TPSet vector.
* Matches code within TriggerPrimitiveMaker.
*
* This is part of the DUNE DAQ Application Framework, copyright 2020.
* Licensing/copyright details are in the COPYING file that you should have
* received with this code.
*/
#include "CLI/CLI.hpp"

#include "../../plugins/TriggerPrimitiveMaker.hpp"
#include "trgdataformats/TriggerPrimitive.hpp"
#include "hdf5libs/HDF5RawDataFile.hpp"

#include "daqdataformats/Fragment.hpp"
#include "daqdataformats/FragmentHeader.hpp"
#include "daqdataformats/TriggerRecordHeader.hpp"
#include "daqdataformats/Types.hpp"

#include <iostream>

int main(int argc, char** argv) {
CLI::App app{ "App description" };

std::string filename;
app.add_option("-f,--file", filename, "Input HDF5 file");

CLI11_PARSE(app, argc, argv);

dunedaq::trigger::TPSet tpset;
std::vector<dunedaq::trigger::TPSet> tpsets;

uint64_t prev_tpset_number = 0; // NOLINT(build/unsigned)
uint32_t seqno = 0; // NOLINT(build/unsigned)
uint64_t old_time_start = 0; // NOLINT(build/unsigned)
uint32_t tpset_time_width = 10; // Arbitrary
uint32_t tpset_time_offset = 5; // Arbitrary
uint16_t element = 0; // Arbitrary

// Prepare input file
std::unique_ptr<dunedaq::hdf5libs::HDF5RawDataFile> input_file = std::make_unique<dunedaq::hdf5libs::HDF5RawDataFile>(filename);

// Check that the file is a TimeSlice type
if (!input_file->is_timeslice_type()) {
std::cout << "Not a timeslice type.\n";
return 1;
}

std::vector<std::string> fragment_paths = input_file->get_all_fragment_dataset_paths();

// Read in the file and place the TPs in TPSets. TPSets have time
// boundaries ( n*tpset_time_width + tpset_time_offset ), and TPs are placed
// in TPSets based on the TP start time
//
// This loop assumes the input file is sorted by TP start time
for (std::string& fragment_path : fragment_paths) {
std::unique_ptr<dunedaq::daqdataformats::Fragment> frag = input_file->get_frag_ptr(fragment_path);
// Make sure this fragment is a TriggerPrimitive
if (frag->get_fragment_type() != dunedaq::daqdataformats::FragmentType::kTriggerPrimitive) continue;

// Prepare TP buffer
size_t num_tps = frag->get_data_size() / sizeof(dunedaq::trgdataformats::TriggerPrimitive);

dunedaq::trgdataformats::TriggerPrimitive* tp_array = static_cast<dunedaq::trgdataformats::TriggerPrimitive*>(frag->get_data());

for (size_t i(0); i < num_tps; i++) {
auto& tp = tp_array[i];
if (tp.time_start >= old_time_start) {
// NOLINTNEXTLINE(build/unsigned)
uint64_t current_tpset_number = (tp.time_start + tpset_time_offset) / tpset_time_width;
old_time_start = tp.time_start;

// If we crossed a time boundary, push the current TPSet and reset it
if (current_tpset_number > prev_tpset_number) {
tpset.start_time = prev_tpset_number * tpset_time_width + tpset_time_offset;
tpset.end_time = tpset.start_time + tpset_time_width;
tpset.seqno = seqno;
++seqno;

// 12-Jul-2021, KAB: setting origin fields from configuration
tpset.origin.id = element;

tpset.type = dunedaq::trigger::TPSet::Type::kPayload;

if (!tpset.objects.empty()) {
// We don't send empty TPSets, so there's no point creating them
tpsets.push_back(tpset);
}
prev_tpset_number = current_tpset_number;

tpset.objects.clear();
}
tpset.objects.push_back(tp);
} else {
std::cout << "TPs are unsorted.\n";
return 1;
}
}
}
if (!tpset.objects.empty()) {
// We don't send empty TPSets, so there's no point creating them
tpsets.push_back(tpset);
}
std::cout << "Read " << seqno << " TPs into " << tpsets.size() << " TPSets, from file " << filename << std::endl;

return 0;
}