21#include "hdf_interface.h"
31 #define RL_API __declspec(dllexport)
33 #define RL_API __declspec(dllimport)
48RL_API
int validate_collector_file_impl(
const HighFive::File & file,
const std::string & filename);
50RL_API
void ensure_file_attributes(HighFive::File & file);
51RL_API
void ensure_collector_group_attributes(HighFive::Group & group);
52RL_API
void ensure_parameter_group_attributes(HighFive::Group & group);
54RL_API std::string validate_file_attributes(
const HighFive::File & file);
55RL_API std::string validate_collector_group(
const HighFive::Group & group);
56RL_API std::string validate_collector_root(
const HighFive::Group & group) ;
58RL_API
bool validate_collector_files(
59 const std::vector<std::string> & in_filenames,
60 std::set<std::string> & datasets,
61 std::map<std::string, size_t> & sizes,
62 std::set<std::string> & valid_files,
63 std::string & collector_name,
64 std::set<std::string> & parameters
73RL_API
bool append_collector_files(
const std::string & out_filename,
const std::vector<std::string> & in_filenames,
bool reset_datasets);
88RL_API
bool merge_collector_files(
const std::string & out_filename,
const std::vector<std::string> & in_filenames,
bool remove_after_merge);
104RL_API
void merge_collector_datasets(
const std::string & out_filename,
const std::vector<std::string> & in_filenames,
const std::string & which_dataset,
bool remove_after_merge);
120 std::set<std::string> users_;
121 std::optional<std::string> filename_{std::nullopt};
122 std::optional<HighFive::File> file_{std::nullopt};
123 std::optional<HighFive::Group> collector_, parameters_{std::nullopt};
126 static const std::string & type_attribute() {
127 static const std::string name{
"type"};
130 static const std::string & collector_group_type() {
131 static const std::string name{
"Readouts"};
134 static const std::string & readout_dataset_name() {
135 static const std::string name{
"readouts"};
138 static const std::string & cue_dataset_name() {
139 static const std::string name{
"cues"};
142 static const std::string & weight_dataset_name() {
143 static const std::string name{
"weights"};
146 static const std::string & normalization_dataset_name() {
147 static const std::string name{
"normalizations"};
156 static const std::string name{
"detector"};
159 static const std::string & efu_address_attribute_name() {
160 static const std::string name{
"efu_address"};
163 static const std::string & efu_port_attribute_name() {
164 static const std::string name{
"efu_port"};
168 static const std::string & parameter_group_type() {
169 static const std::string name{
"Parameters"};
172 static const std::string & parameter_group_name() {
173 static const std::string name{
"parameters"};
176 static const std::string & parameter_unit_attribute_name() {
177 static const std::string name{
"unit"};
180 static const std::string & parameter_description_attribute_name() {
181 static const std::string name{
"description"};
184 static const std::string & program_attribute_name() {
185 static const std::string name{
"program"};
188 static const std::string & version_attribute_name() {
189 static const std::string name{
"version"};
192 static const std::string & revision_attribute_name() {
193 static const std::string name{
"revision"};
196 static const std::string & program_attribute_value() {
197 static const std::string name{
"libreadout"};
200 static const std::string & version_attribute_value() {
201 static const std::string name{
reinterpret_cast<const char *
>(libreadout::version::version_number)};
204 static const std::string & revision_attribute_value() {
205 static const std::string name{
reinterpret_cast<const char *
>(libreadout::version::git_revision)};
216 static void destroy() {
217 instance()->teardown();
220 bool is_setup()
const {
return filename_.has_value(); }
221 [[nodiscard]] std::string current_filename()
const {
return filename_.value_or(
""); }
222 [[nodiscard]]
size_t user_count()
const {
return users_.size(); }
224 void setup(
const std::string& filename) {
225 filename_ = filename;
228 file_ = HighFive::File(filename, HighFive::File::OpenOrCreate);
229 }
catch (HighFive::Exception & ex) {
230 std::cout <<
"Error opening file " << filename <<
" for writing:\n" << ex.what();
233 collector_ = file_->getGroup(
"/");
234 ensure_file_attributes(*file_);
238 if (users_.empty()) {
239 if (file_.has_value()) {
241 file_ = std::nullopt;
243 filename_ = std::nullopt;
244 collector_ = std::nullopt;
245 parameters_ = std::nullopt;
250 std::optional<HighFive::Group> getCollector(
251 const std::string& name,
252 const DetectorType& detector,
253 const ReadoutType& readout
255 return getCollector(name, hdf_compound_type(readout), detector);
266 const std::string& name,
267 const HighFive::DataType& datatype,
268 const std::optional<DetectorType>& detector = std::nullopt,
269 const std::optional<std::string>& description = std::nullopt
271 using namespace HighFive;
272 if (!file_.has_value()) {
273 std::cerr <<
"File not initialized, cannot get dataset!" << std::endl;
276 if (!collector_.has_value()) {
277 collector_ = file_->getGroup(
"/");
279 if (!collector_->exist(name)) {
280 auto group = collector_->createGroup(name);
281 group.createAttribute<std::string>(type_attribute(), collector_group_type());
282 if (detector.has_value()) {
287 DataSetCreateProps props;
288 props.add(Chunking(std::vector<hsize_t>{100}));
291 auto ds = group.createDataSet(readout_dataset_name(), DataSpace({0}, {DataSpace::UNLIMITED}), datatype, props);
292 if (description.has_value()) {
293 ds.createAttribute<std::string>(parameter_description_attribute_name(), description.value());
296 const auto ws = group.createDataSet(weight_dataset_name(), DataSpace({1}, {DataSpace::UNLIMITED}), AtomicType<double>(), props);
297 ws.select({0},{1}).write(0.);
299 const auto cs = group.createDataSet(cue_dataset_name(), DataSpace({1}, {DataSpace::UNLIMITED}), AtomicType<uint32_t>(), props);
300 cs.select({0},{1}).write(0);
302 const auto ns = group.createDataSet(normalization_dataset_name(), DataSpace({1}, {DataSpace::UNLIMITED}), AtomicType<uint64_t>(), props);
303 ns.select({0},{1}).write(0);
308 return collector_->getGroup(name);
311 [[nodiscard]]
auto removeCollector(
const std::string& name) {
312 return users_.erase(name);
317 const std::string& name,
319 const std::optional<std::string> & unit = std::nullopt,
320 const std::optional<std::string> & description = std::nullopt
322 using namespace HighFive;
323 if (!file_.has_value()) {
324 std::cerr <<
"File not initialized, cannot add parameter!" << std::endl;
327 if (!parameters_.has_value()) {
328 parameters_ = collector_->createGroup(parameter_group_name());
329 ensure_parameter_group_attributes(parameters_.value());
331 if (!parameters_->exist(name)) {
332 DataSetCreateProps props;
333 props.add(Chunking(std::vector<hsize_t>{100}));
334 auto ds = parameters_->createDataSet(name, DataSpace({1}, {DataSpace::UNLIMITED}), create_datatype<T>(), props);
335 ds.select({0},{1}).write(value);
336 if (unit.has_value()) {
337 ds.createAttribute(parameter_unit_attribute_name(), unit.value());
339 if (description.has_value()) {
340 ds.createAttribute(parameter_description_attribute_name(), description.value());
344 auto existing_ds = parameters_->getDataSet(name);
345 if (existing_ds.getDataType() != create_datatype<T>()) {
346 std::cerr <<
"Parameter " << name <<
" already exists with a different type!" << std::endl;
348 if (
const auto n = parameter_unit_attribute_name();
350 && (!existing_ds.hasAttribute(n) || existing_ds.getAttribute(n).read<std::string>() != unit.value())
352 std::cerr <<
"Parameter " << name <<
" already exists with a different unit!" << std::endl;
354 if (
const auto n = parameter_description_attribute_name();
355 description.has_value()
356 && (!existing_ds.hasAttribute(n) || existing_ds.getAttribute(n).read<std::string>() != description.value())
358 std::cerr <<
"Parameter " << name <<
" already exists with a different description!" << std::endl;
360 if (existing_ds.select({existing_ds.getDimensions().back()-1},{1}).read<T>() != value) {
361 std::cerr <<
"Parameter " << name <<
" already exists with a different value! Overwriting." << std::endl;
362 existing_ds.write(value);
371 std::optional<HighFive::Group> group_;
372 std::optional<HighFive::DataSet> dataset_;
374 uint64_t normalization_{0};
375 std::optional<DetectorType> detector_{DetectorType::Reserved};
376 std::optional<ReadoutType> readout_{ReadoutType::CAEN};
377 std::optional<HighFive::DataType> datatype_{std::nullopt};
378 size_t record_size_{0};
383 const std::string &filename,
386 const uint64_t normalization=0
387 ): name_{std::move(name)}, normalization_{normalization}, detector_{detectorType_from_int(Type)}, readout_{readoutType_from_detectorType(detector_.value())} {
388 datatype_ = hdf_compound_type(readout_.value());
389 record_size_ = datatype_->getSize();
390 const auto sink = CollectorSink::instance();
391 if (!sink->is_setup()) {
392 sink->setup(filename);
394 group_ = sink->getCollector(name_, detector_.value(), readout_.value());
395 dataset_ = group_->getDataSet(CollectorSink::readout_dataset_name());
409 const std::string &filename,
411 const std::string &type_description,
412 const uint64_t normalization=0,
414 ): name_{std::move(name)}, normalization_{normalization},
415 detector_{Type > 0 ? std::optional(detectorType_from_int(Type)) : std::nullopt},
416 readout_{std::nullopt} {
418 datatype_ = build_hdf5_compound_type(schema);
419 record_size_ = schema.total_size;
420 const auto sink = CollectorSink::instance();
421 if (!sink->is_setup()) {
422 sink->setup(filename);
424 group_ = sink->getCollector(name_, datatype_.value(), detector_, type_description);
425 dataset_ = group_->getDataSet(CollectorSink::readout_dataset_name());
428 [[nodiscard]]
size_t record_size()
const {
return record_size_; }
434 void addRecord(
const double weight,
const void * data) {
435 if (!dataset_.has_value() || !datatype_.has_value()) {
436 std::cerr <<
"Dataset not initialized, cannot save record!" << std::endl;
440 auto & d = dataset_.value();
441 const auto pos = d.getDimensions().back();
443 d.select({pos}, {1}).write_raw(
static_cast<const uint8_t *
>(data), datatype_.value());
447 void addReadout(
const uint8_t Ring,
const uint8_t FEN,
const double tof,
const double weight,
const void * data) {
448 if (!readout_.has_value()) {
449 throw std::runtime_error(
"addReadout requires a typed Collector; use addRecord with a description-based Collector");
452 switch (readout_.value()) {
453 case ReadoutType::CAEN: {
455 return saveReadout(event);
457 case ReadoutType::TTLMonitor: {
459 return saveReadout(event);
461 case ReadoutType::CDT: {
463 return saveReadout(event);
465 case ReadoutType::VMM3: {
467 return saveReadout(event);
469 case ReadoutType::BM0: {
471 return saveReadout(event);
473 case ReadoutType::BM2: {
475 return saveReadout(event);
477 case ReadoutType::BMI: {
479 return saveReadout(event);
482 throw std::runtime_error(
"Saving this readout type is not implemented yet!");
486 template<
class ... Args>
void addParameter(Args && ... args) {
487 CollectorSink::instance()->addParameter(std::forward<Args>(args)...);
492 void setEFU(
const std::string & address,
int port) {
493 if (!group_.has_value() || address.empty() || port <= 0)
return;
494 if (!group_->hasAttribute(CollectorSink::efu_address_attribute_name())) {
495 group_->createAttribute<std::string>(CollectorSink::efu_address_attribute_name(), address);
497 if (!group_->hasAttribute(CollectorSink::efu_port_attribute_name())) {
498 group_->createAttribute<
int>(CollectorSink::efu_port_attribute_name(), port);
504 const auto wds = group_->getDataSet(CollectorSink::weight_dataset_name());
505 auto selection = wds.select({wds.getDimensions().back() - 1}, {1});
506 selection.write(weight_ + selection.read<
double>());
508 if (dataset_.has_value()) {
509 const auto count = dataset_->getDimensions().back();
510 const auto cds = group_->getDataSet(CollectorSink::cue_dataset_name());
511 auto cs = cds.select({cds.getDimensions().back()-1}, {1});
512 cs.write<uint32_t>(
static_cast<uint32_t
>(count));
515 const auto nds = group_->getDataSet(CollectorSink::normalization_dataset_name());
516 auto ns = nds.select({nds.getDimensions().back() - 1}, {1});
517 ns.write(normalization_ + ns.read<uint64_t>());
519 const auto sink = CollectorSink::instance();
520 if (
const auto count = sink->removeCollector(name_); count != 1) {
521 std::cerr <<
"Warning: removed collector " << name_ <<
" from sink " << count <<
" times, but expected to remove exactly one." << std::endl;
529 void saveReadout(T data) {
531 if (!dataset_.has_value() || !datatype_.has_value()) {
532 std::cerr <<
"Dataset not initialized, cannot save readout!" << std::endl;
535 auto & d = dataset_.value();
537 auto pos = d.getDimensions().back();
540 d.select({pos}, {1}).write_raw(
reinterpret_cast<const uint8_t *
>(&data), datatype_.value());
545std::string filename_for_collector(
const std::string & basepath,
const std::string & basename,
const std::string & extension =
"h5");
546std::string filename_for_collector_node(
const std::string & basepath,
const std::string & basename,
int node,
int nodes);
RL_API bool merge_collector_files(const std::string &out_filename, const std::vector< std::string > &in_filenames, bool remove_after_merge)
Merge (append) readouts from multiple same-point collector files, optionally removing the inputs afte...
RL_API bool combine_collector_files(const std::string &out_filename, const std::vector< std::string > &in_filenames)
Combine multiple collector files automatically choosing append vs concatenate based on parameter iden...
RL_API void copy_collector_parameters(const std::string &out_filename, const std::vector< std::string > &in_filenames)
Copy parameters from multiple collector files into a single output file, for a specified scan point i...
RL_API void merge_collector_datasets(const std::string &out_filename, const std::vector< std::string > &in_filenames, const std::string &which_dataset, bool remove_after_merge)
Merge dataset(s) from multiple collector files into a single output file, for a specified scan point ...
RL_API bool append_collector_files(const std::string &out_filename, const std::vector< std::string > &in_filenames, bool reset_datasets)
Append readouts from multiple same-point collector files into a single output file.
RL_API int validate_collector_file(const std::string &filename)
Validate that a given file is a valid collector file, and return the number of scan points it contain...
RL_API bool concatenate_collector_files(const std::string &out_filename, const std::vector< std::string > &in_filenames)
Concatenate readouts from multiple different-point collector files into a single multi-point output f...
Umbrella header for the C API used by the McStas components.
ESS and Bifrost readout data structures.
Parser for C struct type descriptions into a runtime type schema.
RL_API TypeSchema parse_type_description(const std::string &description)
Parse a C struct type description string into a TypeSchema.
Minimal beam-monitor readout event: channel only.
Position-resolving beam-monitor readout event: channel, x, y.
Integrating beam-monitor readout event: channel, sum, 24-bit ADC.
CAEN readout event: group channel and amplitudes A-D.
CDT (DREAM-family) readout event: output module, cathode, anode.
std::optional< HighFive::Group > getCollector(const std::string &name, const HighFive::DataType &datatype, const std::optional< DetectorType > &detector=std::nullopt, const std::optional< std::string > &description=std::nullopt)
Get (or create) a collector group for records of an arbitrary compound datatype.
static const std::string & detector_attribute_name()
The detector identity is a GROUP attribute, alongside the EFU routing attributes: together they say w...
Collector(const std::string &filename, std::string name, const std::string &type_description, const uint64_t normalization=0, const int Type=0)
A description-based collector: records of a user-defined C struct layout.
void setEFU(const std::string &address, int port)
Write optional EFU destination attributes onto this collector group. Only writes if address is non-em...
void addRecord(const double weight, const void *data)
Store one record of the collector's compound type, accumulating its weight.
TTL beam-monitor readout event: channel, position, ADC.
VMM3 readout event: BC, OTADC, GEO, TDC, VMM, channel.