McStas Readout Master 0.3.3
Loading...
Searching...
No Matches
reader.h
1#pragma once
2
3#include <algorithm>
4#include <cstdint>
5#include <cstring>
6#include <map>
7#include <memory>
8#include <optional>
9#include <set>
10#include <sstream>
11#include <stdexcept>
12#include <string>
13#include <vector>
14
15#include "ReadoutClass.h"
16#include "CollectorClass.h"
17
18
19#ifdef WIN32
20// Export symbols if compile flags "READOUT_SHARED" and "READOUT_EXPORT" are set on Windows.
21 #ifdef READOUT_SHARED
22 #ifdef READOUT_EXPORT
23 #define RL_API __declspec(dllexport)
24 #else
25 #define RL_API __declspec(dllimport)
26 #endif
27 #else
28 // Disable definition if linking statically.
29 #define RL_API
30 #endif
31#else
32// Disable definition for non-Win32 systems.
33#define RL_API
34#endif
35
38 std::string name;
39 HighFive::DataType datatype;
40 std::optional<std::string> unit;
41 std::optional<std::string> description;
42};
43
50class Reader {
51 std::shared_ptr<HighFive::File> file_;
52 std::string group_name_;
53 std::optional<HighFive::Group> group_;
54 std::optional<HighFive::DataSet> readouts_, cues_, weights_, normalizations_;
55 std::vector<uint32_t> cue_values_;
56 std::vector<uint64_t> normalization_values_;
57 std::optional<DetectorType> detector_{std::nullopt};
58 std::optional<ReadoutType> sendable_{std::nullopt};
59 std::optional<std::string> description_{std::nullopt};
60 std::optional<std::string> efu_address_{std::nullopt};
61 std::optional<uint16_t> efu_port_{std::nullopt};
62
63 [[nodiscard]] std::pair<size_t, size_t> point_bounds(const size_t point) const {
64 if (point >= cue_values_.size()) {
65 throw std::runtime_error("Out of bounds point requested");
66 }
67 const auto end = static_cast<size_t>(cue_values_.at(point));
68 const auto start = point > 0 ? static_cast<size_t>(cue_values_.at(point - 1)) : 0u;
69 if (end < start || end > size()) {
70 throw std::runtime_error("Invalid cue values in collector group");
71 }
72 return {start, end};
73 }
74
75public:
76 RL_API explicit Reader(std::shared_ptr<HighFive::File> file, const std::string & collector_name): file_{std::move(file)}, group_name_{collector_name} {
77 if (!file_) {
78 throw std::runtime_error("Reader requires an open file");
79 }
80 if (!file_->exist(group_name_)) {
81 std::stringstream s;
82 s << "Collector group \"" << group_name_ << "\" not found in file";
83 throw std::runtime_error(s.str());
84 }
85 group_ = file_->getGroup(group_name_);
86 if (const auto result = validate_collector_group(group_.value()); !result.empty()) {
87 std::stringstream s;
88 s << "Collector group \"" << group_name_ << "\" is invalid: " << result;
89 throw std::runtime_error(s.str());
90 }
91
92 readouts_ = group_->getDataSet(CollectorSink::readout_dataset_name());
93 cues_ = group_->getDataSet(CollectorSink::cue_dataset_name());
94 weights_ = group_->getDataSet(CollectorSink::weight_dataset_name());
95 normalizations_ = group_->getDataSet(CollectorSink::normalization_dataset_name());
96
97 // The detector identity (the ESS packet-type byte at replay) is a group
98 // attribute, next to the EFU routing attributes; absent for user-described
99 // record groups with no EFU destiny. The record layout has no attribute at
100 // all: it is the dataset's own compound datatype.
101 if (group_->hasAttribute(CollectorSink::detector_attribute_name())) {
102 detector_ = detectorType_from_name(group_->getAttribute(CollectorSink::detector_attribute_name()).read<std::string>());
103 }
104 if (readouts_->hasAttribute(CollectorSink::parameter_description_attribute_name())) {
105 description_ = readouts_->getAttribute(CollectorSink::parameter_description_attribute_name()).read<std::string>();
106 }
107 // EFU-sendability is decided by the stored datatype matching a registry compound type,
108 // not by the attributes: an attribute cannot lie about the record layout
109 const auto stored = readouts_->getDataType();
110 for (const auto rt : {ReadoutType::CAEN, ReadoutType::TTLMonitor, ReadoutType::CDT, ReadoutType::VMM3,
111 ReadoutType::BM0, ReadoutType::BM2, ReadoutType::BMI}) {
112 if (stored == hdf_compound_type(rt)) {
113 sendable_ = rt;
114 break;
115 }
116 }
117
118 if (group_->hasAttribute(CollectorSink::efu_address_attribute_name())) {
119 efu_address_ = group_->getAttribute(CollectorSink::efu_address_attribute_name()).read<std::string>();
120 }
121 if (group_->hasAttribute(CollectorSink::efu_port_attribute_name())) {
122 const auto port = group_->getAttribute(CollectorSink::efu_port_attribute_name()).read<int>();
123 if (port > 0 && port <= 65535) {
124 efu_port_ = static_cast<uint16_t>(port);
125 }
126 }
127
128 cues_->read(cue_values_);
129 if (cue_values_.empty()) {
130 throw std::runtime_error("Collector cues dataset is empty");
131 }
132 if (!std::ranges::is_sorted(cue_values_)) {
133 throw std::runtime_error("Collector cues dataset must be monotonically non-decreasing");
134 }
135 if (cue_values_.back() != size()) {
136 throw std::runtime_error("Collector cues end value does not match readout dataset size");
137 }
138 normalizations_->read(normalization_values_);
139 if (weights_->getDimensions().back() != cue_values_.size() || normalization_values_.size() != cue_values_.size()) {
140 throw std::runtime_error("Collector point datasets (cues, weights, normalizations) have mismatched dimensions");
141 }
142 }
143
144 RL_API ~Reader() = default;
145
146 RL_API [[nodiscard]] const std::string & collector_name() const { return group_name_; }
148 RL_API [[nodiscard]] DetectorType detector_type() const { return detector_.value_or(DetectorType::Reserved); }
150 RL_API [[nodiscard]] ReadoutType readout_type() const {
151 if (sendable_.has_value()) { return sendable_.value(); }
152 throw std::runtime_error("Collector group has no readout type: it stores user-described records");
153 }
156 RL_API [[nodiscard]] std::optional<ReadoutType> sendable_readout_type() const { return sendable_; }
158 RL_API [[nodiscard]] const std::optional<std::string> & type_description() const { return description_; }
159 RL_API [[nodiscard]] size_t size() const { return readouts_->getDimensions().back(); }
160 RL_API [[nodiscard]] size_t points() const { return cue_values_.size(); }
161 RL_API [[nodiscard]] HighFive::DataType datatype() const { return readouts_->getDataType(); }
162 RL_API [[nodiscard]] size_t record_size() const { return readouts_->getDataType().getSize(); }
163
166 RL_API [[nodiscard]] double point_weight(const size_t point) const {
167 if (point >= cue_values_.size()) {
168 throw std::runtime_error("Out of bounds point requested");
169 }
170 return weights_->select({point}, {1}).read<double>();
171 }
172
175 RL_API [[nodiscard]] uint64_t point_normalization(const size_t point) const {
176 if (point >= normalization_values_.size()) {
177 throw std::runtime_error("Out of bounds point requested");
178 }
179 return normalization_values_[point];
180 }
181
184 RL_API [[nodiscard]] std::vector<uint8_t> get_raw(const size_t index, const size_t count) const {
185 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds record requested"); }
186 const auto datatype = readouts_->getDataType();
187 std::vector<uint8_t> buffer(count * datatype.getSize());
188 readouts_->select({index}, {count}).read_raw(buffer.data(), datatype);
189 return buffer;
190 }
191
193 RL_API [[nodiscard]] std::optional<std::string> efu_address() const { return efu_address_; }
195 RL_API [[nodiscard]] std::optional<uint16_t> efu_port() const { return efu_port_; }
196
197 RL_API [[nodiscard]] size_t point_offset(const size_t point) const {
198 return point_bounds(point).first;
199 }
200 RL_API [[nodiscard]] size_t point_size(const size_t point) const {
201 const auto [start, end] = point_bounds(point);
202 return end - start;
203 }
204
205 RL_API auto get_CAEN(const size_t index, const size_t count) const {
206 if (sendable_ != ReadoutType::CAEN){ throw std::runtime_error("Non CAEN readout type"); }
207 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
208 std::vector<CAEN_event> event(count);
209 const auto datatype = readouts_->getDataType();
210 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
211 return event;
212 }
213 RL_API auto get_TTLMonitor(const size_t index, const size_t count) const {
214 if (sendable_ != ReadoutType::TTLMonitor){ throw std::runtime_error("Non TTLMonitor readout type"); }
215 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
216 std::vector<TTLMonitor_event> event(count);
217 const auto datatype = readouts_->getDataType();
218 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
219 return event;
220 }
221 RL_API auto get_VMM3(const size_t index, const size_t count) const{
222 if (sendable_ != ReadoutType::VMM3) { throw std::runtime_error("Non VMM3 readout type"); }
223 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
224 std::vector<VMM3_event> event(count);
225 const auto datatype = readouts_->getDataType();
226 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
227 return event;
228 }
229 RL_API auto get_CDT(const size_t index, const size_t count) const{
230 if (sendable_ != ReadoutType::CDT) { throw std::runtime_error("Non CDT readout type"); }
231 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
232 std::vector<CDT_event> event(count);
233 const auto datatype = readouts_->getDataType();
234 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
235 return event;
236 }
237 RL_API auto get_BM0(const size_t index, const size_t count) const{
238 if (sendable_ != ReadoutType::BM0) { throw std::runtime_error("Non BM0 readout type"); }
239 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
240 std::vector<BM0_event> event(count);
241 const auto datatype = readouts_->getDataType();
242 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
243 return event;
244 }
245 RL_API auto get_BM2(const size_t index, const size_t count) const{
246 if (sendable_ != ReadoutType::BM2) { throw std::runtime_error("Non BM2 readout type"); }
247 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
248 std::vector<BM2_event> event(count);
249 const auto datatype = readouts_->getDataType();
250 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
251 return event;
252 }
253 RL_API auto get_BMI(const size_t index, const size_t count) const {
254 if (sendable_ != ReadoutType::BMI) { throw std::runtime_error("Non BMI readout type"); }
255 if (index >= size() || index + count > size()) { throw std::runtime_error("Out of bounds event requested");}
256 std::vector<BMI_event> event(count);
257 const auto datatype = readouts_->getDataType();
258 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
259 return event;
260 }
261
262 RL_API auto get_point_CAEN(const size_t point) const {
263 const auto [start, end] = point_bounds(point);
264 return get_CAEN(start, end - start);
265 }
266 RL_API auto get_point_TTLMonitor(const size_t point) const {
267 const auto [start, end] = point_bounds(point);
268 return get_TTLMonitor(start, end - start);
269 }
270 RL_API auto get_point_VMM3(const size_t point) const {
271 const auto [start, end] = point_bounds(point);
272 return get_VMM3(start, end - start);
273 }
274 RL_API auto get_point_CDT(const size_t point) const {
275 const auto [start, end] = point_bounds(point);
276 return get_CDT(start, end - start);
277 }
278 RL_API auto get_point_BM0(const size_t point) const {
279 const auto [start, end] = point_bounds(point);
280 return get_BM0(start, end - start);
281 }
282 RL_API auto get_point_BM2(const size_t point) const {
283 const auto [start, end] = point_bounds(point);
284 return get_BM2(start, end - start);
285 }
286 RL_API auto get_point_BMI(const size_t point) const {
287 const auto [start, end] = point_bounds(point);
288 return get_BMI(start, end - start);
289 }
290};
291
298 std::string filename_;
299 std::shared_ptr<HighFive::File> file_;
300 std::optional<HighFive::Group> parameters_;
301 std::vector<Reader> readers_;
302 std::vector<std::string> parameter_names_;
303 std::vector<ParameterDatasetView> parameter_views_;
304 size_t points_{0};
305
306 static bool is_parameter_group(const HighFive::Group & group) {
307 return group.hasAttribute(CollectorSink::type_attribute())
308 && group.getAttribute(CollectorSink::type_attribute()).read<std::string>() == CollectorSink::parameter_group_type();
309 }
310
311 static bool is_collector_group(const HighFive::Group & group) {
312 return group.hasAttribute(CollectorSink::type_attribute())
313 && group.getAttribute(CollectorSink::type_attribute()).read<std::string>() == CollectorSink::collector_group_type();
314 }
315
316public:
317 RL_API explicit ReaderSource(const std::string& filename): filename_{filename} {
318 try {
319 file_ = std::make_shared<HighFive::File>(filename, HighFive::File::ReadOnly);
320 } catch (HighFive::Exception & ex) {
321 std::stringstream s;
322 s << "Error opening file " << filename << ":\n" << ex.what();
323 throw std::runtime_error(s.str());
324 }
325
326 std::string program;
327 if (file_->hasAttribute(CollectorSink::program_attribute_name())) {
328 file_->getAttribute(CollectorSink::program_attribute_name()).read(program);
329 }
330 if (program != CollectorSink::program_attribute_value()) {
331 throw std::runtime_error("The provided HDF file was not produced using libreadout Collector output");
332 }
333 if (!file_->hasAttribute(CollectorSink::version_attribute_name()) || !file_->hasAttribute(CollectorSink::revision_attribute_name())) {
334 throw std::runtime_error("Collector file is missing required version attributes");
335 }
336
337 const auto version = file_->getAttribute(CollectorSink::version_attribute_name()).read<std::string>();
338 const auto this_version = std::string(reinterpret_cast<const char *>(libreadout::version::version_number));
339 if (version != this_version){
340 std::cout << "The file was produced using libreadout " << version;
341 std::cout << " not current " << this_version << std::endl;
342 }
343
344 const auto root = file_->getGroup("/");
345 std::vector<std::string> collector_names;
346 for (size_t i=0; i<root.getNumberObjects(); ++i) {
347 const auto name = root.getObjectName(i);
348 if (root.getObjectType(name) != HighFive::ObjectType::Group) {
349 continue;
350 }
351 const auto group = root.getGroup(name);
352 if (name == CollectorSink::parameter_group_name() || is_parameter_group(group)) {
353 parameters_ = group;
354 } else if (is_collector_group(group)) {
355 collector_names.push_back(name);
356 }
357 }
358
359 if (collector_names.empty()) {
360 throw std::runtime_error("Collector file has no collector groups. Legacy flat readout files are not supported.");
361 }
362 std::ranges::sort(collector_names);
363 readers_.reserve(collector_names.size());
364 for (const auto & name : collector_names) {
365 readers_.emplace_back(file_, name);
366 }
367
368 points_ = readers_.front().points();
369 for (const auto & reader : readers_) {
370 if (reader.points() != points_) {
371 throw std::runtime_error("Collector groups have inconsistent point counts");
372 }
373 }
374
375 if (parameters_.has_value()) {
376 for (size_t i=0; i<parameters_->getNumberObjects(); ++i) {
377 const auto name = parameters_->getObjectName(i);
378 if (parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
379 continue;
380 }
381 const auto ds = parameters_->getDataSet(name);
382 if (const auto dims = ds.getDimensions(); dims.size() != 1u || dims.back() != points_) {
383 throw std::runtime_error("Parameter datasets must be 1-D and match collector point count");
384 }
385 parameter_names_.push_back(name);
386 ParameterDatasetView view{name, ds.getDataType(), std::nullopt, std::nullopt};
387 if (ds.hasAttribute(CollectorSink::parameter_unit_attribute_name())) {
388 view.unit = ds.getAttribute(CollectorSink::parameter_unit_attribute_name()).read<std::string>();
389 }
390 if (ds.hasAttribute(CollectorSink::parameter_description_attribute_name())) {
391 view.description = ds.getAttribute(CollectorSink::parameter_description_attribute_name()).read<std::string>();
392 }
393 parameter_views_.push_back(std::move(view));
394 }
395 std::ranges::sort(parameter_names_);
396 std::ranges::sort(parameter_views_, [](const auto & a, const auto & b){ return a.name < b.name; });
397 }
398 }
399
400 RL_API [[nodiscard]] size_t points() const { return points_; }
401 RL_API [[nodiscard]] const std::vector<Reader> & readers() const { return readers_; }
402 RL_API [[nodiscard]] bool has_parameters() const { return parameters_.has_value(); }
403 RL_API [[nodiscard]] const std::vector<std::string> & parameter_names() const { return parameter_names_; }
404 RL_API [[nodiscard]] const std::vector<ParameterDatasetView> & parameter_views() const { return parameter_views_; }
405
406 RL_API [[nodiscard]] const Reader & reader(const std::string & collector_name) const {
407 for (const auto & reader : readers_) {
408 if (reader.collector_name() == collector_name) {
409 return reader;
410 }
411 }
412 throw std::runtime_error("Requested collector reader does not exist");
413 }
414
415 RL_API [[nodiscard]] std::vector<uint8_t> parameter_value(const std::string & name, const size_t point) const {
416 if (!parameters_.has_value()) {
417 throw std::runtime_error("Collector file does not contain a parameters group");
418 }
419 if (point >= points_) {
420 throw std::runtime_error("Out of bounds parameter point requested");
421 }
422 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
423 throw std::runtime_error("Requested parameter dataset does not exist");
424 }
425 const auto ds = parameters_->getDataSet(name);
426 const auto type = ds.getDataType();
427 std::vector<uint8_t> buffer(type.getSize());
428 ds.select({point}, {1}).read_raw(buffer.data(), type);
429 return buffer;
430 }
431
432 RL_API [[nodiscard]] bool parameter_is_double(const std::string & name) const {
433 for (const auto & parameter_view : parameter_views_) {
434 if (parameter_view.name == name) {
435 return parameter_view.datatype == HighFive::AtomicType<double>();
436 }
437 }
438 return false;
439 }
440
441 RL_API [[nodiscard]] double parameter_double_value(const std::string & name, const size_t point) const {
442 if (!parameters_.has_value()) {
443 throw std::runtime_error("Collector file does not contain a parameters group");
444 }
445 if (point >= points_) {
446 throw std::runtime_error("Out of bounds parameter point requested");
447 }
448 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
449 throw std::runtime_error("Requested parameter dataset does not exist");
450 }
451 const auto ds = parameters_->getDataSet(name);
452 const auto type = ds.getDataType();
453 if (type != HighFive::AtomicType<double>()) {
454 throw std::runtime_error("Requested parameter dataset does not have type double");
455 }
456 return ds.select({point}, {1}).read<double>();
457 }
458
459 RL_API [[nodiscard]] bool parameter_is_float(const std::string & name) const {
460 for (const auto & parameter_view : parameter_views_) {
461 if (parameter_view.name == name) {
462 return parameter_view.datatype == HighFive::AtomicType<float>();
463 }
464 }
465 return false;
466 }
467 RL_API [[nodiscard]] float parameter_float_value(const std::string & name, const size_t point) const {
468 if (!parameters_.has_value()) {
469 throw std::runtime_error("Collector file does not contain a parameters group");
470 }
471 if (point >= points_) {
472 throw std::runtime_error("Out of bounds parameter point requested");
473 }
474 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
475 throw std::runtime_error("Requested parameter dataset does not exist");
476 }
477 const auto ds = parameters_->getDataSet(name);
478 const auto type = ds.getDataType();
479 if (type != HighFive::AtomicType<float>()) {
480 throw std::runtime_error("Requested parameter dataset does not have type float");
481 }
482 return ds.select({point}, {1}).read<float>();
483 }
484
485
486 RL_API [[nodiscard]] bool parameter_is_char(const std::string & name) const {
487 for (const auto & parameter_view : parameter_views_) {
488 if (parameter_view.name == name) {
489 return parameter_view.datatype == HighFive::AtomicType<char>();
490 }
491 }
492 return false;
493 }
494 RL_API [[nodiscard]] char parameter_char_value(const std::string & name, const size_t point) const {
495 if (!parameters_.has_value()) {
496 throw std::runtime_error("Collector file does not contain a parameters group");
497 }
498 if (point >= points_) {
499 throw std::runtime_error("Out of bounds parameter point requested");
500 }
501 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
502 throw std::runtime_error("Requested parameter dataset does not exist");
503 }
504 const auto ds = parameters_->getDataSet(name);
505 const auto type = ds.getDataType();
506 if (type != HighFive::AtomicType<char>()) {
507 throw std::runtime_error("Requested parameter dataset does not have type char");
508 }
509 return ds.select({point}, {1}).read<char>();
510 }
511
512 RL_API [[nodiscard]] bool parameter_is_int(const std::string & name) const {
513 for (const auto & parameter_view : parameter_views_) {
514 if (parameter_view.name == name) {
515 return parameter_view.datatype == HighFive::AtomicType<int>();
516 }
517 }
518 return false;
519 }
520 RL_API [[nodiscard]] int parameter_int_value(const std::string & name, const size_t point) const {
521 if (!parameters_.has_value()) {
522 throw std::runtime_error("Collector file does not contain a parameters group");
523 }
524 if (point >= points_) {
525 throw std::runtime_error("Out of bounds parameter point requested");
526 }
527 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
528 throw std::runtime_error("Requested parameter dataset does not exist");
529 }
530 const auto ds = parameters_->getDataSet(name);
531 const auto type = ds.getDataType();
532 if (type != HighFive::AtomicType<int>()) {
533 throw std::runtime_error("Requested parameter dataset does not have type int");
534 }
535 return ds.select({point}, {1}).read<int>();
536 }
537
538 RL_API [[nodiscard]] bool parameter_is_int32(const std::string & name) const {
539 for (const auto & parameter_view : parameter_views_) {
540 if (parameter_view.name == name) {
541 return parameter_view.datatype == HighFive::AtomicType<int32_t>();
542 }
543 }
544 return false;
545 }
546 RL_API [[nodiscard]] int32_t parameter_int32_value(const std::string & name, const size_t point) const {
547 if (!parameters_.has_value()) {
548 throw std::runtime_error("Collector file does not contain a parameters group");
549 }
550 if (point >= points_) {
551 throw std::runtime_error("Out of bounds parameter point requested");
552 }
553 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
554 throw std::runtime_error("Requested parameter dataset does not exist");
555 }
556 const auto ds = parameters_->getDataSet(name);
557 const auto type = ds.getDataType();
558 if (type != HighFive::AtomicType<int32_t>()) {
559 throw std::runtime_error("Requested parameter dataset does not have type int32_t");
560 }
561 return ds.select({point}, {1}).read<int32_t>();
562 }
563
564 RL_API [[nodiscard]] bool parameter_is_int64(const std::string & name) const {
565 for (const auto & parameter_view : parameter_views_) {
566 if (parameter_view.name == name) {
567 return parameter_view.datatype == HighFive::AtomicType<int64_t>();
568 }
569 }
570 return false;
571 }
572 RL_API [[nodiscard]] int64_t parameter_int64_value(const std::string & name, const size_t point) const {
573 if (!parameters_.has_value()) {
574 throw std::runtime_error("Collector file does not contain a parameters group");
575 }
576 if (point >= points_) {
577 throw std::runtime_error("Out of bounds parameter point requested");
578 }
579 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
580 throw std::runtime_error("Requested parameter dataset does not exist");
581 }
582 const auto ds = parameters_->getDataSet(name);
583 const auto type = ds.getDataType();
584 if (type != HighFive::AtomicType<int64_t>()) {
585 throw std::runtime_error("Requested parameter dataset does not have type int64_t");
586 }
587 return ds.select({point}, {1}).read<int64_t>();
588 }
589
590 RL_API [[nodiscard]] bool parameter_is_uint(const std::string & name) const {
591 for (const auto & parameter_view : parameter_views_) {
592 if (parameter_view.name == name) {
593 return parameter_view.datatype == HighFive::AtomicType<unsigned int>();
594 }
595 }
596 return false;
597 }
598 RL_API [[nodiscard]] unsigned int parameter_uint_value(const std::string & name, const size_t point) const {
599 if (!parameters_.has_value()) {
600 throw std::runtime_error("Collector file does not contain a parameters group");
601 }
602 if (point >= points_) {
603 throw std::runtime_error("Out of bounds parameter point requested");
604 }
605 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
606 throw std::runtime_error("Requested parameter dataset does not exist");
607 }
608 const auto ds = parameters_->getDataSet(name);
609 const auto type = ds.getDataType();
610 if (type != HighFive::AtomicType<unsigned int>()) {
611 throw std::runtime_error("Requested parameter dataset does not have type unsigned int");
612 }
613 return ds.select({point}, {1}).read<unsigned int>();
614 }
615
616 RL_API [[nodiscard]] bool parameter_is_uint32(const std::string & name) const {
617 for (const auto & parameter_view : parameter_views_) {
618 if (parameter_view.name == name) {
619 return parameter_view.datatype == HighFive::AtomicType<uint32_t>();
620 }
621 }
622 return false;
623 }
624 RL_API [[nodiscard]] uint32_t parameter_uint32_value(const std::string & name, const size_t point) const {
625 if (!parameters_.has_value()) {
626 throw std::runtime_error("Collector file does not contain a parameters group");
627 }
628 if (point >= points_) {
629 throw std::runtime_error("Out of bounds parameter point requested");
630 }
631 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
632 throw std::runtime_error("Requested parameter dataset does not exist");
633 }
634 const auto ds = parameters_->getDataSet(name);
635 const auto type = ds.getDataType();
636 if (type != HighFive::AtomicType<uint32_t>()) {
637 throw std::runtime_error("Requested parameter dataset does not have type uint32_t");
638 }
639 return ds.select({point}, {1}).read<uint32_t>();
640 }
641
642 RL_API [[nodiscard]] bool parameter_is_uint64(const std::string & name) const {
643 for (const auto & parameter_view : parameter_views_) {
644 if (parameter_view.name == name) {
645 return parameter_view.datatype == HighFive::AtomicType<uint64_t>();
646 }
647 }
648 return false;
649 }
650 RL_API [[nodiscard]] uint64_t parameter_uint64_value(const std::string & name, const size_t point) const {
651 if (!parameters_.has_value()) {
652 throw std::runtime_error("Collector file does not contain a parameters group");
653 }
654 if (point >= points_) {
655 throw std::runtime_error("Out of bounds parameter point requested");
656 }
657 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
658 throw std::runtime_error("Requested parameter dataset does not exist");
659 }
660 const auto ds = parameters_->getDataSet(name);
661 const auto type = ds.getDataType();
662 if (type != HighFive::AtomicType<uint64_t>()) {
663 throw std::runtime_error("Requested parameter dataset does not have type uint64_t");
664 }
665 return ds.select({point}, {1}).read<uint64_t>();
666 }
667
668 RL_API [[nodiscard]] bool parameter_is_string(const std::string & name) const {
669 for (const auto & parameter_view : parameter_views_) {
670 if (parameter_view.name == name) {
671 return parameter_view.datatype.isFixedLenStr() || parameter_view.datatype.isVariableStr();
672 }
673 }
674 return false;
675 }
676 RL_API [[nodiscard]] std::string parameter_string_value(const std::string & name, const size_t point) const {
677 if (!parameters_.has_value()) {
678 throw std::runtime_error("Collector file does not contain a parameters group");
679 }
680 if (point >= points_) {
681 throw std::runtime_error("Out of bounds parameter point requested");
682 }
683 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
684 throw std::runtime_error("Requested parameter dataset does not exist");
685 }
686 const auto ds = parameters_->getDataSet(name);
687 const auto type = ds.getDataType();
688 if (!type.isFixedLenStr() && !type.isVariableStr()) {
689 throw std::runtime_error("Requested parameter dataset does not have a string type");
690 }
691 return ds.select({point}, {1}).read<std::string>();
692 }
693
694};
UDP readout collector class.
UDP readout generator class.
static const std::string & detector_attribute_name()
The detector identity is a GROUP attribute, alongside the EFU routing attributes: together they say w...
Opens one collector file and discovers all collector groups and parameters.
Definition reader.h:297
Typed accessor for one collector group inside a validated collector file.
Definition reader.h:50
RL_API uint64_t point_normalization(const size_t point) const
The accumulated simulated-ray count of one point; grows when files are appended, so stored-weight / n...
Definition reader.h:175
RL_API DetectorType detector_type() const
The detector named by the group's attribute, or Reserved for user-described groups.
Definition reader.h:148
RL_API std::optional< uint16_t > efu_port() const
Returns the EFU UDP port embedded in the file, if any.
Definition reader.h:195
RL_API std::vector< uint8_t > get_raw(const size_t index, const size_t count) const
Raw access to stored records for user-described groups: count records starting at index,...
Definition reader.h:184
RL_API double point_weight(const size_t point) const
The accumulated rate-weight of one point, in stored (ncount-scaled) units: divide by point_normalizat...
Definition reader.h:166
RL_API std::optional< std::string > efu_address() const
Returns the EFU IP address embedded in the file, if any.
Definition reader.h:193
RL_API ReadoutType readout_type() const
The readout type implied by the stored compound datatype.
Definition reader.h:150
RL_API std::optional< ReadoutType > sendable_readout_type() const
The registry readout type whose compound datatype exactly matches the stored records,...
Definition reader.h:156
RL_API const std::optional< std::string > & type_description() const
The original type-description string for user-described groups, if recorded.
Definition reader.h:158
Metadata describing one dataset in the optional root "parameters" group.
Definition reader.h:37