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};
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");
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");
76 RL_API
explicit Reader(std::shared_ptr<HighFive::File> file,
const std::string & collector_name): file_{std::move(file)}, group_name_{collector_name} {
78 throw std::runtime_error(
"Reader requires an open file");
80 if (!file_->exist(group_name_)) {
82 s <<
"Collector group \"" << group_name_ <<
"\" not found in file";
83 throw std::runtime_error(s.str());
85 group_ = file_->getGroup(group_name_);
86 if (
const auto result = validate_collector_group(group_.value()); !result.empty()) {
88 s <<
"Collector group \"" << group_name_ <<
"\" is invalid: " << result;
89 throw std::runtime_error(s.str());
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());
104 if (readouts_->hasAttribute(CollectorSink::parameter_description_attribute_name())) {
105 description_ = readouts_->getAttribute(CollectorSink::parameter_description_attribute_name()).read<std::string>();
109 const auto stored = readouts_->getDataType();
110 for (
const auto rt : {ReadoutType::CAEN, ReadoutType::CDT, ReadoutType::VMM3,
111 ReadoutType::BM0, ReadoutType::BM2, ReadoutType::BMI}) {
112 if (stored == hdf_compound_type(rt)) {
118 if (group_->hasAttribute(CollectorSink::efu_address_attribute_name())) {
119 efu_address_ = group_->getAttribute(CollectorSink::efu_address_attribute_name()).read<std::string>();
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);
128 cues_->read(cue_values_);
129 if (cue_values_.empty()) {
130 throw std::runtime_error(
"Collector cues dataset is empty");
132 if (!std::ranges::is_sorted(cue_values_)) {
133 throw std::runtime_error(
"Collector cues dataset must be monotonically non-decreasing");
135 if (cue_values_.back() != size()) {
136 throw std::runtime_error(
"Collector cues end value does not match readout dataset size");
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");
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); }
151 if (sendable_.has_value()) {
return sendable_.value(); }
152 throw std::runtime_error(
"Collector group has no readout type: it stores user-described records");
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(); }
167 if (point >= cue_values_.size()) {
168 throw std::runtime_error(
"Out of bounds point requested");
170 return weights_->select({point}, {1}).read<double>();
176 if (point >= normalization_values_.size()) {
177 throw std::runtime_error(
"Out of bounds point requested");
179 return normalization_values_[point];
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);
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_; }
197 RL_API [[nodiscard]]
size_t point_offset(
const size_t point)
const {
198 return point_bounds(point).first;
200 RL_API [[nodiscard]]
size_t point_size(
const size_t point)
const {
201 const auto [start, end] = point_bounds(point);
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);
213 RL_API
auto get_VMM3(
const size_t index,
const size_t count)
const{
214 if (sendable_ != ReadoutType::VMM3) {
throw std::runtime_error(
"Non VMM3 readout type"); }
215 if (index >= size() || index + count > size()) {
throw std::runtime_error(
"Out of bounds event requested");}
216 std::vector<VMM3_event> event(count);
217 const auto datatype = readouts_->getDataType();
218 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
221 RL_API
auto get_CDT(
const size_t index,
const size_t count)
const{
222 if (sendable_ != ReadoutType::CDT) {
throw std::runtime_error(
"Non CDT readout type"); }
223 if (index >= size() || index + count > size()) {
throw std::runtime_error(
"Out of bounds event requested");}
224 std::vector<CDT_event> event(count);
225 const auto datatype = readouts_->getDataType();
226 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
229 RL_API
auto get_BM0(
const size_t index,
const size_t count)
const{
230 if (sendable_ != ReadoutType::BM0) {
throw std::runtime_error(
"Non BM0 readout type"); }
231 if (index >= size() || index + count > size()) {
throw std::runtime_error(
"Out of bounds event requested");}
232 std::vector<BM0_event> event(count);
233 const auto datatype = readouts_->getDataType();
234 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
237 RL_API
auto get_BM2(
const size_t index,
const size_t count)
const{
238 if (sendable_ != ReadoutType::BM2) {
throw std::runtime_error(
"Non BM2 readout type"); }
239 if (index >= size() || index + count > size()) {
throw std::runtime_error(
"Out of bounds event requested");}
240 std::vector<BM2_event> event(count);
241 const auto datatype = readouts_->getDataType();
242 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
245 RL_API
auto get_BMI(
const size_t index,
const size_t count)
const {
246 if (sendable_ != ReadoutType::BMI) {
throw std::runtime_error(
"Non BMI readout type"); }
247 if (index >= size() || index + count > size()) {
throw std::runtime_error(
"Out of bounds event requested");}
248 std::vector<BMI_event> event(count);
249 const auto datatype = readouts_->getDataType();
250 readouts_->select({index}, {count}).read_raw(event.data(), datatype);
254 RL_API
auto get_point_CAEN(
const size_t point)
const {
255 const auto [start, end] = point_bounds(point);
256 return get_CAEN(start, end - start);
258 RL_API
auto get_point_VMM3(
const size_t point)
const {
259 const auto [start, end] = point_bounds(point);
260 return get_VMM3(start, end - start);
262 RL_API
auto get_point_CDT(
const size_t point)
const {
263 const auto [start, end] = point_bounds(point);
264 return get_CDT(start, end - start);
266 RL_API
auto get_point_BM0(
const size_t point)
const {
267 const auto [start, end] = point_bounds(point);
268 return get_BM0(start, end - start);
270 RL_API
auto get_point_BM2(
const size_t point)
const {
271 const auto [start, end] = point_bounds(point);
272 return get_BM2(start, end - start);
274 RL_API
auto get_point_BMI(
const size_t point)
const {
275 const auto [start, end] = point_bounds(point);
276 return get_BMI(start, end - start);
286 std::string filename_;
287 std::shared_ptr<HighFive::File> file_;
288 std::optional<HighFive::Group> parameters_;
289 std::vector<Reader> readers_;
290 std::vector<std::string> parameter_names_;
291 std::vector<ParameterDatasetView> parameter_views_;
294 static bool is_parameter_group(
const HighFive::Group & group) {
295 return group.hasAttribute(CollectorSink::type_attribute())
296 && group.getAttribute(CollectorSink::type_attribute()).read<std::string>() == CollectorSink::parameter_group_type();
299 static bool is_collector_group(
const HighFive::Group & group) {
300 return group.hasAttribute(CollectorSink::type_attribute())
301 && group.getAttribute(CollectorSink::type_attribute()).read<std::string>() == CollectorSink::collector_group_type();
307 RL_API
explicit ReaderSource(
const std::string& filename,
const bool warn_build =
true): filename_{filename} {
309 file_ = std::make_shared<HighFive::File>(filename, HighFive::File::ReadOnly);
310 }
catch (HighFive::Exception & ex) {
312 s <<
"Error opening file " << filename <<
":\n" << ex.what();
313 throw std::runtime_error(s.str());
317 if (file_->hasAttribute(CollectorSink::program_attribute_name())) {
318 file_->getAttribute(CollectorSink::program_attribute_name()).read(program);
320 if (program != CollectorSink::program_attribute_value()) {
321 throw std::runtime_error(
"The provided HDF file was not produced using libreadout Collector output");
323 if (!file_->hasAttribute(CollectorSink::version_attribute_name()) || !file_->hasAttribute(CollectorSink::revision_attribute_name())) {
324 throw std::runtime_error(
"Collector file is missing required version attributes");
331 const auto root = file_->getGroup(
"/");
332 std::vector<std::string> collector_names;
333 for (
size_t i=0; i<root.getNumberObjects(); ++i) {
334 const auto name = root.getObjectName(i);
335 if (root.getObjectType(name) != HighFive::ObjectType::Group) {
338 const auto group = root.getGroup(name);
339 if (name == CollectorSink::parameter_group_name() || is_parameter_group(group)) {
341 }
else if (is_collector_group(group)) {
342 collector_names.push_back(name);
346 if (collector_names.empty()) {
347 throw std::runtime_error(
"Collector file has no collector groups. Legacy flat readout files are not supported.");
349 std::ranges::sort(collector_names);
350 readers_.reserve(collector_names.size());
351 for (
const auto & name : collector_names) {
352 readers_.emplace_back(file_, name);
355 points_ = readers_.front().points();
356 for (
const auto & reader : readers_) {
357 if (reader.points() != points_) {
358 throw std::runtime_error(
"Collector groups have inconsistent point counts");
362 if (parameters_.has_value()) {
363 for (
size_t i=0; i<parameters_->getNumberObjects(); ++i) {
364 const auto name = parameters_->getObjectName(i);
365 if (parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
368 const auto ds = parameters_->getDataSet(name);
369 if (
const auto dims = ds.getDimensions(); dims.size() != 1u || dims.back() != points_) {
370 throw std::runtime_error(
"Parameter datasets must be 1-D and match collector point count");
372 parameter_names_.push_back(name);
374 if (ds.hasAttribute(CollectorSink::parameter_unit_attribute_name())) {
375 view.unit = ds.getAttribute(CollectorSink::parameter_unit_attribute_name()).read<std::string>();
377 if (ds.hasAttribute(CollectorSink::parameter_description_attribute_name())) {
378 view.description = ds.getAttribute(CollectorSink::parameter_description_attribute_name()).read<std::string>();
380 parameter_views_.push_back(std::move(view));
382 std::ranges::sort(parameter_names_);
383 std::ranges::sort(parameter_views_, [](
const auto & a,
const auto & b){
return a.name < b.name; });
387 RL_API [[nodiscard]]
size_t points()
const {
return points_; }
388 RL_API [[nodiscard]]
const std::vector<Reader> & readers()
const {
return readers_; }
389 RL_API [[nodiscard]]
bool has_parameters()
const {
return parameters_.has_value(); }
390 RL_API [[nodiscard]]
const std::vector<std::string> & parameter_names()
const {
return parameter_names_; }
391 RL_API [[nodiscard]]
const std::vector<ParameterDatasetView> & parameter_views()
const {
return parameter_views_; }
393 RL_API [[nodiscard]]
const Reader & reader(
const std::string & collector_name)
const {
394 for (
const auto & reader : readers_) {
395 if (reader.collector_name() == collector_name) {
399 throw std::runtime_error(
"Requested collector reader does not exist");
402 RL_API [[nodiscard]] std::vector<uint8_t> parameter_value(
const std::string & name,
const size_t point)
const {
403 if (!parameters_.has_value()) {
404 throw std::runtime_error(
"Collector file does not contain a parameters group");
406 if (point >= points_) {
407 throw std::runtime_error(
"Out of bounds parameter point requested");
409 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
410 throw std::runtime_error(
"Requested parameter dataset does not exist");
412 const auto ds = parameters_->getDataSet(name);
413 const auto type = ds.getDataType();
414 std::vector<uint8_t> buffer(type.getSize());
415 ds.select({point}, {1}).read_raw(buffer.data(), type);
419 RL_API [[nodiscard]]
bool parameter_is_double(
const std::string & name)
const {
420 for (
const auto & parameter_view : parameter_views_) {
421 if (parameter_view.name == name) {
422 return parameter_view.datatype == HighFive::AtomicType<double>();
428 RL_API [[nodiscard]]
double parameter_double_value(
const std::string & name,
const size_t point)
const {
429 if (!parameters_.has_value()) {
430 throw std::runtime_error(
"Collector file does not contain a parameters group");
432 if (point >= points_) {
433 throw std::runtime_error(
"Out of bounds parameter point requested");
435 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
436 throw std::runtime_error(
"Requested parameter dataset does not exist");
438 const auto ds = parameters_->getDataSet(name);
439 const auto type = ds.getDataType();
440 if (type != HighFive::AtomicType<double>()) {
441 throw std::runtime_error(
"Requested parameter dataset does not have type double");
443 return ds.select({point}, {1}).read<double>();
446 RL_API [[nodiscard]]
bool parameter_is_float(
const std::string & name)
const {
447 for (
const auto & parameter_view : parameter_views_) {
448 if (parameter_view.name == name) {
449 return parameter_view.datatype == HighFive::AtomicType<float>();
454 RL_API [[nodiscard]]
float parameter_float_value(
const std::string & name,
const size_t point)
const {
455 if (!parameters_.has_value()) {
456 throw std::runtime_error(
"Collector file does not contain a parameters group");
458 if (point >= points_) {
459 throw std::runtime_error(
"Out of bounds parameter point requested");
461 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
462 throw std::runtime_error(
"Requested parameter dataset does not exist");
464 const auto ds = parameters_->getDataSet(name);
465 const auto type = ds.getDataType();
466 if (type != HighFive::AtomicType<float>()) {
467 throw std::runtime_error(
"Requested parameter dataset does not have type float");
469 return ds.select({point}, {1}).read<float>();
473 RL_API [[nodiscard]]
bool parameter_is_char(
const std::string & name)
const {
474 for (
const auto & parameter_view : parameter_views_) {
475 if (parameter_view.name == name) {
476 return parameter_view.datatype == HighFive::AtomicType<char>();
481 RL_API [[nodiscard]]
char parameter_char_value(
const std::string & name,
const size_t point)
const {
482 if (!parameters_.has_value()) {
483 throw std::runtime_error(
"Collector file does not contain a parameters group");
485 if (point >= points_) {
486 throw std::runtime_error(
"Out of bounds parameter point requested");
488 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
489 throw std::runtime_error(
"Requested parameter dataset does not exist");
491 const auto ds = parameters_->getDataSet(name);
492 const auto type = ds.getDataType();
493 if (type != HighFive::AtomicType<char>()) {
494 throw std::runtime_error(
"Requested parameter dataset does not have type char");
496 return ds.select({point}, {1}).read<char>();
499 RL_API [[nodiscard]]
bool parameter_is_int(
const std::string & name)
const {
500 for (
const auto & parameter_view : parameter_views_) {
501 if (parameter_view.name == name) {
502 return parameter_view.datatype == HighFive::AtomicType<int>();
507 RL_API [[nodiscard]]
int parameter_int_value(
const std::string & name,
const size_t point)
const {
508 if (!parameters_.has_value()) {
509 throw std::runtime_error(
"Collector file does not contain a parameters group");
511 if (point >= points_) {
512 throw std::runtime_error(
"Out of bounds parameter point requested");
514 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
515 throw std::runtime_error(
"Requested parameter dataset does not exist");
517 const auto ds = parameters_->getDataSet(name);
518 const auto type = ds.getDataType();
519 if (type != HighFive::AtomicType<int>()) {
520 throw std::runtime_error(
"Requested parameter dataset does not have type int");
522 return ds.select({point}, {1}).read<int>();
525 RL_API [[nodiscard]]
bool parameter_is_int32(
const std::string & name)
const {
526 for (
const auto & parameter_view : parameter_views_) {
527 if (parameter_view.name == name) {
528 return parameter_view.datatype == HighFive::AtomicType<int32_t>();
533 RL_API [[nodiscard]] int32_t parameter_int32_value(
const std::string & name,
const size_t point)
const {
534 if (!parameters_.has_value()) {
535 throw std::runtime_error(
"Collector file does not contain a parameters group");
537 if (point >= points_) {
538 throw std::runtime_error(
"Out of bounds parameter point requested");
540 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
541 throw std::runtime_error(
"Requested parameter dataset does not exist");
543 const auto ds = parameters_->getDataSet(name);
544 const auto type = ds.getDataType();
545 if (type != HighFive::AtomicType<int32_t>()) {
546 throw std::runtime_error(
"Requested parameter dataset does not have type int32_t");
548 return ds.select({point}, {1}).read<int32_t>();
551 RL_API [[nodiscard]]
bool parameter_is_int64(
const std::string & name)
const {
552 for (
const auto & parameter_view : parameter_views_) {
553 if (parameter_view.name == name) {
554 return parameter_view.datatype == HighFive::AtomicType<int64_t>();
559 RL_API [[nodiscard]] int64_t parameter_int64_value(
const std::string & name,
const size_t point)
const {
560 if (!parameters_.has_value()) {
561 throw std::runtime_error(
"Collector file does not contain a parameters group");
563 if (point >= points_) {
564 throw std::runtime_error(
"Out of bounds parameter point requested");
566 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
567 throw std::runtime_error(
"Requested parameter dataset does not exist");
569 const auto ds = parameters_->getDataSet(name);
570 const auto type = ds.getDataType();
571 if (type != HighFive::AtomicType<int64_t>()) {
572 throw std::runtime_error(
"Requested parameter dataset does not have type int64_t");
574 return ds.select({point}, {1}).read<int64_t>();
577 RL_API [[nodiscard]]
bool parameter_is_uint(
const std::string & name)
const {
578 for (
const auto & parameter_view : parameter_views_) {
579 if (parameter_view.name == name) {
580 return parameter_view.datatype == HighFive::AtomicType<unsigned int>();
585 RL_API [[nodiscard]]
unsigned int parameter_uint_value(
const std::string & name,
const size_t point)
const {
586 if (!parameters_.has_value()) {
587 throw std::runtime_error(
"Collector file does not contain a parameters group");
589 if (point >= points_) {
590 throw std::runtime_error(
"Out of bounds parameter point requested");
592 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
593 throw std::runtime_error(
"Requested parameter dataset does not exist");
595 const auto ds = parameters_->getDataSet(name);
596 const auto type = ds.getDataType();
597 if (type != HighFive::AtomicType<unsigned int>()) {
598 throw std::runtime_error(
"Requested parameter dataset does not have type unsigned int");
600 return ds.select({point}, {1}).read<unsigned int>();
603 RL_API [[nodiscard]]
bool parameter_is_uint32(
const std::string & name)
const {
604 for (
const auto & parameter_view : parameter_views_) {
605 if (parameter_view.name == name) {
606 return parameter_view.datatype == HighFive::AtomicType<uint32_t>();
611 RL_API [[nodiscard]] uint32_t parameter_uint32_value(
const std::string & name,
const size_t point)
const {
612 if (!parameters_.has_value()) {
613 throw std::runtime_error(
"Collector file does not contain a parameters group");
615 if (point >= points_) {
616 throw std::runtime_error(
"Out of bounds parameter point requested");
618 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
619 throw std::runtime_error(
"Requested parameter dataset does not exist");
621 const auto ds = parameters_->getDataSet(name);
622 const auto type = ds.getDataType();
623 if (type != HighFive::AtomicType<uint32_t>()) {
624 throw std::runtime_error(
"Requested parameter dataset does not have type uint32_t");
626 return ds.select({point}, {1}).read<uint32_t>();
629 RL_API [[nodiscard]]
bool parameter_is_uint64(
const std::string & name)
const {
630 for (
const auto & parameter_view : parameter_views_) {
631 if (parameter_view.name == name) {
632 return parameter_view.datatype == HighFive::AtomicType<uint64_t>();
637 RL_API [[nodiscard]] uint64_t parameter_uint64_value(
const std::string & name,
const size_t point)
const {
638 if (!parameters_.has_value()) {
639 throw std::runtime_error(
"Collector file does not contain a parameters group");
641 if (point >= points_) {
642 throw std::runtime_error(
"Out of bounds parameter point requested");
644 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
645 throw std::runtime_error(
"Requested parameter dataset does not exist");
647 const auto ds = parameters_->getDataSet(name);
648 const auto type = ds.getDataType();
649 if (type != HighFive::AtomicType<uint64_t>()) {
650 throw std::runtime_error(
"Requested parameter dataset does not have type uint64_t");
652 return ds.select({point}, {1}).read<uint64_t>();
655 RL_API [[nodiscard]]
bool parameter_is_string(
const std::string & name)
const {
656 for (
const auto & parameter_view : parameter_views_) {
657 if (parameter_view.name == name) {
658 return parameter_view.datatype.isFixedLenStr() || parameter_view.datatype.isVariableStr();
663 RL_API [[nodiscard]] std::string parameter_string_value(
const std::string & name,
const size_t point)
const {
664 if (!parameters_.has_value()) {
665 throw std::runtime_error(
"Collector file does not contain a parameters group");
667 if (point >= points_) {
668 throw std::runtime_error(
"Out of bounds parameter point requested");
670 if (!parameters_->exist(name) || parameters_->getObjectType(name) != HighFive::ObjectType::Dataset) {
671 throw std::runtime_error(
"Requested parameter dataset does not exist");
673 const auto ds = parameters_->getDataSet(name);
674 const auto type = ds.getDataType();
675 if (!type.isFixedLenStr() && !type.isVariableStr()) {
676 throw std::runtime_error(
"Requested parameter dataset does not have a string type");
678 return ds.select({point}, {1}).read<std::string>();