musrfit 1.12.1
PNeXus.cpp
Go to the documentation of this file.
1/***************************************************************************
2
3 PNeXus.cpp
4
5 Author: Andreas Suter
6 e-mail: andreas.suter@psi.ch
7
8***************************************************************************/
9
10/***************************************************************************
11 * Copyright (C) 2007-2026 by Andreas Suter *
12 * andreas.suter@psi.ch *
13 * *
14 * This program is free software; you can redistribute it and/or modify *
15 * it under the terms of the GNU General Public License as published by *
16 * the Free Software Foundation; either version 2 of the License, or *
17 * (at your option) any later version. *
18 * *
19 * This program is distributed in the hope that it will be useful, *
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
22 * GNU General Public License for more details. *
23 * *
24 * You should have received a copy of the GNU General Public License *
25 * along with this program; if not, write to the *
26 * Free Software Foundation, Inc., *
27 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
28 ***************************************************************************/
29
69
70#include <iostream>
71#include <fstream>
72#include <sstream>
73#include <stdexcept>
74#include <cstring>
75#include <cmath>
76#include <chrono>
77#include <ctime>
78#include <iomanip>
79
80#include "Minuit2/MnStrategy.h"
81#include "Minuit2/MnMinimize.h"
82#include "Minuit2/FunctionMinimum.h"
83
84#include "PNeXus.h"
85
86//=============================================================================
87// nxs::checkHDFType - Determine HDF file format from magic bytes
88//=============================================================================
100nxs::HDFType nxs::checkHDFType(const std::string& filename) {
101
102 std::ifstream file(filename, std::ios::binary);
103 if (!file.is_open()) {
104 std::cerr << "Error: Cannot open file '" << filename << "'" << std::endl;
106 }
107
108 // Read first 8 bytes (enough for both signatures)
109 unsigned char header[8];
110 file.read(reinterpret_cast<char*>(header), 8);
111
112 if (file.gcount() < 4) {
113 std::cerr << "**Error**: File too small to be HDF4 or HDF5" << std::endl;
115 }
116
117 // HDF5 signature: 0x89 'H' 'D' 'F' 0x0d 0x0a 0x1a 0x0a
118 const unsigned char hdf5_sig[8] = {0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a};
119
120 // HDF4 signature: 0x0e 0x03 0x13 0x01
121 const unsigned char hdf4_sig[4] = {0x0e, 0x03, 0x13, 0x01};
122
123 // Check HDF5 first (needs 8 bytes)
124 if (file.gcount() >= 8 && std::memcmp(header, hdf5_sig, 8) == 0) {
125 return nxs::HDFType::HDF5;
126 }
127
128 // Check HDF4 (needs 4 bytes)
129 if (std::memcmp(header, hdf4_sig, 4) == 0) {
130 return nxs::HDFType::HDF4;
131 }
132
134}
135
136//=============================================================================
137// nxs::getIso8601TimestampLocal - current time stamp in ISO8601 format
138//=============================================================================
140 auto now = std::chrono::system_clock::now();
141 std::time_t now_c = std::chrono::system_clock::to_time_t(now);
142 std::tm* local_tm = std::localtime(&now_c);
143
144 // Get timezone offset
145 char tz_offset[10];
146 std::strftime(tz_offset, sizeof(tz_offset), "%z", local_tm);
147
148 std::ostringstream oss;
149 oss << std::put_time(local_tm, "%Y-%m-%dT%H:%M:%S");
150
151 // Insert colon in timezone offset: +0100 → +01:00
152 std::string tz_str(tz_offset);
153 tz_str.insert(3, ":");
154 oss << tz_str;
155
156 return oss.str();
157}
158
159#ifdef HAVE_HDF4
160namespace {
171int32 Vgetname_portable(int32 vkey, char *vgname, size_t bufSize)
172{
173#ifdef HDF4_VGETNAME_HAS_BUFSIZE
174 return Vgetname(vkey, vgname, &bufSize);
175#else
176 (void)bufSize;
177 return Vgetname(vkey, vgname);
178#endif
179}
180} // namespace
181
182//=============================================================================
183// nxH4::PNeXusDeadTime Constructor
184//=============================================================================
186{
187 std::map<std::string, std::any> dataMap = nxs->GetDataMap();
188
189 int idfVersion = nxs->GetIdfVersion();
190 if ((idfVersion != 1) && (idfVersion != 2)) {
191 std::stringstream err;
192 err << "**ERROR** Found unsupported IDF version '" << idfVersion << "'";
193 std::cout << err.str() << std::endl;
194 fValid = false;
195 return;
196 }
197
198 if (idfVersion == 1) {
199 // not yet implemented
200 } else { // idfVersion == 2
201 // get counts
202 if (dataMap.find("/raw_data_1/instrument/detector_1/counts") != dataMap.end()) {
203 auto counts_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/counts"]);
204 const auto& counts = counts_data.GetData();
205 fCounts = counts;
206 auto dims = counts_data.GetDimensions();
207 if (fDebug) {
208 std::cout << " counts dimensions: " << dims[0] << " x "
209 << dims[1] << " x " << dims[2] << std::endl;
210 }
211 fDims = dims;
212 } else {
213 std::cout << "**ERROR** Couldn't find 'counts' dataset" << std::endl;
214 fValid = false;
215 return;
216 }
217
218 // get time_resolution
219 if (dataMap.find("/raw_data_1/instrument/detector_1/time_resolution") != dataMap.end()) {
220 auto time_res_data = std::any_cast<PNXdata<float>>(dataMap["/raw_data_1/instrument/detector_1/time_resolution"]);
221 const auto& time_res = time_res_data.GetData();
222 if (time_res.size() > 0) {
223 fTimeResolution = time_res[0];
224 if (fDebug) {
225 std::cout << " time resolution: " << fTimeResolution << " ps" << std::endl;
226 }
227 }
228 } else {
229 std::cout << "**ERROR** Couldn't find 'time_resolution' dataset" << std::endl;
230 fValid = false;
231 return;
232 }
233
234 // get good_frames
235 if (dataMap.find("/raw_data_1/good_frames") != dataMap.end()) {
236 auto good_frames_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/good_frames"]);
237 const auto& good_frames = good_frames_data.GetData();
238 if (good_frames.size() > 0) {
239 fGoodFrames = good_frames[0];
240 if (fDebug) {
241 std::cout << " good frames: " << fGoodFrames << std::endl;
242 }
243 }
244 } else {
245 std::cout << "**ERROR** Couldn't find 'good_frames' dataset" << std::endl;
246 fValid = false;
247 return;
248 }
249
250 // get t0_bin, first_good_bin, last_good_bin
251 if (dataMap.find("/raw_data_1/instrument/detector_1/spectrum_index") != dataMap.end()) {
252 auto spec_idx_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/spectrum_index"]);
253 if (spec_idx_data.HasAttribute("t0_bin")) {
254 try {
255 fT0Bin = std::any_cast<int>(spec_idx_data.GetAttribute("t0_bin"));
256 } catch (...) {
257 std::cout << "**WARNING** Couldn't read t0_bin attribute" << std::endl;
258 }
259 }
260
261 if (spec_idx_data.HasAttribute("first_good_bin")) {
262 try {
263 fFgbBin = std::any_cast<int>(spec_idx_data.GetAttribute("first_good_bin"));
264 } catch (...) {
265 std::cout << "**WARNING** Couldn't read first_good_bin attribute" << std::endl;
266 }
267 }
268
269 if (spec_idx_data.HasAttribute("last_good_bin")) {
270 try {
271 fLgbBin = std::any_cast<int>(spec_idx_data.GetAttribute("last_good_bin"));
272 } catch (...) {
273 std::cout << "**WARNING** Couldn't read last_good_bin attribute" << std::endl;
274 }
275 }
276 }
277
278 if (fDebug) {
279 std::cout << " t0_bin: " << fT0Bin << ", first_good_bin: " << fFgbBin
280 << ", last_good_bin: " << fLgbBin << std::endl;
281 }
282 }
283
284 fValid = true;
285}
286
287//=============================================================================
288// PNeXusDeadTime::operator()
289//=============================================================================
290double nxH4::PNeXusDeadTime::operator()(const std::vector<double> &par) const
291{
292 double chisq = 0.0;
293 double tau = par[0]; // dead time in microseconds
294
295 // Convert time resolution from ps to microseconds
296 double dt = fTimeResolution * 1.0e-6; // ps -> us
297
298 // Calculate chi-square for the specified spectrum
299 int period = 0; // assuming single period
300
301 for (unsigned int bin = fFgbBin; bin <= static_cast<unsigned int>(fLgbBin); bin++) {
302 int idx = period * fDims[1] * fDims[2] + fIdx * fDims[2] + bin;
303 double n_obs = static_cast<double>(fCounts[idx]);
304
305 if (n_obs > 0) {
306 // Dead time correction: n_true = n_obs / (1 - n_obs * tau / (good_frames * dt))
307 double correction = 1.0 - (n_obs * tau) / (fGoodFrames * dt);
308 if (correction > 0) {
309 double n_expected = n_obs / correction;
310 double diff = n_obs - n_expected;
311 chisq += (diff * diff) / n_expected;
312 }
313 }
314 }
315
316 return chisq;
317}
318
319//=============================================================================
320// PNeXusDeadTime::minimize
321//=============================================================================
323{
324 if (i < 0 || i >= static_cast<int>(fDims[1])) {
325 std::cerr << "**ERROR** Invalid spectrum index: " << i << std::endl;
326 return;
327 }
328
329 fIdx = static_cast<unsigned int>(i);
330
331 ROOT::Minuit2::MnUserParameters upar;
332 upar.Add("dead_time", 0.1, 0.01); // initial value, step size
333 upar.SetLimits("dead_time", 0.0, 10.0); // limits in microseconds
334
335 ROOT::Minuit2::MnMinimize minimize(*this, upar);
336 ROOT::Minuit2::FunctionMinimum min = minimize();
337
338 std::cout << "Spectrum " << i << ": ";
339 if (min.IsValid()) {
340 double dt = min.UserState().Value("dead_time");
341 double dt_err = min.UserState().Error("dead_time");
342 std::cout << "Dead time = " << dt << " +/- " << dt_err << " us" << std::endl;
343 } else {
344 std::cout << "Minimization failed" << std::endl;
345 }
346}
347
348//=============================================================================
349// PNeXus Default Constructor
350//=============================================================================
352{
353 uint32 major_v, minor_v, release;
354 char string[256];
355 Hgetlibversion(&major_v, &minor_v, &release, string);
356 fHdf4LibVersion = string;
357}
358
359//=============================================================================
360// PNeXus Constructor with filename
361//=============================================================================
362nxH4::PNeXus::PNeXus(const std::string fln, const bool printDebug)
363 : fPrintDebug(printDebug), fFileName(fln), fSdId(-1), fFileId(-1)
364{
365 uint32 major_v, minor_v, release;
366 char string[256];
367 Hgetlibversion(&major_v, &minor_v, &release, string);
368 fHdf4LibVersion = string;
369
370 if (ReadNexusFile() != 0) {
371 throw std::runtime_error("Failed to read NeXus file: " + fln);
372 }
373}
374
375//=============================================================================
376// PNeXus Destructor
377//=============================================================================
379{
380 if (fSdId != -1) {
381 SDend(fSdId);
382 }
383 if (fFileId != -1) {
384 Hclose(fFileId);
385 }
386}
387
388//=============================================================================
389// PNeXus::ReadNexusFile
390//=============================================================================
392{
393 // Open the HDF4 file
394 fSdId = SDstart(fFileName.c_str(), DFACC_READ);
395 if (fSdId == FAIL) {
396 std::cerr << "**ERROR** Failed to open HDF4 file: " << fFileName << std::endl;
397 return 1;
398 }
399
400 fFileId = Hopen(fFileName.c_str(), DFACC_READ, 0);
401 if (fFileId == FAIL) {
402 std::cerr << "**ERROR** Failed to open HDF4 file (H interface): " << fFileName << std::endl;
403 SDend(fSdId);
404 fSdId = -1;
405 return 1;
406 }
407
408 // Read file-level attributes
409 int32 n_datasets, n_file_attrs;
410 if (SDfileinfo(fSdId, &n_datasets, &n_file_attrs) == FAIL) {
411 std::cerr << "**ERROR** Failed to get file info" << std::endl;
412 return 1;
413 }
414
415 if (fPrintDebug) {
416 std::cout << "Number of datasets: " << n_datasets << std::endl;
417 std::cout << "Number of file attributes: " << n_file_attrs << std::endl;
418 }
419
420 // Read HDF4 version and NeXus version from attributes
421 char attr_name[H4_MAX_NC_NAME];
422 int32 attr_type, attr_count;
423
424 for (int32 i = 0; i < n_file_attrs; i++) {
425 if (SDattrinfo(fSdId, i, attr_name, &attr_type, &attr_count) == FAIL) {
426 continue;
427 }
428
429 if (CaseInsensitiveEquals(attr_name, "HDF_version") ||
430 CaseInsensitiveEquals(attr_name, "HDF4_version")) {
431 std::vector<char> buffer(attr_count + 1, '\0');
432 if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
433 fHdf4Version = std::string(buffer.data());
434 }
435 } else if (CaseInsensitiveEquals(attr_name, "NeXus_version")) {
436 std::vector<char> buffer(attr_count + 1, '\0');
437 if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
438 fNeXusVersion = std::string(buffer.data());
439 }
440 } else if (CaseInsensitiveEquals(attr_name, "file_name")) {
441 std::vector<char> buffer(attr_count + 1, '\0');
442 if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
443 fFileNameNxs = std::string(buffer.data());
444 }
445 } else if (CaseInsensitiveEquals(attr_name, "file_time")) {
446 std::vector<char> buffer(attr_count + 1, '\0');
447 if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
448 fFileTimeNxs = std::string(buffer.data());
449 }
450 }
451 }
452
453 // Determine IDF version
454 // Try to find IDF_version dataset - check multiple possible locations
455
456 // First try simple root-level name
457 int32 idf_idx = SDnametoindex(fSdId, "IDF_version");
458 if (idf_idx != FAIL) {
459 int32 sds_id = SDselect(fSdId, idf_idx);
460 if (sds_id != FAIL) {
461 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
462 char name[H4_MAX_NC_NAME];
463 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
464 if (data_type == DFNT_INT32) {
465 int32 idf_val;
466 int32 start[H4_MAX_VAR_DIMS] = {0};
467 int32 edges[H4_MAX_VAR_DIMS] = {1};
468 if (SDreaddata(sds_id, start, nullptr, edges, &idf_val) != FAIL) {
469 fIdfVersion = idf_val;
470 }
471 }
472 }
473 SDendaccess(sds_id);
474 }
475 }
476
477 // If not found, try /run/IDF_version (IDF version 1 location)
478 if (fIdfVersion == -1) {
479 try {
480 int32 sds_ref = FindDatasetRefByPath("/run/IDF_version");
481 if (sds_ref != -1) {
482 int32 sds_idx = SDreftoindex(fSdId, sds_ref);
483 if (sds_idx != FAIL) {
484 int32 sds_id = SDselect(fSdId, sds_idx);
485 if (sds_id != FAIL) {
486 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
487 char name[H4_MAX_NC_NAME];
488 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
489 if (data_type == DFNT_INT32) {
490 int32 idf_val;
491 int32 start[H4_MAX_VAR_DIMS] = {0};
492 int32 edges[H4_MAX_VAR_DIMS] = {1};
493 if (SDreaddata(sds_id, start, nullptr, edges, &idf_val) != FAIL) {
494 fIdfVersion = idf_val;
495 }
496 }
497 }
498 SDendaccess(sds_id);
499 }
500 }
501 }
502 } catch (...) {
503 // Path not found, continue to next attempt
504 }
505 }
506
507 // If still not found, try /raw_data_1/idf_version (IDF version 2 location)
508 if (fIdfVersion == -1) {
509 try {
510 int32 sds_ref = FindDatasetRefByPath("/raw_data_1/idf_version");
511 if (sds_ref != -1) {
512 int32 sds_idx = SDreftoindex(fSdId, sds_ref);
513 if (sds_idx != FAIL) {
514 int32 sds_id = SDselect(fSdId, sds_idx);
515 if (sds_id != FAIL) {
516 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
517 char name[H4_MAX_NC_NAME];
518 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
519 if (data_type == DFNT_INT32) {
520 int32 idf_val;
521 int32 start[H4_MAX_VAR_DIMS] = {0};
522 int32 edges[H4_MAX_VAR_DIMS] = {1};
523 if (SDreaddata(sds_id, start, nullptr, edges, &idf_val) != FAIL) {
524 fIdfVersion = idf_val;
525 }
526 }
527 }
528 SDendaccess(sds_id);
529 }
530 }
531 }
532 } catch (...) {
533 // Path not found, will use default
534 }
535 }
536
537 if (fIdfVersion == -1) {
538 // Try alternate approach - check for raw_data_1 structure
539 // If we find raw_data_1/detector_1, assume IDF v2
540 // Otherwise assume IDF v1
541 fIdfVersion = 2; // default to version 2
542 }
543
544 if (fPrintDebug) {
545 std::cout << "HDF4 Version: " << fHdf4Version << std::endl;
546 std::cout << "NeXus Version: " << fNeXusVersion << std::endl;
547 std::cout << "IDF Version: " << fIdfVersion << std::endl;
548 }
549
550 // Read datasets based on IDF version
551 if (fIdfVersion == 1) {
553 } else if (fIdfVersion == 2) {
555 } else {
556 std::cerr << "**ERROR** Unsupported IDF version: " << fIdfVersion << std::endl;
557 return 1;
558 }
559
560 return 0;
561}
562
563//=============================================================================
564// nxH4::PNeXus::HandleIdfV1
565//=============================================================================
567{
568 // IDF version 1 handling - not yet implemented
569 if (fPrintDebug) {
570 std::cout << "IDF version 1 handling ..." << std::endl;
571 }
572
573 std::vector<std::string> int_datasets = {
574 "/run/IDF_version",
575 "/run/number",
576 "/run/switching_states",
577 "/run/instrument/detector/number",
578 "/run/instrument/beam/frames_good",
579 "/run/histogram_data_1/counts",
580 "/run/histogram_data_1/resolution",
581 "/run/histogram_data_1/grouping"
582 };
583
584 std::vector<std::string> float_datasets = {
585 "/run/sample/temperature",
586 "/run/sample/magnetic_field",
587 "/run/sample/magnetic_field_vector",
588 "/run/instrument/detector/deadtimes",
589 "/run/histogram_data_1/time_zero",
590 "/run/histogram_data_1/raw_time",
591 "/run/histogram_data_1/corrected_time",
592 "/run/histogram_data_1/alpha"
593 };
594
595 std::vector<std::string> string_datasets = {
596 "/run/program_name",
597 "/run/title",
598 "/run/notes",
599 "/run/analysis",
600 "/run/lab",
601 "/run/beamline",
602 "/run/start_time",
603 "/run/stop_time",
604 "/run/user/name",
605 "/run/user/experiment_number",
606 "/run/sample/name",
607 "/run/sample/magnetic_field_state",
608 "/run/sample/environment",
609 "/run/instrument/name",
610 "/run/instrument/collimator/type"
611 };
612
613 // Read integer datasets
614 for (const auto& path : int_datasets) {
615 try {
616 ReadIntDataset(sd_id, path);
617 } catch (const std::exception& e) {
618 if (fPrintDebug) {
619 std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
620 }
621 }
622 }
623
624 // Read float datasets
625 for (const auto& path : float_datasets) {
626 try {
627 ReadFloatDataset(sd_id, path);
628 } catch (const std::exception& e) {
629 if (fPrintDebug) {
630 std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
631 }
632 }
633 }
634
635 // Read string datasets
636 for (const auto& path : string_datasets) {
637 try {
638 ReadStringDataset(sd_id, path);
639 } catch (const std::exception& e) {
640 if (fPrintDebug) {
641 std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
642 }
643 }
644 }
645}
646
647//=============================================================================
648// nxH4::PNeXus::HandleIdfV2
649//=============================================================================
651{
652 // Read common datasets for IDF version 2
653 // These are typical datasets found in muon NeXus files
654
655 std::vector<std::string> int_datasets = {
656 "/raw_data_1/instrument/detector_1/counts",
657 "/raw_data_1/instrument/detector_1/spectrum_index",
658 "/raw_data_1/good_frames",
659 "/raw_data_1/instrument/detector_1/raw_time",
660 "/raw_data_1/run_number"
661 };
662
663 std::vector<std::string> float_datasets = {
664 "/raw_data_1/instrument/detector_1/resolution",
665 "/raw_data_1/instrument/detector_1/time_zero"
666 };
667
668 std::vector<std::string> string_datasets = {
669 "/raw_data_1/name",
670 "/raw_data_1/title",
671 "/raw_data_1/start_time",
672 "/raw_data_1/end_time"
673 };
674
675 // Read integer datasets
676 for (const auto& path : int_datasets) {
677 try {
678 ReadIntDataset(sd_id, path);
679 } catch (const std::exception& e) {
680 if (fPrintDebug) {
681 std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
682 }
683 }
684 }
685
686 // Read float datasets
687 for (const auto& path : float_datasets) {
688 try {
689 ReadFloatDataset(sd_id, path);
690 } catch (const std::exception& e) {
691 if (fPrintDebug) {
692 std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
693 }
694 }
695 }
696
697 // Read string datasets
698 for (const auto& path : string_datasets) {
699 try {
700 ReadStringDataset(sd_id, path);
701 } catch (const std::exception& e) {
702 if (fPrintDebug) {
703 std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
704 }
705 }
706 }
707}
708
709//=============================================================================
710// nxH4::PNeXus::ReadIntDataset
711//=============================================================================
712void nxH4::PNeXus::ReadIntDataset(int32 sd_id, const std::string& path)
713{
714 // Extract dataset name from path
715 std::vector<std::string> components = SplitPath(path);
716 if (components.empty()) {
717 throw std::runtime_error("Invalid path: " + path);
718 }
719 std::string dataset_name = components.back();
720
721 // Use VGroup navigation to find the correct dataset reference
722 int32 sds_ref = -1;
723 if (components.size() > 1) {
724 // Try to resolve via VGroup hierarchy
725 sds_ref = FindDatasetRefByPath(path);
726 }
727
728 int32 sds_idx = -1;
729 if (sds_ref != -1) {
730 // Find SDS index from reference
731 sds_idx = SDreftoindex(sd_id, sds_ref);
732 }
733
734 // Fallback to name-based search if VGroup navigation failed
735 if (sds_idx == FAIL || sds_idx == -1) {
736 sds_idx = FindDatasetIndex(sd_id, dataset_name);
737 }
738
739 int32 sds_id = SDselect(sd_id, sds_idx);
740 if (sds_id == FAIL) {
741 throw std::runtime_error("Failed to select dataset: " + dataset_name);
742 }
743
744 // Get dataset info
745 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
746 char name[H4_MAX_NC_NAME];
747 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
748 SDendaccess(sds_id);
749 throw std::runtime_error("Failed to get dataset info: " + dataset_name);
750 }
751
752 // Calculate total number of elements
753 int32 n_elements = 1;
754 std::vector<uint32_t> dimensions;
755 for (int32 i = 0; i < rank; i++) {
756 n_elements *= dim_sizes[i];
757 dimensions.push_back(static_cast<uint32_t>(dim_sizes[i]));
758 }
759
760 // Read data
761 std::vector<int32> data(n_elements);
762 int32 start[H4_MAX_VAR_DIMS] = {0};
763 if (SDreaddata(sds_id, start, nullptr, dim_sizes, data.data()) == FAIL) {
764 SDendaccess(sds_id);
765 throw std::runtime_error("Failed to read dataset: " + dataset_name);
766 }
767
768 // Convert to int vector
769 std::vector<int> int_data(data.begin(), data.end());
770
771 // Create PNXdata object
773 pnx_data.SetData(int_data);
774 pnx_data.SetDimensions(dimensions);
775
776 // Read attributes
777 ReadDatasetAttributes(sds_id, pnx_data);
778
779 // Store in data map
780 fDataMap[path] = pnx_data;
781
782 SDendaccess(sds_id);
783}
784
785//=============================================================================
786// nxH4::PNeXus::ReadFloatDataset
787//=============================================================================
788void nxH4::PNeXus::ReadFloatDataset(int32 sd_id, const std::string& path)
789{
790 // Extract dataset name from path
791 std::vector<std::string> components = SplitPath(path);
792 if (components.empty()) {
793 throw std::runtime_error("Invalid path: " + path);
794 }
795 std::string dataset_name = components.back();
796
797 // Use VGroup navigation to find the correct dataset reference
798 int32 sds_ref = -1;
799 if (components.size() > 1) {
800 // Try to resolve via VGroup hierarchy
801 sds_ref = FindDatasetRefByPath(path);
802 }
803
804 int32 sds_idx = -1;
805 if (sds_ref != -1) {
806 // Find SDS index from reference
807 sds_idx = SDreftoindex(sd_id, sds_ref);
808 }
809
810 // Fallback to name-based search if VGroup navigation failed
811 if (sds_idx == FAIL || sds_idx == -1) {
812 sds_idx = FindDatasetIndex(sd_id, dataset_name);
813 }
814
815 int32 sds_id = SDselect(sd_id, sds_idx);
816 if (sds_id == FAIL) {
817 throw std::runtime_error("Failed to select dataset: " + dataset_name);
818 }
819
820 // Get dataset info
821 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
822 char name[H4_MAX_NC_NAME];
823 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
824 SDendaccess(sds_id);
825 throw std::runtime_error("Failed to get dataset info: " + dataset_name);
826 }
827
828 // Calculate total number of elements
829 int32 n_elements = 1;
830 std::vector<uint32_t> dimensions;
831 for (int32 i = 0; i < rank; i++) {
832 n_elements *= dim_sizes[i];
833 dimensions.push_back(static_cast<uint32_t>(dim_sizes[i]));
834 }
835
836 // Read data
837 std::vector<float32> data(n_elements);
838 int32 start[H4_MAX_VAR_DIMS] = {0};
839 if (SDreaddata(sds_id, start, nullptr, dim_sizes, data.data()) == FAIL) {
840 SDendaccess(sds_id);
841 throw std::runtime_error("Failed to read dataset: " + dataset_name);
842 }
843
844 // Convert to float vector
845 std::vector<float> float_data(data.begin(), data.end());
846
847 // Create PNXdata object
849 pnx_data.SetData(float_data);
850 pnx_data.SetDimensions(dimensions);
851
852 // Read attributes
853 ReadDatasetAttributes(sds_id, pnx_data);
854
855 // Store in data map
856 fDataMap[path] = pnx_data;
857
858 SDendaccess(sds_id);
859}
860
861//=============================================================================
862// nxH4::PNeXus::ReadStringDataset
863//=============================================================================
864void nxH4::PNeXus::ReadStringDataset(int32 sd_id, const std::string& path)
865{
866 // Extract dataset name from path
867 std::vector<std::string> components = SplitPath(path);
868 if (components.empty()) {
869 throw std::runtime_error("Invalid path: " + path);
870 }
871 std::string dataset_name = components.back();
872
873 // Use VGroup navigation to find the correct dataset reference
874 int32 sds_ref = -1;
875 if (components.size() > 1) {
876 // Try to resolve via VGroup hierarchy
877 sds_ref = FindDatasetRefByPath(path);
878 }
879
880 int32 sds_idx = -1;
881 if (sds_ref != -1) {
882 // Find SDS index from reference
883 sds_idx = SDreftoindex(sd_id, sds_ref);
884 }
885
886 // Fallback to name-based search if VGroup navigation failed
887 if (sds_idx == FAIL || sds_idx == -1) {
888 sds_idx = FindDatasetIndex(sd_id, dataset_name);
889 }
890
891 int32 sds_id = SDselect(sd_id, sds_idx);
892 if (sds_id == FAIL) {
893 throw std::runtime_error("Failed to select dataset: " + dataset_name);
894 }
895
896 // Get dataset info
897 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
898 char name[H4_MAX_NC_NAME];
899 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
900 SDendaccess(sds_id);
901 throw std::runtime_error("Failed to get dataset info: " + dataset_name);
902 }
903
904 // Calculate total size for string
905 int32 n_elements = 1;
906 std::vector<uint32_t> dimensions;
907 for (int32 i = 0; i < rank; i++) {
908 n_elements *= dim_sizes[i];
909 dimensions.push_back(static_cast<uint32_t>(dim_sizes[i]));
910 }
911
912 // Read data as char array
913 std::vector<char> data(n_elements + 1, '\0');
914 int32 start[H4_MAX_VAR_DIMS] = {0};
915 if (SDreaddata(sds_id, start, nullptr, dim_sizes, data.data()) == FAIL) {
916 SDendaccess(sds_id);
917 throw std::runtime_error("Failed to read dataset: " + dataset_name);
918 }
919
920 // Create string
921 std::vector<std::string> string_data;
922 string_data.push_back(std::string(data.data()));
923
924 // Create PNXdata object
926 pnx_data.SetData(string_data);
927 pnx_data.SetDimensions(dimensions);
928
929 // Read attributes
930 ReadDatasetAttributes(sds_id, pnx_data);
931
932 // Store in data map
933 fDataMap[path] = pnx_data;
934
935 SDendaccess(sds_id);
936}
937
938//=============================================================================
939// nxH4::PNeXus::ReadDatasetAttributes (template specializations)
940//=============================================================================
941template <typename T>
943{
944 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
945 char name[H4_MAX_NC_NAME];
946
947 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
948 return;
949 }
950
951 for (int32 i = 0; i < n_attrs; i++) {
952 char attr_name[H4_MAX_NC_NAME];
953 int32 attr_type, attr_count;
954
955 if (SDattrinfo(sds_id, i, attr_name, &attr_type, &attr_count) == FAIL) {
956 continue;
957 }
958
959 // Read attribute based on type - store as primitives like h5nexus
960 if (attr_type == DFNT_INT32) {
961 if (attr_count == 1) {
962 // Single value - store as int directly
963 int32 value;
964 if (SDreadattr(sds_id, i, &value) != FAIL) {
965 data.AddAttribute(attr_name, static_cast<int>(value));
966 }
967 } else {
968 // Multiple values - store as vector
969 std::vector<int32> attr_data(attr_count);
970 if (SDreadattr(sds_id, i, attr_data.data()) != FAIL) {
971 std::vector<int> int_data(attr_data.begin(), attr_data.end());
972 data.AddAttribute(attr_name, int_data);
973 }
974 }
975 } else if (attr_type == DFNT_FLOAT32) {
976 if (attr_count == 1) {
977 // Single value - store as float directly
978 float32 value;
979 if (SDreadattr(sds_id, i, &value) != FAIL) {
980 data.AddAttribute(attr_name, static_cast<float>(value));
981 }
982 } else {
983 // Multiple values - store as vector
984 std::vector<float32> attr_data(attr_count);
985 if (SDreadattr(sds_id, i, attr_data.data()) != FAIL) {
986 std::vector<float> float_data(attr_data.begin(), attr_data.end());
987 data.AddAttribute(attr_name, float_data);
988 }
989 }
990 } else if (attr_type == DFNT_CHAR8 || attr_type == DFNT_UCHAR8) {
991 // String attributes - always store as string
992 std::vector<char> attr_data(attr_count + 1, '\0');
993 if (SDreadattr(sds_id, i, attr_data.data()) != FAIL) {
994 data.AddAttribute(attr_name, std::string(attr_data.data()));
995 }
996 }
997 }
998}
999
1000//=============================================================================
1001// nxH4::PNeXus::Dump
1002//=============================================================================
1004{
1005 int first_good_bin{0};
1006
1007 if (fIdfVersion == 1) {
1008 std::cout << std::endl;
1009 std::cout << "========================================" << std::endl;
1010 std::cout << "NeXus File Dump" << std::endl;
1011 std::cout << "========================================" << std::endl;
1012 std::cout << "Filename: " << fFileName << std::endl;
1013 std::cout << "HDF4 Version: " << fHdf4Version << std::endl;
1014 std::cout << "NeXus Version: " << fNeXusVersion << std::endl;
1015 std::cout << "IDF Version: " << fIdfVersion << std::endl;
1016 std::cout << "----------------------------------------" << std::endl;
1017
1018 std::cout << std::endl << "++++";
1019 std::cout << std::endl << "run";
1020 std::cout << std::endl << "----";
1021 std::cout << std::endl << " IDF_version: " << fIdfVersion;
1022 if (fDataMap.find("/run/program_name") != fDataMap.end()) {
1023 try {
1024 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/program_name"]);
1025 std::cout << std::endl << " program_name : " << str_data.GetData()[0];
1026
1027 // Check for attributes
1028 if (str_data.HasAttribute("version")) {
1029 try {
1030 auto version = std::any_cast<std::string>(str_data.GetAttribute("version"));
1031 std::cout << " version : " << version;
1032 } catch (const std::bad_any_cast& e) {
1033 std::cerr << std::endl << "**ERROR**: Failed to cast version attribute" << std::endl;
1034 }
1035 }
1036 } catch (const std::bad_any_cast& e) {
1037 std::cerr << std::endl << "**ERROR**: Failed to cast program_name data" << std::endl;
1038 }
1039 } else {
1040 std::cout << std::endl << " program_name : n/a";
1041 }
1042
1043 if (fDataMap.find("/run/number") != fDataMap.end()) {
1044 try {
1045 auto number = std::any_cast<PNXdata<int>>(fDataMap["/run/number"]);
1046 std::cout << std::endl << " number : " << number.GetData()[0];
1047 } catch (const std::bad_any_cast& e) {
1048 std::cerr << std::endl << "**ERROR**: Failed to cast number data" << std::endl;
1049 }
1050 } else {
1051 std::cout << std::endl << " number : n/a";
1052 }
1053
1054 if (fDataMap.find("/run/title") != fDataMap.end()) {
1055 try {
1056 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/title"]);
1057 std::cout << std::endl << " title : " << str_data.GetData()[0];
1058 } catch (const std::bad_any_cast& e) {
1059 std::cerr << std::endl << "**ERROR**: Failed to cast title data" << std::endl;
1060 }
1061 } else {
1062 std::cout << std::endl << " title : n/a";
1063 }
1064
1065 if (fDataMap.find("/run/notes") != fDataMap.end()) {
1066 try {
1067 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/notes"]);
1068 std::cout << std::endl << " notes : " << str_data.GetData()[0];
1069 } catch (const std::bad_any_cast& e) {
1070 std::cerr << std::endl << "**ERROR**: Failed to cast notes data" << std::endl;
1071 }
1072 } else {
1073 std::cout << std::endl << " notes : n/a";
1074 }
1075
1076 if (fDataMap.find("/run/analysis") != fDataMap.end()) {
1077 try {
1078 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/analysis"]);
1079 std::cout << std::endl << " analysis : " << str_data.GetData()[0];
1080 } catch (const std::bad_any_cast& e) {
1081 std::cerr << std::endl << "**ERROR**: Failed to cast analysis data" << std::endl;
1082 }
1083 } else {
1084 std::cout << std::endl << " analysis : n/a";
1085 }
1086
1087 if (fDataMap.find("/run/lab") != fDataMap.end()) {
1088 try {
1089 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/lab"]);
1090 std::cout << std::endl << " lab : " << str_data.GetData()[0];
1091 } catch (const std::bad_any_cast& e) {
1092 std::cerr << std::endl << "**ERROR**: Failed to cast lab data" << std::endl;
1093 }
1094 } else {
1095 std::cout << std::endl << " lab : n/a";
1096 }
1097
1098 if (fDataMap.find("/run/beamline") != fDataMap.end()) {
1099 try {
1100 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/beamline"]);
1101 std::cout << std::endl << " beamline : " << str_data.GetData()[0];
1102 } catch (const std::bad_any_cast& e) {
1103 std::cerr << std::endl << "**ERROR**: Failed to cast beamline data" << std::endl;
1104 }
1105 } else {
1106 std::cout << std::endl << " beamline : n/a";
1107 }
1108
1109 if (fDataMap.find("/run/start_time") != fDataMap.end()) {
1110 try {
1111 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/start_time"]);
1112 std::cout << std::endl << " start_time : " << str_data.GetData()[0];
1113 } catch (const std::bad_any_cast& e) {
1114 std::cerr << std::endl << "**ERROR**: Failed to cast start_time data" << std::endl;
1115 }
1116 } else {
1117 std::cout << std::endl << " start_time : n/a";
1118 }
1119
1120 if (fDataMap.find("/run/stop_time") != fDataMap.end()) {
1121 try {
1122 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/stop_time"]);
1123 std::cout << std::endl << " stop_time : " << str_data.GetData()[0];
1124 } catch (const std::bad_any_cast& e) {
1125 std::cerr << std::endl << "**ERROR**: Failed to cast stop_time data" << std::endl;
1126 }
1127 } else {
1128 std::cout << std::endl << " stop_time : n/a";
1129 }
1130
1131 if (fDataMap.find("/run/switching_state") != fDataMap.end()) {
1132 try {
1133 auto int_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/switching_state"]);
1134 std::cout << std::endl << " switching_state : " << int_data.GetData()[0];
1135 } catch (const std::bad_any_cast& e) {
1136 std::cerr << std::endl << "**ERROR**: Failed to cast switching_state data" << std::endl;
1137 }
1138 } else {
1139 std::cout << std::endl << " switching_state : n/a";
1140 }
1141
1142 std::cout << std::endl << "----";
1143 std::cout << std::endl << " user";
1144 std::cout << std::endl << "----";
1145 if (fDataMap.find("/run/user/name") != fDataMap.end()) {
1146 try {
1147 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/name"]);
1148 std::cout << std::endl << " name : '" << str_data.GetData()[0] << "'";
1149 } catch (const std::bad_any_cast& e) {
1150 std::cerr << std::endl << "**ERROR**: Failed to cast name data" << std::endl;
1151 }
1152 } else {
1153 std::cout << std::endl << " name : n/a";
1154 }
1155
1156 if (fDataMap.find("/run/user/experiment_number") != fDataMap.end()) {
1157 try {
1158 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/experiment_number"]);
1159 std::cout << std::endl << " experiment_number : " << str_data.GetData()[0];
1160 } catch (const std::bad_any_cast& e) {
1161 std::cerr << std::endl << "**ERROR**: Failed to cast experiment_number data" << std::endl;
1162 }
1163 } else {
1164 std::cout << std::endl << " experiment_number : n/a";
1165 }
1166
1167 std::cout << std::endl << "----";
1168 std::cout << std::endl << " sample";
1169 std::cout << std::endl << "----";
1170 if (fDataMap.find("/run/sample/name") != fDataMap.end()) {
1171 try {
1172 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/name"]);
1173 std::cout << std::endl << " name : '" << str_data.GetData()[0] << "'";
1174 } catch (const std::bad_any_cast& e) {
1175 std::cerr << std::endl << "**ERROR**: Failed to cast name data" << std::endl;
1176 }
1177 } else {
1178 std::cout << std::endl << " name : n/a";
1179 }
1180
1181 if (fDataMap.find("/run/sample/temperature") != fDataMap.end()) {
1182 try {
1183 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/temperature"]);
1184 std::cout << std::endl << " temperature : " << float_data.GetData()[0];
1185
1186 // Check for attributes
1187 if (float_data.HasAttribute("units")) {
1188 try {
1189 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
1190 std::cout << " units : " << units;
1191 } catch (const std::bad_any_cast& e) {
1192 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1193 }
1194 } else {
1195 std::cout << " units : n/a";
1196 }
1197 } catch (const std::bad_any_cast& e) {
1198 std::cerr << std::endl << "**ERROR**: Failed to cast temperature data" << std::endl;
1199 }
1200 } else {
1201 std::cout << std::endl << " temperature : n/a";
1202 }
1203
1204 if (fDataMap.find("/run/sample/magnetic_field") != fDataMap.end()) {
1205 try {
1206 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field"]);
1207 std::cout << std::endl << " magnetic_field : " << float_data.GetData()[0];
1208
1209 // Check for attributes
1210 if (float_data.HasAttribute("units")) {
1211 try {
1212 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
1213 std::cout << " units : " << units;
1214 } catch (const std::bad_any_cast& e) {
1215 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1216 }
1217 } else {
1218 std::cout << " units : n/a";
1219 }
1220 } catch (const std::bad_any_cast& e) {
1221 std::cerr << std::endl << "**ERROR**: Failed to cast magnetic_field data" << std::endl;
1222 }
1223 } else {
1224 std::cout << std::endl << " magnetic_field : n/a";
1225 }
1226
1227 if (fDataMap.find("/run/sample/magnetic_field_vector") != fDataMap.end()) {
1228 try {
1229 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field_vector"]);
1230 std::cout << std::endl << " magnetic_field_vector : " << float_data.GetData()[0];
1231
1232 // Check for attributes
1233 if (float_data.HasAttribute("coordinate_system")) {
1234 try {
1235 auto coordinate_system = std::any_cast<std::string>(float_data.GetAttribute("coordinate_system"));
1236 std::cout << std::endl << " coordinate_system : " << coordinate_system << std::endl;
1237 } catch (const std::bad_any_cast& e) {
1238 std::cerr << "**ERROR**: Failed to cast coordinate_system attribute" << std::endl;
1239 }
1240 } else {
1241 std::cout << std::endl << " coordinate_system : n/a" << std::endl;
1242 }
1243
1244 if (float_data.HasAttribute("units")) {
1245 try {
1246 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
1247 std::cout << " units : " << units << std::endl;
1248 } catch (const std::bad_any_cast& e) {
1249 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1250 }
1251 } else {
1252 std::cout << " units : n/a" << std::endl;
1253 }
1254
1255 if (float_data.HasAttribute("available")) {
1256 try {
1257 auto available = std::any_cast<int>(float_data.GetAttribute("available"));
1258 std::cout << " available : " << available;
1259 } catch (const std::bad_any_cast& e) {
1260 std::cerr << "**ERROR**: Failed to cast available attribute" << std::endl;
1261 }
1262 } else {
1263 std::cout << " available : n/a";
1264 }
1265 } catch (const std::bad_any_cast& e) {
1266 std::cerr << std::endl << "**ERROR**: Failed to cast magnetic_field_vector data" << std::endl;
1267 }
1268 } else {
1269 std::cout << std::endl << " magnetic_field_vector : n/a";
1270 }
1271
1272 if (fDataMap.find("/run/sample/environment") != fDataMap.end()) {
1273 try {
1274 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/environment"]);
1275 std::cout << std::endl << " environment : " << str_data.GetData()[0];
1276 } catch (const std::bad_any_cast& e) {
1277 std::cerr << std::endl << "**ERROR**: Failed to cast environment data" << std::endl;
1278 }
1279 } else {
1280 std::cout << std::endl << " environment : n/a";
1281 }
1282
1283 std::cout << std::endl << "----";
1284 std::cout << std::endl << " instrument";
1285 std::cout << std::endl << "----";
1286 if (fDataMap.find("/run/instrument/name") != fDataMap.end()) {
1287 try {
1288 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/name"]);
1289 std::cout << std::endl << " name : '" << str_data.GetData()[0] << "'";
1290 } catch (const std::bad_any_cast& e) {
1291 std::cerr << std::endl << "**ERROR**: Failed to cast name data" << std::endl;
1292 }
1293 } else {
1294 std::cout << std::endl << " name : n/a";
1295 }
1296
1297 std::cout << std::endl << "----";
1298 std::cout << std::endl << " detector";
1299 std::cout << std::endl << "----";
1300 if (fDataMap.find("/run/instrument/detector/number") != fDataMap.end()) {
1301 try {
1302 auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/instrument/detector/number"]);
1303 std::cout << std::endl << " number : " << int_data.GetData()[0];
1304 } catch (const std::bad_any_cast& e) {
1305 std::cerr << std::endl << "**ERROR**: Failed to cast number data" << std::endl;
1306 }
1307 } else {
1308 std::cout << std::endl << " number : n/a";
1309 }
1310
1311 if (fDataMap.find("/run/instrument/detector/deadtimes") != fDataMap.end()) {
1312 try {
1313 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/instrument/detector/deadtimes"]);
1314 auto data = float_data.GetData();
1315 unsigned int end{15};
1316 if (data.size() < end)
1317 end = data.size();
1318 std::cout << std::endl << " deadtimes: ";
1319 for (unsigned int i=0; i<end; i++)
1320 std::cout << data[i] << ", ";
1321 std::cout << "...";
1322 } catch (const std::bad_any_cast& e) {
1323 std::cerr << std::endl << "**ERROR**: Failed to cast deadtimes data" << std::endl;
1324 }
1325 } else {
1326 std::cout << std::endl << " deadtimes: n/a";
1327 }
1328
1329 std::cout << std::endl << "----";
1330 std::cout << std::endl << " beam";
1331 std::cout << std::endl << "----";
1332 if (fDataMap.find("/run/instrument/beam/frames_good") != fDataMap.end()) {
1333 try {
1334 auto fg = std::any_cast<PNXdata<int>>(fDataMap["/run/instrument/beam/frames_good"]);
1335 std::cout << std::endl << " good_frames : " << fg.GetData()[0];
1336 } catch (const std::bad_any_cast& e) {
1337 std::cerr << std::endl << "**ERROR**: Failed to cast good_frames data" << std::endl;
1338 }
1339 } else {
1340 std::cout << std::endl << " good_frames : n/a";
1341 }
1342
1343 std::cout << std::endl << "----";
1344 std::cout << std::endl << " collimator";
1345 std::cout << std::endl << "----";
1346 if (fDataMap.find("/run/instrument/collimator/type") != fDataMap.end()) {
1347 try {
1348 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/collimator/type"]);
1349 std::cout << std::endl << " type : " << str_data.GetData()[0];
1350 } catch (const std::bad_any_cast& e) {
1351 std::cerr << std::endl << "**ERROR**: Failed to cast type data" << std::endl;
1352 }
1353 } else {
1354 std::cout << std::endl << " type : n/a";
1355 }
1356
1357 std::cout << std::endl << "----";
1358 std::cout << std::endl << " histogram_data_1";
1359 std::cout << std::endl << "----";
1360 std::cout << std::endl << " counts:";
1361 std::cout << std::endl;
1362 try {
1363 auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/counts"]);
1364
1365 // Check for attributes
1366 if (counts_data.HasAttribute("units")) {
1367 try {
1368 auto units = std::any_cast<std::string>(counts_data.GetAttribute("units"));
1369 std::cout << " units : " << units << std::endl;
1370 } catch (const std::bad_any_cast& e) {
1371 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1372 }
1373 } else {
1374 std::cout << " units : n/a" << std::endl;
1375 }
1376
1377 if (counts_data.HasAttribute("signal")) {
1378 try {
1379 auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
1380 std::cout << " signal : " << signal << std::endl;
1381 } catch (const std::bad_any_cast& e) {
1382 std::cerr << "**ERROR**: Failed to cast signal attribute" << std::endl;
1383 }
1384 } else {
1385 std::cout << " signal : n/a" << std::endl;
1386 }
1387
1388 int noOfHistos{0};
1389 if (counts_data.HasAttribute("number")) {
1390 try {
1391 noOfHistos = std::any_cast<int>(counts_data.GetAttribute("number"));
1392 std::cout << " number : " << noOfHistos << std::endl;
1393 } catch (const std::bad_any_cast& e) {
1394 std::cerr << "**ERROR**: Failed to cast number attribute" << std::endl;
1395 }
1396 } else {
1397 std::cout << " number : n/a" << std::endl;
1398 }
1399
1400 int histoLength{0};
1401 if (counts_data.HasAttribute("length")) {
1402 try {
1403 histoLength = std::any_cast<int>(counts_data.GetAttribute("length"));
1404 std::cout << " length : " << histoLength << std::endl;
1405 } catch (const std::bad_any_cast& e) {
1406 std::cerr << "**ERROR**: Failed to cast length attribute" << std::endl;
1407 }
1408 } else {
1409 std::cout << " length : n/a" << std::endl;
1410 }
1411
1412 if (counts_data.HasAttribute("t0_bin")) {
1413 try {
1414 auto t0_bin = std::any_cast<int>(counts_data.GetAttribute("t0_bin"));
1415 std::cout << " t0_bin : " << t0_bin << std::endl;
1416 } catch (const std::bad_any_cast& e) {
1417 std::cerr << "**ERROR**: Failed to cast t0_bin attribute" << std::endl;
1418 }
1419 } else {
1420 std::cout << " t0_bin : n/a" << std::endl;
1421 }
1422
1423 if (counts_data.HasAttribute("first_good_bin")) {
1424 try {
1425 first_good_bin = std::any_cast<int>(counts_data.GetAttribute("first_good_bin"));
1426 std::cout << " first_good_bin : " << first_good_bin << std::endl;
1427 } catch (const std::bad_any_cast& e) {
1428 std::cerr << "**ERROR**: Failed to cast first_good_bin attribute" << std::endl;
1429 }
1430 } else {
1431 std::cout << " first_good_bin : n/a" << std::endl;
1432 }
1433
1434 if (counts_data.HasAttribute("last_good_bin")) {
1435 try {
1436 auto last_good_bin = std::any_cast<int>(counts_data.GetAttribute("last_good_bin"));
1437 std::cout << " last_good_bin : " << last_good_bin << std::endl;
1438 } catch (const std::bad_any_cast& e) {
1439 std::cerr << "**ERROR**: Failed to cast last_good_bin attribute" << std::endl;
1440 }
1441 } else {
1442 std::cout << " last_good_bin : n/a" << std::endl;
1443 }
1444
1445 if (counts_data.HasAttribute("offset")) {
1446 try {
1447 auto offset = std::any_cast<float>(counts_data.GetAttribute("offset"));
1448 std::cout << " offset : " << offset << std::endl;
1449 } catch (const std::bad_any_cast& e) {
1450 std::cerr << "**ERROR**: Failed to cast offset attribute" << std::endl;
1451 }
1452 } else {
1453 std::cout << " offset : n/a" << std::endl;
1454 }
1455
1456 // dump the first couple of counts of each detector
1457 const auto& data = counts_data.GetData();
1458 std::cout << std::endl << " first couple of counts of each detector:";
1459 for (unsigned int i=0; i<noOfHistos; i++) {
1460 if (i<9)
1461 std::cout << std::endl << " " << i+1 << ": ";
1462 else
1463 std::cout << std::endl << " " << i+1 << ": ";
1464 for (unsigned int j=0; j<first_good_bin+5; j++)
1465 std::cout << data[i*histoLength+j] << ", ";
1466 std::cout << "...";
1467 }
1468 } catch (const std::bad_any_cast& e) {
1469 std::cerr << "**ERROR**: Failed to cast counts data" << std::endl;
1470 }
1471
1472 if (fDataMap.find("/run/histogram_data_1/resolution") != fDataMap.end()) {
1473 try {
1474 auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/resolution"]);
1475 std::cout << std::endl << " resolution : " << int_data.GetData()[0];
1476
1477 if (int_data.HasAttribute("units")) {
1478 try {
1479 auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
1480 std::cout << " units : " << units;
1481 } catch (const std::bad_any_cast& e) {
1482 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1483 }
1484 } else {
1485 std::cout << std::endl << " units : n/a";
1486 }
1487 } catch (const std::bad_any_cast& e) {
1488 std::cerr << std::endl << "**ERROR**: Failed to cast resolution data" << std::endl;
1489 }
1490 } else {
1491 std::cout << std::endl << " resolution : n/a";
1492 }
1493
1494 if (fDataMap.find("/run/histogram_data_1/time_zero") != fDataMap.end()) {
1495 try {
1496 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/time_zero"]);
1497 std::cout << std::endl << " time_zero : " << float_data.GetData()[0];
1498
1499 if (float_data.HasAttribute("units")) {
1500 try {
1501 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
1502 std::cout << std::endl << " units : " << units;
1503 } catch (const std::bad_any_cast& e) {
1504 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1505 }
1506 } else {
1507 std::cout << std::endl << " units : n/a";
1508 }
1509
1510 if (float_data.HasAttribute("available")) {
1511 try {
1512 auto available = std::any_cast<int>(float_data.GetAttribute("available"));
1513 std::cout << std::endl << " available : " << available;
1514 } catch (const std::bad_any_cast& e) {
1515 std::cerr << "**ERROR**: Failed to cast available attribute";
1516 }
1517 } else {
1518 std::cout << std::endl << " available : n/a";
1519 }
1520 } catch (const std::bad_any_cast& e) {
1521 std::cerr << std::endl << "**ERROR**: Failed to cast time_zero data" << std::endl;
1522 }
1523 } else {
1524 std::cout << std::endl << " time_zero : n/a";
1525 }
1526
1527 if (fDataMap.find("/run/histogram_data_1/raw_time") != fDataMap.end()) {
1528 try {
1529 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/raw_time"]);
1530 std::cout << std::endl << " raw_time : " << float_data.GetData()[0];
1531 if (float_data.HasAttribute("axis")) {
1532 try {
1533 auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
1534 std::cout << std::endl << " axis : " << axis;
1535 } catch (const std::bad_any_cast& e) {
1536 std::cerr << "**ERROR**: Failed to cast axis attribute" << std::endl;
1537 }
1538 } else {
1539 std::cout << std::endl << " axis : n/a";
1540 }
1541
1542 if (float_data.HasAttribute("primary")) {
1543 try {
1544 auto primary = std::any_cast<int>(float_data.GetAttribute("primary"));
1545 std::cout << std::endl << " primary : " << primary;
1546 } catch (const std::bad_any_cast& e) {
1547 std::cerr << "**ERROR**: Failed to cast primary attribute" << std::endl;
1548 }
1549 } else {
1550 std::cout << std::endl << " primary : n/a";
1551 }
1552
1553 if (float_data.HasAttribute("units")) {
1554 try {
1555 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
1556 std::cout << std::endl << " units : " << units;
1557 } catch (const std::bad_any_cast& e) {
1558 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1559 }
1560 } else {
1561 std::cout << std::endl << " units : n/a";
1562 }
1563 } catch (const std::bad_any_cast& e) {
1564 std::cerr << std::endl << "**ERROR**: Failed to cast raw_time data" << std::endl;
1565 }
1566 } else {
1567 std::cout << std::endl << " raw_time : n/a";
1568 }
1569
1570 if (fDataMap.find("/run/histogram_data_1/corrected_time") != fDataMap.end()) {
1571 try {
1572 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/corrected_time"]);
1573 std::cout << std::endl << " corrected_time : " << float_data.GetData()[0];
1574 if (float_data.HasAttribute("axis")) {
1575 try {
1576 auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
1577 std::cout << std::endl << " axis : " << axis;
1578 } catch (const std::bad_any_cast& e) {
1579 std::cerr << "**ERROR**: Failed to cast axis attribute" << std::endl;
1580 }
1581 } else {
1582 std::cout << std::endl << " axis : n/a";
1583 }
1584
1585 if (float_data.HasAttribute("units")) {
1586 try {
1587 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
1588 std::cout << std::endl << " units : " << units;
1589 } catch (const std::bad_any_cast& e) {
1590 std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
1591 }
1592 } else {
1593 std::cout << std::endl << " units : n/a";
1594 }
1595 } catch (const std::bad_any_cast& e) {
1596 std::cerr << std::endl << "**ERROR**: Failed to cast raw_time data" << std::endl;
1597 }
1598 } else {
1599 std::cout << std::endl << " corrected_time : n/a";
1600 }
1601
1602 if (fDataMap.find("/run/histogram_data_1/grouping") != fDataMap.end()) {
1603 try {
1604 auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/grouping"]);
1605 auto data = int_data.GetData();
1606 unsigned int end{15};
1607 if (data.size() < end)
1608 end = data.size();
1609 std::cout << std::endl << " grouping : ";
1610 for (unsigned int i=0; i<end; i++)
1611 std::cout << data[i] << ", ";
1612 std::cout << "...";
1613 if (int_data.HasAttribute("available")) {
1614 try {
1615 auto available = std::any_cast<int>(int_data.GetAttribute("available"));
1616 std::cout << std::endl << " available : " << available;
1617 } catch (const std::bad_any_cast& e) {
1618 std::cerr << "**ERROR**: Failed to cast available attribute" << std::endl;
1619 }
1620 } else {
1621 std::cout << std::endl << " available : n/a";
1622 }
1623 } catch (const std::bad_any_cast& e) {
1624 std::cerr << std::endl << "**ERROR**: Failed to cast grouping data" << std::endl;
1625 }
1626 } else {
1627 std::cout << std::endl << " grouping : n/a";
1628 }
1629
1630 if (fDataMap.find("/run/histogram_data_1/alpha") != fDataMap.end()) {
1631 try {
1632 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/alpha"]);
1633 auto data = float_data.GetData();
1634 unsigned int end{15};
1635 if (data.size() < end)
1636 end = data.size();
1637 std::cout << std::endl << " alpha : ";
1638 for (unsigned int i=0; i<end; i++)
1639 std::cout << data[i] << ", ";
1640 std::cout << "...";
1641 if (float_data.HasAttribute("available")) {
1642 try {
1643 auto available = std::any_cast<int>(float_data.GetAttribute("available"));
1644 std::cout << std::endl << " available : " << available;
1645 } catch (const std::bad_any_cast& e) {
1646 std::cerr << "**ERROR**: Failed to cast available attribute" << std::endl;
1647 }
1648 } else {
1649 std::cout << std::endl << " alpha : n/a";
1650 }
1651 } catch (const std::bad_any_cast& e) {
1652 std::cerr << std::endl << "**ERROR**: Failed to cast alpha data" << std::endl;
1653 }
1654 } else {
1655 std::cout << std::endl << " available : n/a";
1656 }
1657
1658 std::cout << std::endl;
1659 std::cout << "========================================" << std::endl;
1660 std::cout << std::endl;
1661 } else { // IDF Version 2
1662 std::cout << std::endl;
1663 std::cout << std::endl << "hdf5-NeXus file content of file:' " << fFileName << "'";
1664 std::cout << std::endl << "****";
1665 std::cout << std::endl << "Top Level Attributes:";
1666 std::cout << std::endl << " HDF4 Version : " << fHdf4Version;
1667 std::cout << std::endl << " NeXus Version: " << fNeXusVersion;
1668 std::cout << std::endl << " file_name : " << fFileNameNxs;
1669 std::cout << std::endl << " file_time : " << fFileTimeNxs;
1670 std::cout << std::endl << "++++";
1671 std::cout << std::endl << "raw_data_1";
1672 std::cout << std::endl << "----";
1673 std::cout << std::endl << " IDF_version: " << fIdfVersion;
1674 if (fDataMap.find("/raw_data_1/beamline") != fDataMap.end()) {
1675 try {
1676 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/beamline"]);
1677 std::cout << std::endl << " beamline : " << str.GetData()[0];
1678 } catch (const std::bad_any_cast& e) {
1679 std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
1680 }
1681 } else {
1682 std::cout << std::endl << " beamline : n/a";
1683 }
1684
1685 if (fDataMap.find("/raw_data_1/definition") != fDataMap.end()) {
1686 try {
1687 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/definition"]);
1688 std::cout << std::endl << " definition : " << str.GetData()[0];
1689 } catch (const std::bad_any_cast& e) {
1690 std::cerr << std::endl << "Error: Failed to cast definition data" << std::endl;
1691 }
1692 } else {
1693 std::cout << std::endl << " definition : n/a";
1694 }
1695
1696 if (fDataMap.find("/raw_data_1/run_number") != fDataMap.end()) {
1697 try {
1698 auto str = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/run_number"]);
1699 std::cout << std::endl << " run_number : " << str.GetData()[0];
1700 } catch (const std::bad_any_cast& e) {
1701 std::cerr << std::endl << "Error: Failed to cast run_number data" << std::endl;
1702 }
1703 } else {
1704 std::cout << std::endl << " run_number : n/a";
1705 }
1706
1707 if (fDataMap.find("/raw_data_1/title") != fDataMap.end()) {
1708 try {
1709 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/title"]);
1710 std::cout << std::endl << " title : " << str.GetData()[0];
1711 } catch (const std::bad_any_cast& e) {
1712 std::cerr << std::endl << "Error: Failed to cast title data" << std::endl;
1713 }
1714 } else {
1715 std::cout << std::endl << " title : n/a";
1716 }
1717
1718 if (fDataMap.find("/raw_data_1/start_time") != fDataMap.end()) {
1719 try {
1720 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/start_time"]);
1721 std::cout << std::endl << " start_time : " << str.GetData()[0];
1722 } catch (const std::bad_any_cast& e) {
1723 std::cerr << std::endl << "Error: Failed to cast start_time data" << std::endl;
1724 }
1725 } else {
1726 std::cout << std::endl << " start_time : n/a";
1727 }
1728
1729 if (fDataMap.find("/raw_data_1/end_time") != fDataMap.end()) {
1730 try {
1731 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/end_time"]);
1732 std::cout << std::endl << " end_time : " << str.GetData()[0];
1733 } catch (const std::bad_any_cast& e) {
1734 std::cerr << std::endl << "Error: Failed to cast end_time data" << std::endl;
1735 }
1736 } else {
1737 std::cout << std::endl << " end_time : n/a";
1738 }
1739
1740 if (fDataMap.find("/raw_data_1/good_frames") != fDataMap.end()) {
1741 try {
1742 auto good_frames = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/good_frames"]);
1743 std::cout << std::endl << " good_frames: " << good_frames.GetData()[0];
1744 } catch(const std::bad_any_cast& e) {
1745 std::cerr << std::endl << "Error: Failed to cast good_frames data" << std::endl;
1746 }
1747 } else {
1748 std::cout << std::endl << " good_frames: n/a";
1749 }
1750
1751 if (fDataMap.find("/raw_data_1/experiment_identifier") != fDataMap.end()) {
1752 try {
1753 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/experiment_identifier"]);
1754 std::cout << std::endl << " experiment_identifier: " << str.GetData()[0];
1755 } catch (const std::bad_any_cast& e) {
1756 std::cerr << std::endl << "Error: Failed to cast experiment_identifier data" << std::endl;
1757 }
1758 } else {
1759 std::cout << std::endl << " experiment_identifier: n/a";
1760 }
1761
1762 std::cout << std::endl << "----";
1763 std::cout << std::endl << " instrument";
1764 if (fDataMap.find("/raw_data_1/instrument/name") != fDataMap.end()) {
1765 try {
1766 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/name"]);
1767 std::cout << std::endl << " name : " << str.GetData()[0];
1768 } catch (const std::bad_any_cast& e) {
1769 std::cerr << std::endl << "Error: Failed to cast instrument/name data" << std::endl;
1770 }
1771 } else {
1772 std::cout << std::endl << " name : n/a";
1773 }
1774
1775 std::cout << std::endl << "----";
1776 std::cout << std::endl << " source";
1777 if (fDataMap.find("/raw_data_1/instrument/source/name") != fDataMap.end()) {
1778 try {
1779 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/name"]);
1780 std::cout << std::endl << " name : " << str.GetData()[0];
1781 } catch (const std::bad_any_cast& e) {
1782 std::cerr << std::endl << "Error: Failed to cast instrument/source/name data" << std::endl;
1783 }
1784 } else {
1785 std::cout << std::endl << " name : n/a";
1786 }
1787
1788 if (fDataMap.find("/raw_data_1/instrument/source/type") != fDataMap.end()) {
1789 try {
1790 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/type"]);
1791 std::cout << std::endl << " type : " << str.GetData()[0];
1792 } catch (const std::bad_any_cast& e) {
1793 std::cerr << std::endl << "Error: Failed to cast instrument/source/type data" << std::endl;
1794 }
1795 } else {
1796 std::cout << std::endl << " type : n/a";
1797 }
1798
1799 if (fDataMap.find("/raw_data_1/instrument/source/probe") != fDataMap.end()) {
1800 try {
1801 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/probe"]);
1802 std::cout << std::endl << " probe : " << str.GetData()[0];
1803 } catch (const std::bad_any_cast& e) {
1804 std::cerr << std::endl << "Error: Failed to cast instrument/source/probe data" << std::endl;
1805 }
1806 } else {
1807 std::cout << std::endl << " probe : n/a";
1808 }
1809
1810 std::cout << std::endl << "----";
1811 std::cout << std::endl << " detector_1";
1812 std::cout << std::endl << " counts:";
1813 std::cout << std::endl;
1814 try {
1815 auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/counts"]);
1816 auto dims = counts_data.GetDimensions();
1817 std::cout << " counts dimensions: " << dims[0] << " x "
1818 << dims[1] << " x " << dims[2] << std::endl;
1819 std::cout << " total elements: " << counts_data.GetNumElements() << std::endl;
1820
1821 // Check for attributes
1822 if (counts_data.HasAttribute("signal")) {
1823 try {
1824 auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
1825 std::cout << " signal : " << signal << std::endl;
1826 } catch (const std::bad_any_cast& e) {
1827 std::cerr << "Error: Failed to cast signal attribute" << std::endl;
1828 }
1829 } else {
1830 std::cout << std::endl << " signal : n/a";
1831 }
1832
1833 if (counts_data.HasAttribute("axes")) {
1834 try {
1835 auto axes = std::any_cast<std::string>(counts_data.GetAttribute("axes"));
1836 std::cout << " axes : " << axes << std::endl;
1837 } catch (const std::bad_any_cast& e) {
1838 std::cerr << "Error: Failed to cast axes attribute" << std::endl;
1839 }
1840 } else {
1841 std::cout << std::endl << " axes : n/a";
1842 }
1843
1844 if (counts_data.HasAttribute("long_name")) {
1845 try {
1846 auto long_name = std::any_cast<std::string>(counts_data.GetAttribute("long_name"));
1847 std::cout << " long_name : " << long_name << std::endl;
1848 } catch (const std::bad_any_cast& e) {
1849 std::cerr << "Error: Failed to cast long_name attribute" << std::endl;
1850 }
1851 } else {
1852 std::cout << std::endl << " long_name : n/a";
1853 }
1854
1855 if (counts_data.HasAttribute("t0_bin")) {
1856 try {
1857 auto t0_bin = std::any_cast<int>(counts_data.GetAttribute("t0_bin"));
1858 std::cout << " t0_bin : " << t0_bin << std::endl;
1859 } catch (const std::bad_any_cast& e) {
1860 std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
1861 }
1862 } else {
1863 std::cout << std::endl << " t0_bin : n/a";
1864 }
1865
1866 if (counts_data.HasAttribute("first_good_bin")) {
1867 try {
1868 first_good_bin = std::any_cast<int>(counts_data.GetAttribute("first_good_bin"));
1869 std::cout << " first_good_bin : " << first_good_bin << std::endl;
1870 } catch (const std::bad_any_cast& e) {
1871 std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
1872 }
1873 } else {
1874 std::cout << std::endl << " first_good_bin : n/a";
1875 }
1876
1877 if (counts_data.HasAttribute("last_good_bin")) {
1878 try {
1879 auto last_good_bin = std::any_cast<int>(counts_data.GetAttribute("last_good_bin"));
1880 std::cout << " last_good_bin : " << last_good_bin << std::endl;
1881 } catch (const std::bad_any_cast& e) {
1882 std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
1883 }
1884 } else {
1885 std::cout << std::endl << " last_good_bin : n/a";
1886 }
1887
1888 if (fDataMap.find("/raw_data_1/instrument/detector_1/resolution") != fDataMap.end()) {
1889 try {
1890 auto ivalData = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/resolution"]);
1891 std::cout << " resolution : " << ivalData.GetData()[0];
1892 if (ivalData.HasAttribute("units")) {
1893 try {
1894 auto units = std::any_cast<std::string>(ivalData.GetAttribute("units"));
1895 std::cout << " " << units << std::endl;
1896 } catch (const std::bad_any_cast& e) {
1897 std::cerr << "Error: Failed to cast units attribute" << std::endl;
1898 }
1899 } else {
1900 std::cout << std::endl << " unit : n/a" << std::endl;
1901 }
1902
1903 } catch (const std::bad_any_cast& e) {
1904 std::cerr << "Error: Failed to cast resolution attribute" << std::endl;
1905 }
1906 } else {
1907 std::cout << std::endl << " resolution : n/a";
1908 }
1909
1910 // dump the first couple of counts of each detector
1911 const auto& data = counts_data.GetData();
1912 std::cout << std::endl << " first couple of counts of each detector:";
1913 for (unsigned int i=0; i<dims[1]; i++) {
1914 if (i<9)
1915 std::cout << std::endl << " " << i+1 << ": ";
1916 else
1917 std::cout << std::endl << " " << i+1 << ": ";
1918 for (unsigned int j=0; j<first_good_bin+5; j++)
1919 std::cout << data[i*dims[2]+j] << ", ";
1920 std::cout << "...";
1921 }
1922 } catch (const std::bad_any_cast& e) {
1923 std::cerr << "Error: Failed to cast counts data" << std::endl;
1924 }
1925
1926 std::cout << std::endl;
1927 std::cout << std::endl << " raw_time:";
1928 std::cout << std::endl;
1929 try {
1930 auto raw_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/instrument/detector_1/raw_time"]);
1931 const auto& data = raw_time.GetData();
1932
1933 // Check for attributes
1934 if (raw_time.HasAttribute("units")) {
1935 try {
1936 auto units = std::any_cast<std::string>(raw_time.GetAttribute("units"));
1937 std::cout << " units : " << units << std::endl;
1938 } catch (const std::bad_any_cast& e) {
1939 std::cerr << "Error: Failed to cast units attribute" << std::endl;
1940 }
1941 } else {
1942 std::cout << std::endl << " units : n/a" << std::endl;
1943 }
1944
1945 // dump the first couple of raw_times
1946 std::cout << " ";
1947 for (unsigned int i=0; i<first_good_bin+5; i++)
1948 std::cout << data[i] << ", ";
1949 std::cout << "...";
1950 } catch (const std::bad_any_cast& e) {
1951 std::cerr << "Error: Failed to cast raw_time data" << std::endl;
1952 }
1953
1954
1955 std::cout << std::endl;
1956 std::cout << std::endl << " spectrum_index:";
1957 std::cout << std::endl;
1958 try {
1959 auto spectrum_index = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/spectrum_index"]);
1960 const auto& data = spectrum_index.GetData();
1961
1962 // dump the first couple of raw_times
1963 std::cout << " ";
1964 for (unsigned int i=0; i<first_good_bin+5; i++)
1965 std::cout << data[i] << ", ";
1966 std::cout << "...";
1967 } catch (const std::bad_any_cast& e) {
1968 std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
1969 }
1970
1971 std::cout << std::endl;
1972 std::cout << std::endl << " dead_time:";
1973 std::cout << std::endl;
1974 try {
1975 auto dead_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/instrument/detector_1/dead_time"]);
1976 const auto& data = dead_time.GetData();
1977
1978 // dump the first couple of raw_times
1979 std::cout << " ";
1980 for (unsigned int i=0; i<first_good_bin+5; i++)
1981 std::cout << data[i] << ", ";
1982 std::cout << "...";
1983 } catch (const std::bad_any_cast& e) {
1984 std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
1985 }
1986
1987 std::cout << std::endl;
1988 }
1989}
1990
1991//=============================================================================
1992// nxH4::PNeXus::WriteNexusFile
1993//=============================================================================
1994int nxH4::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion)
1995{
1996 // Create new HDF4 file using SD interface (for datasets)
1997 int32 sd_id = SDstart(filename.c_str(), DFACC_CREATE);
1998 if (sd_id == FAIL) {
1999 std::cerr << "**ERROR** Failed to create HDF4 file: " << filename << std::endl;
2000 return 1;
2001 }
2002
2003 // Write file-level attributes (NeXus_version, HDF4_version, etc.)
2004 WriteFileAttributes(sd_id);
2005
2006 if ((idfVersion != 1) && (idfVersion != 2)) {
2007 std::cerr << "**ERROR** Unsupported IDF version for writing: " << idfVersion << std::endl;
2008 SDend(sd_id);
2009 return 1;
2010 }
2011
2012 // Write all SDS datasets and collect their references keyed by path
2013 std::map<std::string, int32> sdsRefMap;
2014
2015 for (const auto& [path, data_any] : fDataMap) {
2016 int32 sds_ref = -1;
2017
2018 // Try to write as int dataset
2019 try {
2020 auto data = std::any_cast<PNXdata<int>>(data_any);
2021 sds_ref = WriteIntDataset(sd_id, path, data);
2022 } catch (...) {}
2023
2024 if (sds_ref == -1) {
2025 // Try to write as float dataset
2026 try {
2027 auto data = std::any_cast<PNXdata<float>>(data_any);
2028 sds_ref = WriteFloatDataset(sd_id, path, data);
2029 } catch (...) {}
2030 }
2031
2032 if (sds_ref == -1) {
2033 // Try to write as string dataset
2034 try {
2035 auto data = std::any_cast<PNXdata<std::string>>(data_any);
2036 sds_ref = WriteStringDataset(sd_id, path, data);
2037 } catch (...) {}
2038 }
2039
2040 if (sds_ref != -1) {
2041 sdsRefMap[path] = sds_ref;
2042 }
2043 }
2044
2045 // Close SD interface before building Vgroup hierarchy
2046 SDend(sd_id);
2047
2048 // Now open the file with Hopen/Vstart to build the Vgroup hierarchy
2049 int32 file_id = Hopen(filename.c_str(), DFACC_WRITE, 0);
2050 if (file_id == FAIL) {
2051 std::cerr << "**ERROR** Failed to reopen HDF4 file for Vgroup creation: " << filename << std::endl;
2052 return 1;
2053 }
2054
2055 if (Vstart(file_id) == FAIL) {
2056 std::cerr << "**ERROR** Failed to initialize Vgroup interface" << std::endl;
2057 Hclose(file_id);
2058 return 1;
2059 }
2060
2061 // Build Vgroup hierarchy and link SDS datasets into the correct groups
2062 std::map<std::string, int32> vgroupCache; // path -> vgroup ref
2063
2064 for (const auto& [path, sds_ref] : sdsRefMap) {
2065 // Extract parent path
2066 size_t lastSlash = path.find_last_of('/');
2067 if (lastSlash == std::string::npos || lastSlash == 0) {
2068 // Dataset is at root level, no Vgroup needed
2069 continue;
2070 }
2071 std::string parentPath = path.substr(0, lastSlash);
2072
2073 // Create the Vgroup hierarchy for the parent path
2074 int32 parent_vg_ref = CreateVGroupHierarchy(file_id, parentPath, vgroupCache);
2075 if (parent_vg_ref == -1) {
2076 std::cerr << "**ERROR** Failed to create Vgroup hierarchy for: " << parentPath << std::endl;
2077 continue;
2078 }
2079
2080 // Attach to parent Vgroup and add the SDS dataset
2081 int32 parent_vg_id = Vattach(file_id, parent_vg_ref, "w");
2082 if (parent_vg_id != FAIL) {
2083 Vaddtagref(parent_vg_id, DFTAG_NDG, sds_ref);
2084 Vdetach(parent_vg_id);
2085 }
2086 }
2087
2088 // Detach all cached Vgroups (they were left attached for reuse)
2089 // No — Vgroups were detached after creation in CreateVGroupHierarchy.
2090 // We only re-attach briefly above to add tag/refs.
2091
2092 Vend(file_id);
2093 Hclose(file_id);
2094
2095 return 0;
2096}
2097
2098//=============================================================================
2099// nxH4::PNeXus::WriteFileAttributes
2100//=============================================================================
2102{
2103 // Write HDF4 version
2104 if (!fHdf4Version.empty()) {
2105 SDsetattr(sd_id, "HDF4_version", DFNT_CHAR8,
2106 fHdf4Version.length(), fHdf4Version.c_str());
2107 }
2108
2109 // Write NeXus version
2110 if (!fNeXusVersion.empty()) {
2111 SDsetattr(sd_id, "NeXus_version", DFNT_CHAR8,
2112 fNeXusVersion.length(), fNeXusVersion.c_str());
2113 }
2114
2115 // Write file name
2116 if (!fFileNameNxs.empty()) {
2117 SDsetattr(sd_id, "file_name", DFNT_CHAR8,
2118 fFileNameNxs.length(), fFileNameNxs.c_str());
2119 }
2120
2121 // Write file time
2122 if (!fFileTimeNxs.empty()) {
2123 SDsetattr(sd_id, "file_time", DFNT_CHAR8,
2124 fFileTimeNxs.length(), fFileTimeNxs.c_str());
2125 }
2126
2127 // Write root level attributes from fGroupAttributes
2128 if (fGroupAttributes.find("/") != fGroupAttributes.end()) {
2129 const auto& attrs = fGroupAttributes.at("/");
2130 for (const auto& [attr_name, attr_value] : attrs) {
2131 // Handle different attribute types
2132 try {
2133 auto str_attr = std::any_cast<std::string>(attr_value);
2134 SDsetattr(sd_id, attr_name.c_str(), DFNT_CHAR8,
2135 str_attr.length(), str_attr.c_str());
2136 } catch (...) {
2137 // Try other types if needed
2138 }
2139 }
2140 }
2141}
2142
2143//=============================================================================
2144// nxH4::PNeXus::WriteIntDataset
2145//=============================================================================
2146int32 nxH4::PNeXus::WriteIntDataset(int32 sd_id, const std::string& path,
2147 const PNXdata<int>& data)
2148{
2149 // Extract dataset name from path
2150 std::vector<std::string> components = SplitPath(path);
2151 if (components.empty()) {
2152 throw std::runtime_error("Invalid path: " + path);
2153 }
2154 std::string dataset_name = components.back();
2155
2156 const auto& dims = data.GetDimensions();
2157 int32 rank = static_cast<int32>(dims.size());
2158 std::vector<int32> dim_sizes(rank);
2159 for (int32 i = 0; i < rank; i++) {
2160 dim_sizes[i] = static_cast<int32>(dims[i]);
2161 }
2162
2163 // Create dataset
2164 int32 sds_id = SDcreate(sd_id, dataset_name.c_str(), DFNT_INT32, rank, dim_sizes.data());
2165 if (sds_id == FAIL) {
2166 throw std::runtime_error("Failed to create dataset: " + dataset_name);
2167 }
2168
2169 // Convert data to int32
2170 const auto& int_data = data.GetData();
2171 std::vector<int32> data32(int_data.begin(), int_data.end());
2172
2173 // Write data
2174 int32 start[H4_MAX_VAR_DIMS] = {0};
2175 if (SDwritedata(sds_id, start, nullptr, dim_sizes.data(), data32.data()) == FAIL) {
2176 SDendaccess(sds_id);
2177 throw std::runtime_error("Failed to write dataset: " + dataset_name);
2178 }
2179
2180 // Write attributes
2181 WriteDatasetAttributes(sds_id, data);
2182
2183 // Get the SDS reference for Vgroup linking
2184 int32 sds_ref = SDidtoref(sds_id);
2185
2186 SDendaccess(sds_id);
2187
2188 return sds_ref;
2189}
2190
2191//=============================================================================
2192// nxH4::PNeXus::WriteFloatDataset
2193//=============================================================================
2194int32 nxH4::PNeXus::WriteFloatDataset(int32 sd_id, const std::string& path,
2195 const PNXdata<float>& data)
2196{
2197 // Extract dataset name from path
2198 std::vector<std::string> components = SplitPath(path);
2199 if (components.empty()) {
2200 throw std::runtime_error("Invalid path: " + path);
2201 }
2202 std::string dataset_name = components.back();
2203
2204 const auto& dims = data.GetDimensions();
2205 int32 rank = static_cast<int32>(dims.size());
2206 std::vector<int32> dim_sizes(rank);
2207 for (int32 i = 0; i < rank; i++) {
2208 dim_sizes[i] = static_cast<int32>(dims[i]);
2209 }
2210
2211 // Create dataset
2212 int32 sds_id = SDcreate(sd_id, dataset_name.c_str(), DFNT_FLOAT32, rank, dim_sizes.data());
2213 if (sds_id == FAIL) {
2214 throw std::runtime_error("Failed to create dataset: " + dataset_name);
2215 }
2216
2217 // Convert data to float32
2218 const auto& float_data = data.GetData();
2219 std::vector<float32> data32(float_data.begin(), float_data.end());
2220
2221 // Write data
2222 int32 start[H4_MAX_VAR_DIMS] = {0};
2223 if (SDwritedata(sds_id, start, nullptr, dim_sizes.data(), data32.data()) == FAIL) {
2224 SDendaccess(sds_id);
2225 throw std::runtime_error("Failed to write dataset: " + dataset_name);
2226 }
2227
2228 // Write attributes
2229 WriteDatasetAttributes(sds_id, data);
2230
2231 // Get the SDS reference for Vgroup linking
2232 int32 sds_ref = SDidtoref(sds_id);
2233
2234 SDendaccess(sds_id);
2235
2236 return sds_ref;
2237}
2238
2239//=============================================================================
2240// nxH4::PNeXus::WriteStringDataset
2241//=============================================================================
2242int32 nxH4::PNeXus::WriteStringDataset(int32 sd_id, const std::string& path,
2243 const PNXdata<std::string>& data)
2244{
2245 // Extract dataset name from path
2246 std::vector<std::string> components = SplitPath(path);
2247 if (components.empty()) {
2248 throw std::runtime_error("Invalid path: " + path);
2249 }
2250 std::string dataset_name = components.back();
2251
2252 const auto& string_data = data.GetData();
2253 if (string_data.empty()) {
2254 return -1;
2255 }
2256
2257 std::string str = string_data[0];
2258 int32 dims[1] = {static_cast<int32>(str.length())};
2259
2260 // Create dataset
2261 int32 sds_id = SDcreate(sd_id, dataset_name.c_str(), DFNT_CHAR8, 1, dims);
2262 if (sds_id == FAIL) {
2263 throw std::runtime_error("Failed to create dataset: " + dataset_name);
2264 }
2265
2266 // Write data - HDF4 requires non-const pointer
2267 std::vector<char> str_buffer(str.begin(), str.end());
2268 int32 start[1] = {0};
2269 if (SDwritedata(sds_id, start, nullptr, dims, str_buffer.data()) == FAIL) {
2270 SDendaccess(sds_id);
2271 throw std::runtime_error("Failed to write dataset: " + dataset_name);
2272 }
2273
2274 // Write attributes
2275 WriteDatasetAttributes(sds_id, data);
2276
2277 // Get the SDS reference for Vgroup linking
2278 int32 sds_ref = SDidtoref(sds_id);
2279
2280 SDendaccess(sds_id);
2281
2282 return sds_ref;
2283}
2284
2285//=============================================================================
2286// nxH4::PNeXus::WriteDatasetAttributes (template specializations)
2287//=============================================================================
2288template <typename T>
2290{
2291 const auto& attributes = data.GetAttributes();
2292
2293 for (const auto& [attr_name, attr_value] : attributes) {
2294 // Try different attribute types - primitives first (like h5nexus)
2295
2296 // Try scalar int
2297 try {
2298 int32 value = std::any_cast<int>(attr_value);
2299 SDsetattr(sds_id, attr_name.c_str(), DFNT_INT32, 1, &value);
2300 continue;
2301 } catch (...) {}
2302
2303 // Try scalar float
2304 try {
2305 float32 value = std::any_cast<float>(attr_value);
2306 SDsetattr(sds_id, attr_name.c_str(), DFNT_FLOAT32, 1, &value);
2307 continue;
2308 } catch (...) {}
2309
2310 // Try string
2311 try {
2312 std::string str = std::any_cast<std::string>(attr_value);
2313 SDsetattr(sds_id, attr_name.c_str(), DFNT_CHAR8, str.length(), str.c_str());
2314 continue;
2315 } catch (...) {}
2316
2317 // Try vector<int> for multi-element attributes
2318 try {
2319 std::vector<int> int_vec = std::any_cast<std::vector<int>>(attr_value);
2320 std::vector<int32> data32(int_vec.begin(), int_vec.end());
2321 SDsetattr(sds_id, attr_name.c_str(), DFNT_INT32, data32.size(), data32.data());
2322 continue;
2323 } catch (...) {}
2324
2325 // Try vector<float> for multi-element attributes
2326 try {
2327 std::vector<float> float_vec = std::any_cast<std::vector<float>>(attr_value);
2328 std::vector<float32> data32(float_vec.begin(), float_vec.end());
2329 SDsetattr(sds_id, attr_name.c_str(), DFNT_FLOAT32, data32.size(), data32.data());
2330 continue;
2331 } catch (...) {}
2332 }
2333}
2334
2335//=============================================================================
2336// nxH4::PNeXus::WriteVGroupAttributes
2337//=============================================================================
2339 const std::map<std::string, std::any>& attributes)
2340{
2341 for (const auto& [attr_name, attr_value] : attributes) {
2342 // Try string
2343 try {
2344 std::string str = std::any_cast<std::string>(attr_value);
2345 Vsetattr(vgroup_id, attr_name.c_str(), DFNT_CHAR8,
2346 static_cast<int32>(str.length()), str.c_str());
2347 continue;
2348 } catch (...) {}
2349
2350 // Try scalar int
2351 try {
2352 int32 value = std::any_cast<int>(attr_value);
2353 Vsetattr(vgroup_id, attr_name.c_str(), DFNT_INT32, 1, &value);
2354 continue;
2355 } catch (...) {}
2356
2357 // Try scalar float
2358 try {
2359 float32 value = std::any_cast<float>(attr_value);
2360 Vsetattr(vgroup_id, attr_name.c_str(), DFNT_FLOAT32, 1, &value);
2361 continue;
2362 } catch (...) {}
2363
2364 // Try vector<int>
2365 try {
2366 std::vector<int> int_vec = std::any_cast<std::vector<int>>(attr_value);
2367 std::vector<int32> data32(int_vec.begin(), int_vec.end());
2368 Vsetattr(vgroup_id, attr_name.c_str(), DFNT_INT32,
2369 static_cast<int32>(data32.size()), data32.data());
2370 continue;
2371 } catch (...) {}
2372
2373 // Try vector<float>
2374 try {
2375 std::vector<float> float_vec = std::any_cast<std::vector<float>>(attr_value);
2376 std::vector<float32> data32(float_vec.begin(), float_vec.end());
2377 Vsetattr(vgroup_id, attr_name.c_str(), DFNT_FLOAT32,
2378 static_cast<int32>(data32.size()), data32.data());
2379 continue;
2380 } catch (...) {}
2381 }
2382}
2383
2384//=============================================================================
2385// nxH4::PNeXus::CreateVGroupHierarchy
2386//=============================================================================
2387int32 nxH4::PNeXus::CreateVGroupHierarchy(int32 file_id, const std::string& path,
2388 std::map<std::string, int32>& vgroupCache)
2389{
2390 std::vector<std::string> components = SplitPath(path);
2391 if (components.empty()) {
2392 return -1;
2393 }
2394
2395 std::string currentPath;
2396 int32 parent_vg_ref = -1;
2397
2398 for (const auto& component : components) {
2399 if (component.empty()) continue;
2400
2401 currentPath += "/" + component;
2402
2403 // Check if this Vgroup was already created
2404 auto it = vgroupCache.find(currentPath);
2405 if (it != vgroupCache.end()) {
2406 parent_vg_ref = it->second;
2407 continue;
2408 }
2409
2410 // Create a new Vgroup
2411 int32 vg_id = Vattach(file_id, -1, "w");
2412 if (vg_id == FAIL) {
2413 std::cerr << "**ERROR** Failed to create Vgroup for: " << currentPath << std::endl;
2414 return -1;
2415 }
2416
2417 Vsetname(vg_id, component.c_str());
2418
2419 // Write group attributes if any exist for this path
2420 auto attrIt = fGroupAttributes.find(currentPath);
2421 if (attrIt != fGroupAttributes.end()) {
2422 // Check for NX_class attribute and set it as Vgroup class
2423 auto classIt = attrIt->second.find("NX_class");
2424 if (classIt != attrIt->second.end()) {
2425 try {
2426 std::string nxClass = std::any_cast<std::string>(classIt->second);
2427 Vsetclass(vg_id, nxClass.c_str());
2428 } catch (...) {}
2429 }
2430 WriteVGroupAttributes(vg_id, attrIt->second);
2431 }
2432
2433 // Get the reference of the newly created Vgroup
2434 int32 vg_ref = VQueryref(vg_id);
2435
2436 // If there is a parent Vgroup, add this one as a child
2437 if (parent_vg_ref != -1) {
2438 int32 parent_vg_id = Vattach(file_id, parent_vg_ref, "w");
2439 if (parent_vg_id != FAIL) {
2440 Vinsert(parent_vg_id, vg_id);
2441 Vdetach(parent_vg_id);
2442 }
2443 }
2444
2445 Vdetach(vg_id);
2446
2447 // Cache the reference for this path
2448 vgroupCache[currentPath] = vg_ref;
2449 parent_vg_ref = vg_ref;
2450 }
2451
2452 return parent_vg_ref;
2453}
2454
2455//=============================================================================
2456// Group attribute methods - manage attributes associated with HDF4 groups
2457//=============================================================================
2458
2471bool nxH4::PNeXus::AddGroupAttribute(const std::string& groupPath,
2472 const std::string& attrName,
2473 const std::any& attrValue)
2474{
2475 fGroupAttributes[groupPath][attrName] = attrValue;
2476 return true;
2477}
2478
2479bool nxH4::PNeXus::RemoveGroupAttribute(const std::string& groupPath,
2480 const std::string& attrName)
2481{
2482 auto it = fGroupAttributes.find(groupPath);
2483 if (it != fGroupAttributes.end()) {
2484 auto attr_it = it->second.find(attrName);
2485 if (attr_it != it->second.end()) {
2486 it->second.erase(attr_it);
2487 return true;
2488 }
2489 }
2490 return false;
2491}
2492
2493bool nxH4::PNeXus::HasGroupAttribute(const std::string& groupPath,
2494 const std::string& attrName) const
2495{
2496 auto it = fGroupAttributes.find(groupPath);
2497 if (it != fGroupAttributes.end()) {
2498 return it->second.find(attrName) != it->second.end();
2499 }
2500 return false;
2501}
2502
2503std::any nxH4::PNeXus::GetGroupAttribute(const std::string& groupPath,
2504 const std::string& attrName) const
2505{
2506 return fGroupAttributes.at(groupPath).at(attrName);
2507}
2508
2509const std::map<std::string, std::any>& nxH4::PNeXus::GetGroupAttributes(
2510 const std::string& groupPath) const
2511{
2512 static std::map<std::string, std::any> empty_map;
2513 auto it = fGroupAttributes.find(groupPath);
2514 if (it != fGroupAttributes.end()) {
2515 return it->second;
2516 }
2517 return empty_map;
2518}
2519
2520bool nxH4::PNeXus::ClearGroupAttributes(const std::string& groupPath)
2521{
2522 auto it = fGroupAttributes.find(groupPath);
2523 if (it != fGroupAttributes.end()) {
2524 it->second.clear();
2525 return true;
2526 }
2527 return false;
2528}
2529
2530bool nxH4::PNeXus::AddRootAttribute(const std::string& attrName, const std::any& attrValue)
2531{
2532 return AddGroupAttribute("/", attrName, attrValue);
2533}
2534
2535//=============================================================================
2536// Helper methods
2537//=============================================================================
2538bool nxH4::PNeXus::CaseInsensitiveEquals(const std::string& a, const std::string& b)
2539{
2540 if (a.length() != b.length()) {
2541 return false;
2542 }
2543 return std::equal(a.begin(), a.end(), b.begin(),
2544 [](char a, char b) {
2545 return std::tolower(static_cast<unsigned char>(a)) ==
2546 std::tolower(static_cast<unsigned char>(b));
2547 });
2548}
2549
2550std::vector<std::string> nxH4::PNeXus::SplitPath(const std::string& path)
2551{
2552 std::vector<std::string> components;
2553 std::string current;
2554
2555 for (char c : path) {
2556 if (c == '/') {
2557 if (!current.empty()) {
2558 components.push_back(current);
2559 current.clear();
2560 }
2561 } else {
2562 current += c;
2563 }
2564 }
2565
2566 if (!current.empty()) {
2567 components.push_back(current);
2568 }
2569
2570 return components;
2571}
2572
2573std::string nxH4::PNeXus::FindAttributeName(int32 sd_id, const std::string& requestedName)
2574{
2575 int32 n_datasets, n_attrs;
2576 if (SDfileinfo(sd_id, &n_datasets, &n_attrs) == FAIL) {
2577 throw std::runtime_error("Failed to get file info");
2578 }
2579
2580 char attr_name[H4_MAX_NC_NAME];
2581 int32 attr_type, attr_count;
2582
2583 for (int32 i = 0; i < n_attrs; i++) {
2584 if (SDattrinfo(sd_id, i, attr_name, &attr_type, &attr_count) != FAIL) {
2585 if (CaseInsensitiveEquals(attr_name, requestedName)) {
2586 return std::string(attr_name);
2587 }
2588 }
2589 }
2590
2591 throw std::runtime_error("Attribute not found: " + requestedName);
2592}
2593
2594int32 nxH4::PNeXus::FindDatasetIndex(int32 sd_id, const std::string& requestedName)
2595{
2596 // First try exact match
2597 int32 idx = SDnametoindex(sd_id, requestedName.c_str());
2598 if (idx != FAIL) {
2599 return idx;
2600 }
2601
2602 // Try case-insensitive search
2603 int32 n_datasets, n_attrs;
2604 if (SDfileinfo(sd_id, &n_datasets, &n_attrs) == FAIL) {
2605 throw std::runtime_error("Failed to get file info");
2606 }
2607
2608 for (int32 i = 0; i < n_datasets; i++) {
2609 int32 sds_id = SDselect(sd_id, i);
2610 if (sds_id != FAIL) {
2611 char name[H4_MAX_NC_NAME];
2612 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_ds_attrs;
2613 if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_ds_attrs) != FAIL) {
2614 if (CaseInsensitiveEquals(name, requestedName)) {
2615 SDendaccess(sds_id);
2616 return i;
2617 }
2618 }
2619 SDendaccess(sds_id);
2620 }
2621 }
2622
2623 throw std::runtime_error("Dataset not found: " + requestedName);
2624}
2625
2626int32 nxH4::PNeXus::FindDatasetRefByPath(const std::string& path)
2627{
2628 // Navigate VGroup hierarchy to find the dataset reference
2629 // Path format: /group1/group2/.../dataset_name
2630
2631 std::vector<std::string> components = SplitPath(path);
2632 if (components.empty()) {
2633 return -1;
2634 }
2635
2636 // Open file with V interface
2637 if (Vstart(fFileId) == FAIL) {
2638 return -1;
2639 }
2640
2641 int32 result_ref = -1;
2642 int32 current_vg_ref = -1;
2643
2644 // Find root VGroup (first component after /)
2645 // For IDF version 1, root is typically "run"
2646 if (components.size() >= 1) {
2647 // Get all lone vgroups
2648 int32 n_vgroups = Vlone(fFileId, nullptr, 0);
2649 if (n_vgroups > 0) {
2650 std::vector<int32> vgroup_refs(n_vgroups);
2651 Vlone(fFileId, vgroup_refs.data(), n_vgroups);
2652
2653 // Find the root VGroup
2654 for (int32 i = 0; i < n_vgroups; i++) {
2655 int32 vg_id = Vattach(fFileId, vgroup_refs[i], "r");
2656 if (vg_id != FAIL) {
2657 char vg_name[VGNAMELENMAX];
2658 if (Vgetname_portable(vg_id, vg_name, sizeof(vg_name)) != FAIL) {
2659 if (CaseInsensitiveEquals(vg_name, components[0])) {
2660 current_vg_ref = vgroup_refs[i];
2661 Vdetach(vg_id);
2662 break;
2663 }
2664 }
2665 Vdetach(vg_id);
2666 }
2667 }
2668 }
2669 }
2670
2671 // Navigate through intermediate VGroups
2672 for (size_t comp_idx = 1; comp_idx < components.size() - 1; comp_idx++) {
2673 if (current_vg_ref == -1) break;
2674
2675 int32 vg_id = Vattach(fFileId, current_vg_ref, "r");
2676 if (vg_id == FAIL) {
2677 current_vg_ref = -1;
2678 break;
2679 }
2680
2681 int32 n_entries = Vntagrefs(vg_id);
2682 bool found = false;
2683
2684 for (int32 i = 0; i < n_entries; i++) {
2685 int32 tag, ref;
2686 if (Vgettagref(vg_id, i, &tag, &ref) != FAIL) {
2687 // Check if it's a VGroup
2688 if (tag == DFTAG_VG) {
2689 int32 child_vg_id = Vattach(fFileId, ref, "r");
2690 if (child_vg_id != FAIL) {
2691 char child_name[VGNAMELENMAX];
2692 if (Vgetname_portable(child_vg_id, child_name, sizeof(child_name)) != FAIL) {
2693 if (CaseInsensitiveEquals(child_name, components[comp_idx])) {
2694 current_vg_ref = ref;
2695 found = true;
2696 Vdetach(child_vg_id);
2697 break;
2698 }
2699 }
2700 Vdetach(child_vg_id);
2701 }
2702 }
2703 }
2704 }
2705
2706 Vdetach(vg_id);
2707
2708 if (!found) {
2709 current_vg_ref = -1;
2710 break;
2711 }
2712 }
2713
2714 // Find the dataset in the final VGroup
2715 if (current_vg_ref != -1 && components.size() >= 1) {
2716 int32 vg_id = Vattach(fFileId, current_vg_ref, "r");
2717 if (vg_id != FAIL) {
2718 int32 n_entries = Vntagrefs(vg_id);
2719 std::string target_name = components.back();
2720
2721 for (int32 i = 0; i < n_entries; i++) {
2722 int32 tag, ref;
2723 if (Vgettagref(vg_id, i, &tag, &ref) != FAIL) {
2724 // Check if it's a Numeric Data (SDS)
2725 if (tag == DFTAG_NDG) {
2726 // Try to get the name by converting ref to index
2727 int32 sds_idx = SDreftoindex(fSdId, ref);
2728 if (sds_idx != FAIL) {
2729 int32 sds_id = SDselect(fSdId, sds_idx);
2730 if (sds_id != FAIL) {
2731 char ds_name[H4_MAX_NC_NAME];
2732 int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
2733 if (SDgetinfo(sds_id, ds_name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
2734 if (CaseInsensitiveEquals(ds_name, target_name)) {
2735 result_ref = ref;
2736 SDendaccess(sds_id);
2737 break;
2738 }
2739 }
2740 SDendaccess(sds_id);
2741 }
2742 }
2743 }
2744 }
2745 }
2746
2747 Vdetach(vg_id);
2748 }
2749 }
2750
2751 Vend(fFileId);
2752 return result_ref;
2753}
2754
2767{
2768 switch (hdf4_type) {
2769 case DFNT_INT32: return H4DataType::kINT32;
2770 case DFNT_FLOAT32: return H4DataType::kFLOAT32;
2771 case DFNT_FLOAT64: return H4DataType::kFLOAT64;
2772 case DFNT_CHAR8: return H4DataType::kCHAR8;
2773 case DFNT_UINT32: return H4DataType::kUINT32;
2774 case DFNT_INT16: return H4DataType::kINT16;
2775 case DFNT_UINT16: return H4DataType::kUINT16;
2776 case DFNT_INT8: return H4DataType::kINT8;
2777 case DFNT_UINT8: return H4DataType::kUINT8;
2778 default: return H4DataType::kINT32;
2779 }
2780}
2781
2794{
2795 switch (dataType) {
2796 case H4DataType::kINT32: return DFNT_INT32;
2797 case H4DataType::kFLOAT32: return DFNT_FLOAT32;
2798 case H4DataType::kFLOAT64: return DFNT_FLOAT64;
2799 case H4DataType::kCHAR8: return DFNT_CHAR8;
2800 case H4DataType::kUINT32: return DFNT_UINT32;
2801 case H4DataType::kINT16: return DFNT_INT16;
2802 case H4DataType::kUINT16: return DFNT_UINT16;
2803 case H4DataType::kINT8: return DFNT_INT8;
2804 case H4DataType::kUINT8: return DFNT_UINT8;
2805 default: return DFNT_INT32;
2806 }
2807}
2808#endif // HAVE_HDF4
2809
2810// ** HDF5 ********************************************************************
2811
2812//=============================================================================
2813// nxH5::PNeXusDeadTime Constructor
2814//=============================================================================
2816{
2817 std::map<std::string, std::any> dataMap = nxs->GetDataMap();
2818
2819 int idfVersion = nxs->GetIdfVersion();
2820 if ((idfVersion != 1) && (idfVersion != 2)) {
2821 std::stringstream err;
2822 err << "**ERROR** Found unsupported IDF version '" << idfVersion << "'";
2823 std::cout << err.str() << std::endl;
2824 fValid = false;
2825 return;
2826 }
2827
2828 if (idfVersion == 1) {
2829 // not yet implemented
2830 } else { // idfVersion == 2
2831 // get counts
2832 if (dataMap.find("/raw_data_1/instrument/detector_1/counts") != dataMap.end()) {
2833 auto counts_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/counts"]);
2834 const auto& counts = counts_data.GetData();
2835 fCounts = counts;
2836 auto dims = counts_data.GetDimensions();
2837 if (fDebug) {
2838 std::cout << " counts dimensions: " << dims[0] << " x "
2839 << dims[1] << " x " << dims[2] << std::endl;
2840 }
2841 fDims = dims;
2842
2843 // get t0
2844 if (counts_data.HasAttribute("t0_bin")) {
2845 try {
2846 auto t0_bin = std::any_cast<int>(counts_data.GetAttribute("t0_bin"));
2847 fT0Bin = t0_bin;
2848 if (fDebug)
2849 std::cout << " fT0Bin: " << fT0Bin << std::endl;
2850 } catch (const std::bad_any_cast& e) {
2851 std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
2852 }
2853 }
2854 // get fgb
2855 if (counts_data.HasAttribute("first_good_bin")) {
2856 try {
2857 auto first_good_bin = std::any_cast<int>(counts_data.GetAttribute("first_good_bin"));
2858 fFgbBin = first_good_bin;
2859 if (fDebug)
2860 std::cout << " fFgbBin: " << fFgbBin << std::endl;
2861 } catch (const std::bad_any_cast& e) {
2862 std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
2863 }
2864 }
2865 // get lgb
2866 if (counts_data.HasAttribute("last_good_bin")) {
2867 try {
2868 auto last_good_bin = std::any_cast<int>(counts_data.GetAttribute("last_good_bin"));
2869 fLgbBin = last_good_bin;
2870 if (fDebug)
2871 std::cout << " fLgbBin: " << fLgbBin << std::endl;
2872 } catch (const std::bad_any_cast& e) {
2873 std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
2874 }
2875 }
2876 }
2877
2878 // get dead time parameters
2879 if (dataMap.find("/raw_data_1/instrument/detector_1/dead_time") != dataMap.end()) {
2880 auto dead_time = std::any_cast<PNXdata<float>>(dataMap["/raw_data_1/instrument/detector_1/dead_time"]);
2881 const auto& dt_data = dead_time.GetData();
2882 fDeadTime = dt_data;
2883 }
2884 fDeadTimeEstimated.resize(fDeadTime.size());
2885
2886 // get good_frames
2887 if (dataMap.find("/raw_data_1/good_frames") != dataMap.end()) {
2888 auto intData = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/good_frames"]);
2889 const auto& ival = intData.GetData();
2890 fGoodFrames = ival[0];
2891 if (fDebug)
2892 std::cout << " good_frames: " << fGoodFrames << std::endl;
2893 }
2894 // get resolution with units
2895 if (dataMap.find("/raw_data_1/instrument/detector_1/resolution") != dataMap.end()) {
2896 auto int_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/resolution"]);
2897 const auto& ival = int_data.GetData();
2898 float scale{1.0};
2899 if (int_data.HasAttribute("units")) {
2900 try {
2901 auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
2902 if (units == "picoseconds")
2903 scale = 1.0e-6;
2904 else if (units == "nanoseconds")
2905 scale = 1.0e-3;
2906 } catch (const std::bad_any_cast& e) {
2907 std::cerr << "Error: Failed to cast units attribute" << std::endl;
2908 }
2909 }
2910 fTimeResolution = ival[0] * scale;
2911 if (fDebug)
2912 std::cout << " time_resolution: " << fTimeResolution << " (us)" << std::endl;
2913 }
2914 }
2915}
2916
2917//=============================================================================
2918// nxH5::PNeXusDeadTime::operator()
2919//=============================================================================
2942double nxH5::PNeXusDeadTime::operator()(const std::vector<double> &par) const
2943{
2944 // par[0]: dtc, par[1]: N0, par[2]: N_bkg
2945
2946 double nt=0, nd=0, ll=0;
2947 const double tau_mu=2.1969811;
2948 for (unsigned int i=fFgbBin; i<fLgbBin; i++) {
2949 nt = par[1]*exp(-(((double)i-(double)fT0Bin+0.5)*fTimeResolution)/tau_mu)+par[2]; // theory
2950 if (nt <= 0) nt=1.0e-6;
2951 nt = nt/(1.0+nt*par[0]); // dead time corrected
2952 if (nt <= 1.0e-12) nt=1.0e-12;
2953 nd = (double)fCounts[i+fIdx*fDims[2]]; // data
2954 ll += nt - nd/(fGoodFrames*fTimeResolution) * log(nt);
2955 }
2956
2957 return ll;
2958}
2959
2960//=============================================================================
2961// nxH5::PNeXusDeadTime::Minimize()
2962//=============================================================================
2983{
2984 fIdx = i;
2985
2986 double n0 = 1.07*(double)fCounts[fFgbBin+fIdx*fDims[2]];
2987 ROOT::Minuit2::MnUserParameters params;
2988 params.Add("dtc", 0.01, 0.01);
2989 params.Add("N0", n0/fDims[2], 1.0);
2990 params.Add("N_bkg", 0.1/fDims[2], 0.1);
2991
2992 // create minimizer object
2993 ROOT::Minuit2::MnMinimize minimize(*this, params, ROOT::Minuit2::MnStrategy{2});
2994
2995 // minimize
2996 // maxfcn is MINUIT2 Default maxfcn
2997 unsigned int maxfcn = std::numeric_limits<unsigned int>::max();
2998 double tolerance = 0.1;
2999 ROOT::Minuit2::FunctionMinimum min = minimize(maxfcn, tolerance);
3000
3001 std::unique_ptr<ROOT::Minuit2::FunctionMinimum> fcnMin;
3002
3003 // keep FunctionMinimum object
3004 fcnMin.reset(new ROOT::Minuit2::FunctionMinimum(min));
3005
3006 // keep user parameters
3007 if (fcnMin)
3008 params = fcnMin->UserParameters();
3009
3010 fDeadTimeEstimated[i] = params.Value(0);
3011
3012 if (fDebug)
3013 std::cout << "debug> " << i << ": dtc fitted=" << params.Value(0) << ", dtc from file=" << fDeadTime[i] << std::endl;
3014}
3015
3016//=============================================================================
3017// Case-insensitive string comparison helper
3018//=============================================================================
3037bool nxH5::PNeXus::CaseInsensitiveEquals(const std::string& a, const std::string& b)
3038{
3039 if (a.length() != b.length()) {
3040 return false;
3041 }
3042
3043 for (size_t i = 0; i < a.length(); ++i) {
3044 if (std::tolower(static_cast<unsigned char>(a[i])) !=
3045 std::tolower(static_cast<unsigned char>(b[i]))) {
3046 return false;
3047 }
3048 }
3049
3050 return true;
3051}
3052
3053//=============================================================================
3054// Split HDF5 path into components
3055//=============================================================================
3074std::vector<std::string> nxH5::PNeXus::SplitPath(const std::string& path)
3075{
3076 std::vector<std::string> components;
3077 std::string component;
3078 std::istringstream stream(path);
3079
3080 while (std::getline(stream, component, '/')) {
3081 // Keep empty first component to indicate absolute path
3082 if (!component.empty() || components.empty()) {
3083 components.push_back(component);
3084 }
3085 }
3086
3087 return components;
3088}
3089
3090//=============================================================================
3091// Find attribute with case-insensitive name matching
3092//=============================================================================
3112std::string nxH5::PNeXus::FindAttributeName(H5::H5File& obj, const std::string& requestedName)
3113{
3114 int numAttrs = obj.getNumAttrs();
3115
3116 for (int i = 0; i < numAttrs; i++) {
3117 H5::Attribute attr = obj.openAttribute(i);
3118 std::string attrName = attr.getName();
3119 attr.close();
3120
3121 if (CaseInsensitiveEquals(attrName, requestedName)) {
3122 return attrName;
3123 }
3124 }
3125
3126 // Not found - throw exception
3127 std::ostringstream msg;
3128 msg << "Could not find attribute matching case-insensitive name: '" << requestedName << "'";
3129 throw H5::AttributeIException("FindAttributeName", msg.str());
3130}
3131
3132//=============================================================================
3133// Find group path with case-insensitive matching (File overload)
3134//=============================================================================
3153std::string nxH5::PNeXus::FindGroupPath(H5::H5File& parent, const std::string& requestedPath)
3154{
3155 std::vector<std::string> components = SplitPath(requestedPath);
3156
3157 // Handle empty path
3158 if (components.empty()) {
3159 return "/";
3160 }
3161
3162 // Check if absolute path
3163 bool isAbsolute = (components[0].empty());
3164 std::string currentPath = isAbsolute ? "" : "";
3165
3166 // Start from index 1 if absolute path (skip empty component)
3167 size_t startIdx = isAbsolute ? 1 : 0;
3168
3169 // Traverse path components
3170 for (size_t i = startIdx; i < components.size(); ++i) {
3171 const std::string& requestedComponent = components[i];
3172
3173 // Build the parent path for querying
3174 std::string parentPath = (currentPath.empty() || currentPath == "") ? "/" : currentPath;
3175 H5::Group currentGroup = (parentPath == "/") ? parent.openGroup("/") : parent.openGroup(parentPath);
3176
3177 // List object names using iteration
3178 hsize_t numObjs = currentGroup.getNumObjs();
3179 std::vector<std::string> objectNames;
3180 for (hsize_t j = 0; j < numObjs; j++) {
3181 objectNames.push_back(currentGroup.getObjnameByIdx(j));
3182 }
3183
3184 // Find case-insensitive match
3185 std::string matchedName;
3186 bool found = false;
3187
3188 for (const auto& objName : objectNames) {
3189 if (CaseInsensitiveEquals(objName, requestedComponent)) {
3190 matchedName = objName;
3191 found = true;
3192 break;
3193 }
3194 }
3195
3196 if (!found) {
3197 std::ostringstream msg;
3198 msg << "Could not find group component matching case-insensitive path: '"
3199 << requestedPath << "' (failed at component: '" << requestedComponent << "')";
3200 throw H5::GroupIException("FindGroupPath", msg.str());
3201 }
3202
3203 // Build current path
3204 currentPath += "/" + matchedName;
3205
3206 // Verify it's a group
3207 H5O_type_t objType = currentGroup.childObjType(matchedName);
3208 if (objType != H5O_TYPE_GROUP) {
3209 std::ostringstream msg;
3210 msg << "Path component '" << matchedName << "' in '" << requestedPath
3211 << "' is not a group";
3212 throw H5::GroupIException("FindGroupPath", msg.str());
3213 }
3214 }
3215
3216 return currentPath;
3217}
3218
3219//-----------------------------------------------------------------------------
3220// Find group path with case-insensitive matching (Group overload)
3221//-----------------------------------------------------------------------------
3222std::string nxH5::PNeXus::FindGroupPath(H5::Group& parent, const std::string& requestedPath)
3223{
3224 std::vector<std::string> components = SplitPath(requestedPath);
3225
3226 // Handle empty path
3227 if (components.empty()) {
3228 return "/";
3229 }
3230
3231 // Check if absolute path
3232 bool isAbsolute = (components[0].empty());
3233 std::string currentPath = isAbsolute ? "" : "";
3234 H5::Group currentGroup = parent;
3235
3236 // Start from index 1 if absolute path (skip empty component)
3237 size_t startIdx = isAbsolute ? 1 : 0;
3238
3239 // Traverse path components
3240 for (size_t i = startIdx; i < components.size(); ++i) {
3241 const std::string& requestedComponent = components[i];
3242
3243 // List object names using iteration
3244 hsize_t numObjs = currentGroup.getNumObjs();
3245 std::vector<std::string> objectNames;
3246 for (hsize_t j = 0; j < numObjs; j++) {
3247 objectNames.push_back(currentGroup.getObjnameByIdx(j));
3248 }
3249
3250 // Find case-insensitive match
3251 std::string matchedName;
3252 bool found = false;
3253
3254 for (const auto& objName : objectNames) {
3255 if (CaseInsensitiveEquals(objName, requestedComponent)) {
3256 matchedName = objName;
3257 found = true;
3258 break;
3259 }
3260 }
3261
3262 if (!found) {
3263 std::ostringstream msg;
3264 msg << "Could not find group component matching case-insensitive path: '"
3265 << requestedPath << "' (failed at component: '" << requestedComponent << "')";
3266 throw H5::GroupIException("FindGroupPath", msg.str());
3267 }
3268
3269 // Build current path
3270 currentPath += "/" + matchedName;
3271
3272 // Verify it's a group (unless it's the last component, which is checked by caller)
3273 H5O_type_t objType = currentGroup.childObjType(matchedName);
3274 if (objType != H5O_TYPE_GROUP) {
3275 std::ostringstream msg;
3276 msg << "Path component '" << matchedName << "' in '" << requestedPath
3277 << "' is not a group";
3278 throw H5::GroupIException("FindGroupPath", msg.str());
3279 }
3280
3281 // Open the group for next iteration
3282 if (i < components.size() - 1) {
3283 currentGroup = currentGroup.openGroup(matchedName);
3284 }
3285 }
3286
3287 return currentPath;
3288}
3289
3290//=============================================================================
3291// Find dataset path with case-insensitive matching (File overload)
3292//=============================================================================
3314std::string nxH5::PNeXus::FindDatasetPath(H5::H5File& parent, const std::string& requestedPath)
3315{
3316 std::vector<std::string> components = SplitPath(requestedPath);
3317
3318 // Handle empty path
3319 if (components.empty()) {
3320 throw H5::DataSetIException("FindDatasetPath", "Empty dataset path provided");
3321 }
3322
3323 // Check if absolute path
3324 bool isAbsolute = (components[0].empty());
3325 std::string currentPath = isAbsolute ? "" : "";
3326
3327 // Start from index 1 if absolute path (skip empty component)
3328 size_t startIdx = isAbsolute ? 1 : 0;
3329
3330 // Traverse path components
3331 for (size_t i = startIdx; i < components.size(); ++i) {
3332 const std::string& requestedComponent = components[i];
3333
3334 // Build the parent path for querying
3335 std::string parentPath = (currentPath.empty() || currentPath == "") ? "/" : currentPath;
3336 H5::Group currentGroup = (parentPath == "/") ? parent.openGroup("/") : parent.openGroup(parentPath);
3337
3338 // List object names using iteration
3339 hsize_t numObjs = currentGroup.getNumObjs();
3340 std::vector<std::string> objectNames;
3341 for (hsize_t j = 0; j < numObjs; j++) {
3342 objectNames.push_back(currentGroup.getObjnameByIdx(j));
3343 }
3344
3345 // Find case-insensitive match
3346 std::string matchedName;
3347 bool found = false;
3348
3349 for (const auto& objName : objectNames) {
3350 if (CaseInsensitiveEquals(objName, requestedComponent)) {
3351 matchedName = objName;
3352 found = true;
3353 break;
3354 }
3355 }
3356
3357 if (!found) {
3358 std::ostringstream msg;
3359 msg << "Could not find dataset matching case-insensitive path: '"
3360 << requestedPath << "' (failed at component: '" << requestedComponent << "')";
3361 throw H5::DataSetIException("FindDatasetPath", msg.str());
3362 }
3363
3364 // Build current path
3365 currentPath += "/" + matchedName;
3366
3367 // Check if this is the last component (should be a dataset)
3368 if (i == components.size() - 1) {
3369 H5O_type_t objType = currentGroup.childObjType(matchedName);
3370 if (objType != H5O_TYPE_DATASET) {
3371 std::ostringstream msg;
3372 msg << "Path '" << requestedPath << "' resolves to '" << currentPath
3373 << "' which is not a dataset";
3374 throw H5::DataSetIException("FindDatasetPath", msg.str());
3375 }
3376 } else {
3377 // Intermediate component - should be a group
3378 H5O_type_t objType = currentGroup.childObjType(matchedName);
3379 if (objType != H5O_TYPE_GROUP) {
3380 std::ostringstream msg;
3381 msg << "Path component '" << matchedName << "' in '" << requestedPath
3382 << "' is not a group";
3383 throw H5::DataSetIException("FindDatasetPath", msg.str());
3384 }
3385 }
3386 }
3387
3388 return currentPath;
3389}
3390
3391//-----------------------------------------------------------------------------
3392// Find dataset path with case-insensitive matching (Group overload)
3393//-----------------------------------------------------------------------------
3394std::string nxH5::PNeXus::FindDatasetPath(H5::Group& parent, const std::string& requestedPath)
3395{
3396 std::vector<std::string> components = SplitPath(requestedPath);
3397
3398 // Handle empty path
3399 if (components.empty()) {
3400 throw H5::DataSetIException("FindDatasetPath", "Empty dataset path provided");
3401 }
3402
3403 // Check if absolute path
3404 bool isAbsolute = (components[0].empty());
3405 std::string currentPath = isAbsolute ? "" : "";
3406 H5::Group currentGroup = parent;
3407
3408 // Start from index 1 if absolute path (skip empty component)
3409 size_t startIdx = isAbsolute ? 1 : 0;
3410
3411 // Traverse path components
3412 for (size_t i = startIdx; i < components.size(); ++i) {
3413 const std::string& requestedComponent = components[i];
3414
3415 // List object names using iteration
3416 hsize_t numObjs = currentGroup.getNumObjs();
3417 std::vector<std::string> objectNames;
3418 for (hsize_t j = 0; j < numObjs; j++) {
3419 objectNames.push_back(currentGroup.getObjnameByIdx(j));
3420 }
3421
3422 // Find case-insensitive match
3423 std::string matchedName;
3424 bool found = false;
3425
3426 for (const auto& objName : objectNames) {
3427 if (CaseInsensitiveEquals(objName, requestedComponent)) {
3428 matchedName = objName;
3429 found = true;
3430 break;
3431 }
3432 }
3433
3434 if (!found) {
3435 std::ostringstream msg;
3436 msg << "Could not find dataset matching case-insensitive path: '"
3437 << requestedPath << "' (failed at component: '" << requestedComponent << "')";
3438 throw H5::DataSetIException("FindDatasetPath", msg.str());
3439 }
3440
3441 // Build current path
3442 currentPath += "/" + matchedName;
3443
3444 // Check if this is the last component (should be a dataset)
3445 if (i == components.size() - 1) {
3446 H5O_type_t objType = currentGroup.childObjType(matchedName);
3447 if (objType != H5O_TYPE_DATASET) {
3448 std::ostringstream msg;
3449 msg << "Path '" << requestedPath << "' resolves to '" << currentPath
3450 << "' which is not a dataset";
3451 throw H5::DataSetIException("FindDatasetPath", msg.str());
3452 }
3453 } else {
3454 // Intermediate component - should be a group
3455 H5O_type_t objType = currentGroup.childObjType(matchedName);
3456 if (objType != H5O_TYPE_GROUP) {
3457 std::ostringstream msg;
3458 msg << "Path component '" << matchedName << "' in '" << requestedPath
3459 << "' is not a group";
3460 throw H5::DataSetIException("FindDatasetPath", msg.str());
3461 }
3462 // Open the group for next iteration
3463 currentGroup = currentGroup.openGroup(matchedName);
3464 }
3465 }
3466
3467 return currentPath;
3468}
3469
3470//=============================================================================
3471// nxH5::PNeXus Constructor
3472//=============================================================================
3474{
3475 unsigned majnum, minnum, relnum;
3476 herr_t status;
3477 std::stringstream ss;
3478
3479 status = H5get_libversion(&majnum, &minnum, &relnum);
3480 if (status >= 0) {
3481 ss << majnum << "." << minnum << "." << relnum;
3482 }
3483 fHdf5LibVersion = ss.str();
3484}
3485
3486//=============================================================================
3487// nxH5::PNeXus Constructor
3488//=============================================================================
3512nxH5::PNeXus::PNeXus(const std::string fln, const bool printDebug) : fFileName(fln), fPrintDebug(printDebug)
3513{
3514 unsigned majnum, minnum, relnum;
3515 herr_t status;
3516 std::stringstream ss;
3517
3518 status = H5get_libversion(&majnum, &minnum, &relnum);
3519 if (status >= 0) {
3520 ss << majnum << "." << minnum << "." << relnum;
3521 }
3522 fHdf5LibVersion = ss.str();
3523
3524 ReadNexusFile();
3525}
3526
3527
3528//=============================================================================
3529// Read NeXus File
3530//=============================================================================
3554{
3555 try {
3556 // Open the HDF5/NeXus file
3557 H5::H5File file(fFileName, H5F_ACC_RDONLY);
3558
3559 // Turn off the auto-printing when failure occurs so that we can
3560 // handle the errors appropriately
3561 H5::Exception::dontPrint();
3562
3563 // Load NeXus version attribute (with case-insensitive lookup)
3564 std::string version;
3565 try {
3566 std::string versionNeXusAttr = FindAttributeName(file, "NeXus_version");
3567 H5::Attribute attr = file.openAttribute(versionNeXusAttr);
3568 H5::DataType dtype = attr.getDataType();
3569 attr.read(dtype, version);
3570 attr.close();
3571 fNeXusVersion = version;
3572 if (fPrintDebug)
3573 std::cout << "debug> NeXus version=" << fNeXusVersion << std::endl;
3574 } catch (const H5::AttributeIException& err) {
3575 std::cerr << "Error: Failed to read NeXus_version attribute: " << err.getDetailMsg() << std::endl;
3576 return 1;
3577 }
3578
3579 // Load IDF version from dataset (with case-insensitive lookup)
3580 int idf_vers{-1};
3581 try {
3582 std::string idfPath = FindDatasetPath(file, "/run/IDF_version");
3583 H5::DataSet dataset = file.openDataSet(idfPath);
3584 dataset.read(&idf_vers, H5::PredType::NATIVE_INT);
3585 dataset.close();
3586 fIdfVersion = idf_vers;
3587 } catch (const H5::DataSetIException& err) {
3588 if (fPrintDebug)
3589 std::cout << "Info: not IDF version 1, will check for IDF version 2" << std::endl;
3590 }
3591
3592 if (fIdfVersion == -1) {
3593 try {
3594 std::string idfPath = FindDatasetPath(file, "/raw_data_1/IDF_version");
3595 H5::DataSet dataset = file.openDataSet(idfPath);
3596 dataset.read(&idf_vers, H5::PredType::NATIVE_INT);
3597 dataset.close();
3598 fIdfVersion = idf_vers;
3599 } catch (const H5::DataSetIException& err) {
3600 std::cerr << "Error: Failed to read IDF_version dataset: " << err.getDetailMsg() << std::endl;
3601 return 1;
3602 }
3603
3604 try {
3605 std::string versionHdf5Attr = FindAttributeName(file, "HDF5_version");
3606 H5::Attribute attr = file.openAttribute(versionHdf5Attr);
3607 H5::DataType dtype = attr.getDataType();
3608 attr.read(dtype, version);
3609 attr.close();
3610 fHdf5Version = version;
3611 if (fPrintDebug)
3612 std::cout << "debug> HDF5 version=" << fHdf5Version << std::endl;
3613 } catch (const H5::AttributeIException& err) {
3614 std::cerr << "Error: Failed to read HDF5_version attribute: " << err.getDetailMsg() << std::endl;
3615 return 1;
3616 }
3617 }
3618
3619 if (fPrintDebug)
3620 std::cout << "debug> IDF_version=" << fIdfVersion << std::endl;
3621
3622 if (fIdfVersion == 1) {
3623 try {
3624 HandleIdfV1(file);
3625 } catch (...) {
3626
3627 }
3628 } else { // fIdfVersion == 2
3629 try {
3630 HandleIdfV2(file);
3631 } catch (...) {
3632
3633 }
3634 }
3635
3636 return 0;
3637
3638 } catch (const H5::FileIException& err) {
3639 std::cerr << "Error: Failed to open file '" << fFileName << "': " << err.getDetailMsg() << std::endl;
3640 return 1;
3641 } catch (const H5::Exception& err) {
3642 std::cerr << "Error: HDF5 exception occurred: " << err.getDetailMsg() << std::endl;
3643 return 1;
3644 } catch (const std::exception& err) {
3645 std::cerr << "Error: Unexpected exception: " << err.what() << std::endl;
3646 return 1;
3647 }
3648}
3649
3650//=============================================================================
3651// Handle NeXus IDF version 1 file
3652//=============================================================================
3653void nxH5::PNeXus::HandleIdfV1(H5::H5File &file)
3654{
3655 if (fPrintDebug)
3656 std::cout << "debug> in HandleIdfV1 ..." << std::endl;
3657
3658 std::string attrStr{""}, attrStrName{""};
3659 // get file_name attribute
3660 try {
3661 attrStrName = FindAttributeName(file, "user");
3662 H5::Attribute attr = file.openAttribute(attrStrName);
3663 H5::DataType dtype = attr.getDataType();
3664 attr.read(dtype, attrStr);
3665 attr.close();
3666 fUserV1 = attrStr;
3667 if (fPrintDebug)
3668 std::cout << "debug> user =" << fUserV1 << std::endl;
3669 } catch (const H5::AttributeIException& err) {
3670 std::cerr << "Error: Failed to read user attribute: " << err.getDetailMsg() << std::endl;
3671 }
3672
3673 // Read the mandatory key datasets and store them in the data map
3674 try {
3675 ReadStringDataset(file, "/run/program_name");
3676 ReadIntDataset(file, "/run/number");
3677 ReadStringDataset(file, "/run/title");
3678 ReadStringDataset(file, "/run/notes");
3679 ReadStringDataset(file, "/run/analysis");
3680 ReadStringDataset(file, "/run/lab");
3681 ReadStringDataset(file, "/run/beamline");
3682 ReadStringDataset(file, "/run/start_time");
3683 ReadStringDataset(file, "/run/stop_time");
3684 ReadIntDataset(file, "/run/switching_states");
3685 ReadStringDataset(file, "/run/user/name");
3686 ReadStringDataset(file, "/run/user/experiment_number");
3687 ReadStringDataset(file, "/run/sample/name");
3688 ReadFloatDataset(file, "/run/sample/temperature");
3689 ReadFloatDataset(file, "/run/sample/magnetic_field");
3690 ReadStringDataset(file, "/run/sample/magnetic_field_state");
3691 ReadFloatDataset(file, "/run/sample/magnetic_field_vector");
3692 ReadStringDataset(file, "/run/sample/environment");
3693 ReadStringDataset(file, "/run/instrument/name");
3694 ReadIntDataset(file, "/run/instrument/detector/number");
3695 ReadFloatDataset(file, "/run/instrument/detector/deadtimes");
3696 ReadStringDataset(file, "/run/instrument/collimator/type");
3697 ReadStringDataset(file, "/run/instrument/beam/beamline");
3698 ReadIntDataset(file, "/run/instrument/beam/frames_good");
3699 ReadIntDataset(file, "/run/histogram_data_1/counts");
3700 ReadIntDataset(file, "/run/histogram_data_1/resolution");
3701 ReadIntDataset(file, "/run/histogram_data_1/time_zero");
3702 ReadFloatDataset(file, "/run/histogram_data_1/raw_time");
3703 ReadFloatDataset(file, "/run/histogram_data_1/corrected_time");
3704 ReadIntDataset(file, "/run/histogram_data_1/grouping");
3705 ReadFloatDataset(file, "/run/histogram_data_1/alpha");
3706 } catch (const H5::Exception& err) {
3707 std::cerr << "Error in HandleIdfV1: " << err.getDetailMsg() << std::endl;
3708 throw;
3709 }
3710}
3711
3712//=============================================================================
3713// Handle NeXus IDF version 2 file
3714//=============================================================================
3715void nxH5::PNeXus::HandleIdfV2(H5::H5File &file)
3716{
3717 if (fPrintDebug)
3718 std::cout << "debug> in HandleIdfV2 ..." << std::endl;
3719
3720 std::string attrStr{""}, attrStrName{""};
3721 // get file_name attribute
3722 try {
3723 attrStrName = FindAttributeName(file, "file_name");
3724 H5::Attribute attr = file.openAttribute(attrStrName);
3725 H5::DataType dtype = attr.getDataType();
3726 attr.read(dtype, attrStr);
3727 attr.close();
3728 fFileNameNxs = attrStr;
3729 if (fPrintDebug)
3730 std::cout << "debug> NXS file_name =" << fFileNameNxs << std::endl;
3731 } catch (const H5::AttributeIException& err) {
3732 std::cerr << "Error: Failed to read file_name attribute: " << err.getDetailMsg() << std::endl;
3733 }
3734
3735 // get file_time attribute
3736 attrStr="";
3737 try {
3738 attrStrName = FindAttributeName(file, "file_time");
3739 H5::Attribute attr = file.openAttribute(attrStrName);
3740 H5::DataType dtype = attr.getDataType();
3741 attr.read(dtype, attrStr);
3742 attr.close();
3743 fFileTimeNxs = attrStr;
3744 if (fPrintDebug)
3745 std::cout << "debug> NXS file_time =" << fFileNameNxs << std::endl;
3746 } catch (const H5::AttributeIException& err) {
3747 std::cerr << "Error: Failed to read file_time attribute: " << err.getDetailMsg() << std::endl;
3748 }
3749
3750 // Read the mandatory key datasets and store them in the data map
3751 try {
3752 ReadIntDataset(file, "/raw_data_1/IDF_version");
3753 ReadStringDataset(file, "/raw_data_1/beamline");
3754 ReadStringDataset(file, "/raw_data_1/definition");
3755 ReadIntDataset(file, "/raw_data_1/good_frames");
3756 ReadIntDataset(file, "/raw_data_1/run_number");
3757 ReadStringDataset(file, "/raw_data_1/title");
3758 ReadStringDataset(file, "/raw_data_1/start_time");
3759 ReadStringDataset(file, "/raw_data_1/end_time");
3760 ReadStringDataset(file, "/raw_data_1/experiment_identifier");
3761 ReadStringDataset(file, "/raw_data_1/instrument/name");
3762 ReadStringDataset(file, "/raw_data_1/instrument/source/name");
3763 ReadStringDataset(file, "/raw_data_1/instrument/source/type");
3764 ReadStringDataset(file, "/raw_data_1/instrument/source/probe");
3765 ReadIntDataset(file, "/raw_data_1/instrument/detector_1/resolution");
3766 ReadIntDataset(file, "/raw_data_1/instrument/detector_1/counts");
3767 ReadFloatDataset(file, "/raw_data_1/instrument/detector_1/raw_time");
3768 ReadIntDataset(file, "/raw_data_1/instrument/detector_1/spectrum_index");
3769 ReadFloatDataset(file, "/raw_data_1/instrument/detector_1/dead_time");
3770 ReadStringDataset(file, "/raw_data_1/sample/name");
3771 ReadFloatDataset(file, "/raw_data_1/sample/temperature");
3772 ReadFloatDataset(file, "/raw_data_1/sample/magnetic_field");
3773 ReadStringDataset(file, "/raw_data_1/sample/shape");
3774 } catch (const H5::Exception& err) {
3775 std::cerr << "Error in HandleIdfV2: " << err.getDetailMsg() << std::endl;
3776 throw;
3777 }
3778}
3779
3780//=============================================================================
3781// Dump hdf5-NeXus file content which was read
3782//=============================================================================
3784{
3785 int first_good_bin{0};
3786
3787 if (fIdfVersion == 1) {
3788 std::cout << std::endl;
3789 std::cout << std::endl << "hdf5-NeXus file content of file:' " << fFileName << "'"; std::cout << std::endl << "****";
3790 std::cout << std::endl << "****";
3791 std::cout << std::endl << "Top Level Attributes:";
3792 std::cout << std::endl << " NeXus Version: " << fNeXusVersion;
3793 std::cout << std::endl << " user: " << fUserV1;
3794 std::cout << std::endl << "++++";
3795 std::cout << std::endl << "run";
3796 std::cout << std::endl << "----";
3797 std::cout << std::endl << " IDF_version: " << fIdfVersion;
3798 if (fDataMap.find("/run/program_name") != fDataMap.end()) {
3799 try {
3800 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/program_name"]);
3801 std::cout << std::endl << " program_name : " << str_data.GetData()[0];
3802
3803 // Check for attributes
3804 if (str_data.HasAttribute("version")) {
3805 try {
3806 auto version = std::any_cast<std::string>(str_data.GetAttribute("version"));
3807 std::cout << " version : " << version << std::endl;
3808 } catch (const std::bad_any_cast& e) {
3809 std::cerr << "Error: Failed to cast version attribute" << std::endl;
3810 }
3811 }
3812 } catch (const std::bad_any_cast& e) {
3813 std::cerr << std::endl << "Error: Failed to cast program_name data" << std::endl;
3814 }
3815 } else {
3816 std::cout << std::endl << " program_name : n/a";
3817 }
3818
3819 if (fDataMap.find("/run/number") != fDataMap.end()) {
3820 try {
3821 auto number = std::any_cast<PNXdata<int>>(fDataMap["/run/number"]);
3822 std::cout << std::endl << " number : " << number.GetData()[0];
3823 } catch (const std::bad_any_cast& e) {
3824 std::cerr << std::endl << "Error: Failed to cast number data" << std::endl;
3825 }
3826 } else {
3827 std::cout << std::endl << " number : n/a";
3828 }
3829
3830 if (fDataMap.find("/run/title") != fDataMap.end()) {
3831 try {
3832 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/title"]);
3833 std::cout << std::endl << " title : " << str_data.GetData()[0];
3834 } catch (const std::bad_any_cast& e) {
3835 std::cerr << std::endl << "Error: Failed to cast title data" << std::endl;
3836 }
3837 } else {
3838 std::cout << std::endl << " title : n/a";
3839 }
3840
3841 if (fDataMap.find("/run/notes") != fDataMap.end()) {
3842 try {
3843 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/notes"]);
3844 std::cout << std::endl << " notes : " << str_data.GetData()[0];
3845 } catch (const std::bad_any_cast& e) {
3846 std::cerr << std::endl << "Error: Failed to cast notes data" << std::endl;
3847 }
3848 } else {
3849 std::cout << std::endl << " notes : n/a";
3850 }
3851
3852 if (fDataMap.find("/run/analysis") != fDataMap.end()) {
3853 try {
3854 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/analysis"]);
3855 std::cout << std::endl << " analysis : " << str_data.GetData()[0];
3856 } catch (const std::bad_any_cast& e) {
3857 std::cerr << std::endl << "Error: Failed to cast analysis data" << std::endl;
3858 }
3859 } else {
3860 std::cout << std::endl << " analysis : n/a";
3861 }
3862
3863 if (fDataMap.find("/run/lab") != fDataMap.end()) {
3864 try {
3865 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/lab"]);
3866 std::cout << std::endl << " lab : " << str_data.GetData()[0];
3867 } catch (const std::bad_any_cast& e) {
3868 std::cerr << std::endl << "Error: Failed to cast lab data" << std::endl;
3869 }
3870 } else {
3871 std::cout << std::endl << " lab : n/a";
3872 }
3873
3874 if (fDataMap.find("/run/beamline") != fDataMap.end()) {
3875 try {
3876 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/beamline"]);
3877 std::cout << std::endl << " beamline : " << str_data.GetData()[0];
3878 } catch (const std::bad_any_cast& e) {
3879 std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
3880 }
3881 } else {
3882 std::cout << std::endl << " beamline : n/a";
3883 }
3884
3885 if (fDataMap.find("/run/start_time") != fDataMap.end()) {
3886 try {
3887 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/start_time"]);
3888 std::cout << std::endl << " start_time : " << str_data.GetData()[0];
3889 } catch (const std::bad_any_cast& e) {
3890 std::cerr << std::endl << "Error: Failed to cast start_time data" << std::endl;
3891 }
3892 } else {
3893 std::cout << std::endl << " start_time : n/a";
3894 }
3895
3896 if (fDataMap.find("/run/end_time") != fDataMap.end()) {
3897 try {
3898 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/end_time"]);
3899 std::cout << std::endl << " end_time : " << str_data.GetData()[0];
3900 } catch (const std::bad_any_cast& e) {
3901 std::cerr << std::endl << "Error: Failed to cast end_time data" << std::endl;
3902 }
3903 } else {
3904 std::cout << std::endl << " end_time : n/a";
3905 }
3906
3907 if (fDataMap.find("/run/switching_state") != fDataMap.end()) {
3908 try {
3909 auto int_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/switching_state"]);
3910 std::cout << std::endl << " switching_state : " << int_data.GetData()[0];
3911 } catch (const std::bad_any_cast& e) {
3912 std::cerr << std::endl << "Error: Failed to cast switching_state data" << std::endl;
3913 }
3914 } else {
3915 std::cout << std::endl << " switching_state : n/a";
3916 }
3917
3918 std::cout << std::endl << " user";
3919 std::cout << std::endl << "----";
3920 if (fDataMap.find("/run/usr/name") != fDataMap.end()) {
3921 try {
3922 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/name"]);
3923 std::cout << std::endl << " name : " << str_data.GetData()[0];
3924 } catch (const std::bad_any_cast& e) {
3925 std::cerr << std::endl << "Error: Failed to cast name data" << std::endl;
3926 }
3927 } else {
3928 std::cout << std::endl << " name : n/a";
3929 }
3930
3931 if (fDataMap.find("/run/usr/experiment_number") != fDataMap.end()) {
3932 try {
3933 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/experiment_number"]);
3934 std::cout << std::endl << " experiment_number : " << str_data.GetData()[0];
3935 } catch (const std::bad_any_cast& e) {
3936 std::cerr << std::endl << "Error: Failed to cast experiment_number data" << std::endl;
3937 }
3938 } else {
3939 std::cout << std::endl << " experiment_number : n/a";
3940 }
3941
3942 std::cout << std::endl << " sample";
3943 std::cout << std::endl << "----";
3944 if (fDataMap.find("/run/sample/name") != fDataMap.end()) {
3945 try {
3946 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/name"]);
3947 std::cout << std::endl << " name : " << str_data.GetData()[0];
3948 } catch (const std::bad_any_cast& e) {
3949 std::cerr << std::endl << "Error: Failed to cast name data" << std::endl;
3950 }
3951 } else {
3952 std::cout << std::endl << " name : n/a";
3953 }
3954
3955 if (fDataMap.find("/run/sample/temperature") != fDataMap.end()) {
3956 try {
3957 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/temperature"]);
3958 std::cout << std::endl << " temperature : " << float_data.GetData()[0];
3959
3960 // Check for attributes
3961 if (float_data.HasAttribute("units")) {
3962 try {
3963 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
3964 std::cout << " units : " << units << std::endl;
3965 } catch (const std::bad_any_cast& e) {
3966 std::cerr << "Error: Failed to cast units attribute" << std::endl;
3967 }
3968 }
3969 } catch (const std::bad_any_cast& e) {
3970 std::cerr << std::endl << "Error: Failed to cast temperature data" << std::endl;
3971 }
3972 } else {
3973 std::cout << std::endl << " temperature : n/a";
3974 }
3975
3976 if (fDataMap.find("/run/sample/magnetic_field") != fDataMap.end()) {
3977 try {
3978 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field"]);
3979 std::cout << std::endl << " magnetic_field : " << float_data.GetData()[0];
3980
3981 // Check for attributes
3982 if (float_data.HasAttribute("units")) {
3983 try {
3984 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
3985 std::cout << " units : " << units << std::endl;
3986 } catch (const std::bad_any_cast& e) {
3987 std::cerr << "Error: Failed to cast units attribute" << std::endl;
3988 }
3989 }
3990 } catch (const std::bad_any_cast& e) {
3991 std::cerr << std::endl << "Error: Failed to cast magnetic_field data" << std::endl;
3992 }
3993 } else {
3994 std::cout << std::endl << " magnetic_field : n/a";
3995 }
3996
3997 if (fDataMap.find("/run/sample/magnetic_field_vector") != fDataMap.end()) {
3998 try {
3999 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field_vector"]);
4000 std::cout << std::endl << " magnetic_field_vector : " << float_data.GetData()[0];
4001
4002 // Check for attributes
4003 if (float_data.HasAttribute("coordinate_system")) {
4004 try {
4005 auto coordinate_system = std::any_cast<std::string>(float_data.GetAttribute("coordinate_system"));
4006 std::cout << " coordinate_system : " << coordinate_system << std::endl;
4007 } catch (const std::bad_any_cast& e) {
4008 std::cerr << "Error: Failed to cast coordinate_system attribute" << std::endl;
4009 }
4010 }
4011 if (float_data.HasAttribute("units")) {
4012 try {
4013 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
4014 std::cout << " units : " << units << std::endl;
4015 } catch (const std::bad_any_cast& e) {
4016 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4017 }
4018 }
4019 if (float_data.HasAttribute("available")) {
4020 try {
4021 auto available = std::any_cast<int>(float_data.GetAttribute("available"));
4022 std::cout << " available : " << available << std::endl;
4023 } catch (const std::bad_any_cast& e) {
4024 std::cerr << "Error: Failed to cast available attribute" << std::endl;
4025 }
4026 }
4027 } catch (const std::bad_any_cast& e) {
4028 std::cerr << std::endl << "Error: Failed to cast magnetic_field_vector data" << std::endl;
4029 }
4030 } else {
4031 std::cout << std::endl << " magnetic_field_vector : n/a";
4032 }
4033
4034 if (fDataMap.find("/run/sample/environment") != fDataMap.end()) {
4035 try {
4036 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/environment"]);
4037 std::cout << std::endl << " environment : " << str_data.GetData()[0];
4038 } catch (const std::bad_any_cast& e) {
4039 std::cerr << std::endl << "Error: Failed to cast environment data" << std::endl;
4040 }
4041 } else {
4042 std::cout << std::endl << " environment : n/a";
4043 }
4044
4045 std::cout << std::endl << " instrument";
4046 std::cout << std::endl << "----";
4047 if (fDataMap.find("/run/instrument/name") != fDataMap.end()) {
4048 try {
4049 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/name"]);
4050 std::cout << std::endl << " name : " << str_data.GetData()[0];
4051 } catch (const std::bad_any_cast& e) {
4052 std::cerr << std::endl << "Error: Failed to cast name data" << std::endl;
4053 }
4054 } else {
4055 std::cout << std::endl << " name : n/a";
4056 }
4057
4058 std::cout << std::endl << " detector";
4059 std::cout << std::endl << "----";
4060 if (fDataMap.find("/run/instrument/detector/number") != fDataMap.end()) {
4061 try {
4062 auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/instrument/detector/number"]);
4063 std::cout << std::endl << " number : " << int_data.GetData()[0];
4064 } catch (const std::bad_any_cast& e) {
4065 std::cerr << std::endl << "Error: Failed to cast number data" << std::endl;
4066 }
4067 } else {
4068 std::cout << std::endl << " number : n/a";
4069 }
4070
4071 if (fDataMap.find("/run/instrument/detector/deadtimes") != fDataMap.end()) {
4072 try {
4073 const auto& dead_times = std::any_cast<PNXdata<float>>("/run/instrument/detector/deadtimes").GetData();
4074 std::cout << std::endl << " deadtimes: ";
4075 for (unsigned int i=0; i<10; i++)
4076 std::cout << dead_times[i] << ", ";
4077 std::cout << "...";
4078 } catch (const std::bad_any_cast& e) {
4079 std::cerr << std::endl << "Error: Failed to cast dead time data" << std::endl;
4080 }
4081 } else {
4082 std::cout << std::endl << " deadtimes: n/a";
4083 }
4084
4085 std::cout << std::endl << " collimator";
4086 std::cout << std::endl << "----";
4087 if (fDataMap.find("/run/instrument/collimator/type") != fDataMap.end()) {
4088 try {
4089 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/collimator/type"]);
4090 std::cout << std::endl << " type : " << str_data.GetData()[0];
4091 } catch (const std::bad_any_cast& e) {
4092 std::cerr << std::endl << "Error: Failed to cast type data" << std::endl;
4093 }
4094 } else {
4095 std::cout << std::endl << " type : n/a";
4096 }
4097
4098 std::cout << std::endl << " beam";
4099 std::cout << std::endl << "----";
4100 if (fDataMap.find("/run/instrument/beam/beamline") != fDataMap.end()) {
4101 try {
4102 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/beam/beamline"]);
4103 std::cout << std::endl << " beamline : " << str_data.GetData()[0];
4104 } catch (const std::bad_any_cast& e) {
4105 std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
4106 }
4107 } else {
4108 std::cout << std::endl << " beamline : n/a";
4109 }
4110
4111 if (fDataMap.find("/run/instrument/beam/frames_good") != fDataMap.end()) {
4112 try {
4113 auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/beam/frames_good"]);
4114 std::cout << std::endl << " frames_good : " << str_data.GetData()[0];
4115 } catch (const std::bad_any_cast& e) {
4116 std::cerr << std::endl << "Error: Failed to cast frames_good data" << std::endl;
4117 }
4118 } else {
4119 std::cout << std::endl << " frames_good : n/a";
4120 }
4121
4122 std::cout << std::endl << " histogram_data_1";
4123 std::cout << std::endl << "----";
4124 std::cout << std::endl << " counts:";
4125 std::cout << std::endl;
4126 try {
4127 auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/counts"]);
4128
4129 // Check for attributes
4130 if (counts_data.HasAttribute("units")) {
4131 try {
4132 auto units = std::any_cast<std::string>(counts_data.GetAttribute("units"));
4133 std::cout << " units : " << units << std::endl;
4134 } catch (const std::bad_any_cast& e) {
4135 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4136 }
4137 } else {
4138 std::cout << " units : n/a" << std::endl;
4139 }
4140
4141 if (counts_data.HasAttribute("signal")) {
4142 try {
4143 auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
4144 std::cout << " signal : " << signal << std::endl;
4145 } catch (const std::bad_any_cast& e) {
4146 std::cerr << "Error: Failed to cast signal attribute" << std::endl;
4147 }
4148 } else {
4149 std::cout << " signal : n/a" << std::endl;
4150 }
4151
4152 int noOfHistos{0};
4153 if (counts_data.HasAttribute("number")) {
4154 try {
4155 noOfHistos = std::any_cast<int>(counts_data.GetAttribute("number"));
4156 std::cout << " number : " << noOfHistos << std::endl;
4157 } catch (const std::bad_any_cast& e) {
4158 std::cerr << "Error: Failed to cast number attribute" << std::endl;
4159 }
4160 } else {
4161 std::cout << " number : n/a" << std::endl;
4162 }
4163
4164 int histoLength{0};
4165 if (counts_data.HasAttribute("length")) {
4166 try {
4167 histoLength = std::any_cast<int>(counts_data.GetAttribute("length"));
4168 std::cout << " length : " << noOfHistos << std::endl;
4169 } catch (const std::bad_any_cast& e) {
4170 std::cerr << "Error: Failed to cast length attribute" << std::endl;
4171 }
4172 } else {
4173 std::cout << " length : n/a" << std::endl;
4174 }
4175
4176 if (counts_data.HasAttribute("t0_bin")) {
4177 try {
4178 auto t0_bin = std::any_cast<int>(counts_data.GetAttribute("t0_bin"));
4179 std::cout << " t0_bin : " << t0_bin << std::endl;
4180 } catch (const std::bad_any_cast& e) {
4181 std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
4182 }
4183 } else {
4184 std::cout << " t0_bin : n/a" << std::endl;
4185 }
4186
4187 if (counts_data.HasAttribute("first_good_bin")) {
4188 try {
4189 first_good_bin = std::any_cast<int>(counts_data.GetAttribute("first_good_bin"));
4190 std::cout << " first_good_bin : " << first_good_bin << std::endl;
4191 } catch (const std::bad_any_cast& e) {
4192 std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
4193 }
4194 } else {
4195 std::cout << " first_good_bin : n/a" << std::endl;
4196 }
4197
4198 if (counts_data.HasAttribute("last_good_bin")) {
4199 try {
4200 auto last_good_bin = std::any_cast<int>(counts_data.GetAttribute("last_good_bin"));
4201 std::cout << " last_good_bin : " << last_good_bin << std::endl;
4202 } catch (const std::bad_any_cast& e) {
4203 std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
4204 }
4205 } else {
4206 std::cout << " last_good_bin : n/a" << std::endl;
4207 }
4208
4209 if (counts_data.HasAttribute("offset")) {
4210 try {
4211 auto offset = std::any_cast<float>(counts_data.GetAttribute("offset"));
4212 std::cout << " offset : " << offset << std::endl;
4213 } catch (const std::bad_any_cast& e) {
4214 std::cerr << "Error: Failed to cast offset attribute" << std::endl;
4215 }
4216 } else {
4217 std::cout << " offset : n/a" << std::endl;
4218 }
4219
4220 // dump the first couple of counts of each detector
4221 const auto& data = counts_data.GetData();
4222 std::cout << std::endl << " first couple of counts of each detector:";
4223 for (unsigned int i=0; i<noOfHistos; i++) {
4224 if (i<9)
4225 std::cout << std::endl << " " << i+1 << ": ";
4226 else
4227 std::cout << std::endl << " " << i+1 << ": ";
4228 for (unsigned int j=0; j<first_good_bin+5; j++)
4229 std::cout << data[i*histoLength+j] << ", ";
4230 std::cout << "...";
4231 }
4232 } catch (const std::bad_any_cast& e) {
4233 std::cerr << "Error: Failed to cast counts data" << std::endl;
4234 }
4235
4236 if (fDataMap.find("/run/histogram_data_1/resolution") != fDataMap.end()) {
4237 try {
4238 auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/resolution"]);
4239 std::cout << std::endl << " resolution : " << int_data.GetData()[0];
4240
4241 if (int_data.HasAttribute("units")) {
4242 try {
4243 auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
4244 std::cout << " units : " << units << std::endl;
4245 } catch (const std::bad_any_cast& e) {
4246 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4247 }
4248 } else {
4249 std::cout << " units : n/a" << std::endl;
4250 }
4251 } catch (const std::bad_any_cast& e) {
4252 std::cerr << std::endl << "Error: Failed to cast resolution data" << std::endl;
4253 }
4254 } else {
4255 std::cout << std::endl << " resolution : n/a";
4256 }
4257
4258 if (fDataMap.find("/run/histogram_data_1/time_zero") != fDataMap.end()) {
4259 try {
4260 auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/time_zero"]);
4261 std::cout << std::endl << " time_zero : " << int_data.GetData()[0];
4262
4263 if (int_data.HasAttribute("units")) {
4264 try {
4265 auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
4266 std::cout << " units : " << units << std::endl;
4267 } catch (const std::bad_any_cast& e) {
4268 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4269 }
4270 } else {
4271 std::cout << " units : n/a" << std::endl;
4272 }
4273
4274 if (int_data.HasAttribute("available")) {
4275 try {
4276 auto available = std::any_cast<int>(int_data.GetAttribute("available"));
4277 std::cout << " available : " << available << std::endl;
4278 } catch (const std::bad_any_cast& e) {
4279 std::cerr << "Error: Failed to cast available attribute" << std::endl;
4280 }
4281 } else {
4282 std::cout << " available : n/a" << std::endl;
4283 }
4284 } catch (const std::bad_any_cast& e) {
4285 std::cerr << std::endl << "Error: Failed to cast time_zero data" << std::endl;
4286 }
4287 } else {
4288 std::cout << std::endl << " time_zero : n/a";
4289 }
4290
4291 if (fDataMap.find("/run/histogram_data_1/raw_time") != fDataMap.end()) {
4292 try {
4293 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/raw_time"]);
4294 std::cout << std::endl << " raw_time : " << float_data.GetData()[0];
4295 if (float_data.HasAttribute("axis")) {
4296 try {
4297 auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
4298 std::cout << " axis : " << axis << std::endl;
4299 } catch (const std::bad_any_cast& e) {
4300 std::cerr << "Error: Failed to cast axis attribute" << std::endl;
4301 }
4302 } else {
4303 std::cout << " axis : n/a" << std::endl;
4304 }
4305
4306 if (float_data.HasAttribute("primary")) {
4307 try {
4308 auto primary = std::any_cast<int>(float_data.GetAttribute("primary"));
4309 std::cout << " primary : " << primary << std::endl;
4310 } catch (const std::bad_any_cast& e) {
4311 std::cerr << "Error: Failed to cast primary attribute" << std::endl;
4312 }
4313 } else {
4314 std::cout << " primary : n/a" << std::endl;
4315 }
4316
4317 if (float_data.HasAttribute("units")) {
4318 try {
4319 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
4320 std::cout << " units : " << units << std::endl;
4321 } catch (const std::bad_any_cast& e) {
4322 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4323 }
4324 } else {
4325 std::cout << " units : n/a" << std::endl;
4326 }
4327 } catch (const std::bad_any_cast& e) {
4328 std::cerr << std::endl << "Error: Failed to cast raw_time data" << std::endl;
4329 }
4330 } else {
4331 std::cout << std::endl << " raw_time : n/a";
4332 }
4333
4334 if (fDataMap.find("/run/histogram_data_1/corrected_time") != fDataMap.end()) {
4335 try {
4336 auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/corrected_time"]);
4337 std::cout << std::endl << " corrected_time : " << float_data.GetData()[0];
4338 if (float_data.HasAttribute("axis")) {
4339 try {
4340 auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
4341 std::cout << " axis : " << axis << std::endl;
4342 } catch (const std::bad_any_cast& e) {
4343 std::cerr << "Error: Failed to cast axis attribute" << std::endl;
4344 }
4345 } else {
4346 std::cout << " axis : n/a" << std::endl;
4347 }
4348
4349 if (float_data.HasAttribute("units")) {
4350 try {
4351 auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
4352 std::cout << " units : " << units << std::endl;
4353 } catch (const std::bad_any_cast& e) {
4354 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4355 }
4356 } else {
4357 std::cout << " units : n/a" << std::endl;
4358 }
4359 } catch (const std::bad_any_cast& e) {
4360 std::cerr << std::endl << "Error: Failed to cast raw_time data" << std::endl;
4361 }
4362 } else {
4363 std::cout << std::endl << " corrected_time : n/a";
4364 }
4365
4366 if (fDataMap.find("/run/histogram_data_1/grouping") != fDataMap.end()) {
4367 try {
4368 auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/grouping"]);
4369 std::cout << std::endl << " grouping : " << int_data.GetData()[0];
4370 if (int_data.HasAttribute("available")) {
4371 try {
4372 auto available = std::any_cast<int>(int_data.GetAttribute("available"));
4373 std::cout << " available : " << available << std::endl;
4374 } catch (const std::bad_any_cast& e) {
4375 std::cerr << "Error: Failed to cast available attribute" << std::endl;
4376 }
4377 } else {
4378 std::cout << std::endl << " available : n/a";
4379 }
4380 } catch (const std::bad_any_cast& e) {
4381 std::cerr << std::endl << "Error: Failed to cast grouping data" << std::endl;
4382 }
4383 } else {
4384 std::cout << std::endl << " grouping : n/a";
4385 }
4386
4387 std::cout << std::endl;
4388 } else { // IDF Version 2
4389 std::cout << std::endl;
4390 std::cout << std::endl << "hdf5-NeXus file content of file:' " << fFileName << "'";
4391 std::cout << std::endl << "****";
4392 std::cout << std::endl << "Top Level Attributes:";
4393 std::cout << std::endl << " HDF5 Version : " << fHdf5Version;
4394 std::cout << std::endl << " NeXus Version: " << fNeXusVersion;
4395 std::cout << std::endl << " file_name : " << fFileNameNxs;
4396 std::cout << std::endl << " file_time : " << fFileTimeNxs;
4397 std::cout << std::endl << "++++";
4398 std::cout << std::endl << "raw_data_1";
4399 std::cout << std::endl << "----";
4400 std::cout << std::endl << " IDF_version: " << fIdfVersion;
4401 if (fDataMap.find("/raw_data_1/beamline") != fDataMap.end()) {
4402 try {
4403 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/beamline"]);
4404 std::cout << std::endl << " beamline : " << str.GetData()[0];
4405 } catch (const std::bad_any_cast& e) {
4406 std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
4407 }
4408 } else {
4409 std::cout << std::endl << " beamline : n/a";
4410 }
4411
4412 if (fDataMap.find("/raw_data_1/definition") != fDataMap.end()) {
4413 try {
4414 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/definition"]);
4415 std::cout << std::endl << " definition : " << str.GetData()[0];
4416 } catch (const std::bad_any_cast& e) {
4417 std::cerr << std::endl << "Error: Failed to cast definition data" << std::endl;
4418 }
4419 } else {
4420 std::cout << std::endl << " definition : n/a";
4421 }
4422
4423 if (fDataMap.find("/raw_data_1/run_number") != fDataMap.end()) {
4424 try {
4425 auto str = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/run_number"]);
4426 std::cout << std::endl << " run_number : " << str.GetData()[0];
4427 } catch (const std::bad_any_cast& e) {
4428 std::cerr << std::endl << "Error: Failed to cast run_number data" << std::endl;
4429 }
4430 } else {
4431 std::cout << std::endl << " run_number : n/a";
4432 }
4433
4434 if (fDataMap.find("/raw_data_1/title") != fDataMap.end()) {
4435 try {
4436 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/title"]);
4437 std::cout << std::endl << " title : " << str.GetData()[0];
4438 } catch (const std::bad_any_cast& e) {
4439 std::cerr << std::endl << "Error: Failed to cast title data" << std::endl;
4440 }
4441 } else {
4442 std::cout << std::endl << " title : n/a";
4443 }
4444
4445 if (fDataMap.find("/raw_data_1/start_time") != fDataMap.end()) {
4446 try {
4447 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/start_time"]);
4448 std::cout << std::endl << " start_time : " << str.GetData()[0];
4449 } catch (const std::bad_any_cast& e) {
4450 std::cerr << std::endl << "Error: Failed to cast start_time data" << std::endl;
4451 }
4452 } else {
4453 std::cout << std::endl << " start_time : n/a";
4454 }
4455
4456 if (fDataMap.find("/raw_data_1/end_time") != fDataMap.end()) {
4457 try {
4458 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/end_time"]);
4459 std::cout << std::endl << " end_time : " << str.GetData()[0];
4460 } catch (const std::bad_any_cast& e) {
4461 std::cerr << std::endl << "Error: Failed to cast end_time data" << std::endl;
4462 }
4463 } else {
4464 std::cout << std::endl << " end_time : n/a";
4465 }
4466
4467 if (fDataMap.find("/raw_data_1/good_frames") != fDataMap.end()) {
4468 try {
4469 auto good_frames = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/good_frames"]);
4470 std::cout << std::endl << " good_frames: " << good_frames.GetData()[0];
4471 } catch(const std::bad_any_cast& e) {
4472 std::cerr << std::endl << "Error: Failed to cast good_frames data" << std::endl;
4473 }
4474 } else {
4475 std::cout << std::endl << " good_frames: n/a";
4476 }
4477
4478 if (fDataMap.find("/raw_data_1/experiment_identifier") != fDataMap.end()) {
4479 try {
4480 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/experiment_identifier"]);
4481 std::cout << std::endl << " experiment_identifier: " << str.GetData()[0];
4482 } catch (const std::bad_any_cast& e) {
4483 std::cerr << std::endl << "Error: Failed to cast experiment_identifier data" << std::endl;
4484 }
4485 } else {
4486 std::cout << std::endl << " experiment_identifier: n/a";
4487 }
4488
4489 std::cout << std::endl << "----";
4490 std::cout << std::endl << " instrument";
4491 if (fDataMap.find("/raw_data_1/instrument/name") != fDataMap.end()) {
4492 try {
4493 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/name"]);
4494 std::cout << std::endl << " name : " << str.GetData()[0];
4495 } catch (const std::bad_any_cast& e) {
4496 std::cerr << std::endl << "Error: Failed to cast instrument/name data" << std::endl;
4497 }
4498 } else {
4499 std::cout << std::endl << " name : n/a";
4500 }
4501
4502 std::cout << std::endl << "----";
4503 std::cout << std::endl << " source";
4504 if (fDataMap.find("/raw_data_1/instrument/source/name") != fDataMap.end()) {
4505 try {
4506 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/name"]);
4507 std::cout << std::endl << " name : " << str.GetData()[0];
4508 } catch (const std::bad_any_cast& e) {
4509 std::cerr << std::endl << "Error: Failed to cast instrument/source/name data" << std::endl;
4510 }
4511 } else {
4512 std::cout << std::endl << " name : n/a";
4513 }
4514
4515 if (fDataMap.find("/raw_data_1/instrument/source/type") != fDataMap.end()) {
4516 try {
4517 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/type"]);
4518 std::cout << std::endl << " type : " << str.GetData()[0];
4519 } catch (const std::bad_any_cast& e) {
4520 std::cerr << std::endl << "Error: Failed to cast instrument/source/type data" << std::endl;
4521 }
4522 } else {
4523 std::cout << std::endl << " type : n/a";
4524 }
4525
4526 if (fDataMap.find("/raw_data_1/instrument/source/probe") != fDataMap.end()) {
4527 try {
4528 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/probe"]);
4529 std::cout << std::endl << " probe : " << str.GetData()[0];
4530 } catch (const std::bad_any_cast& e) {
4531 std::cerr << std::endl << "Error: Failed to cast instrument/source/probe data" << std::endl;
4532 }
4533 } else {
4534 std::cout << std::endl << " probe : n/a";
4535 }
4536
4537 std::cout << std::endl << "----";
4538 std::cout << std::endl << " sample";
4539 if (fDataMap.find("/raw_data_1/sample/name") != fDataMap.end()) {
4540 try {
4541 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/sample/name"]);
4542 std::cout << std::endl << " name : " << str.GetData()[0];
4543 } catch (const std::bad_any_cast& e) {
4544 std::cerr << std::endl << "Error: Failed to cast sample/name data" << std::endl;
4545 }
4546 } else {
4547 std::cout << std::endl << " name : n/a";
4548 }
4549
4550 if (fDataMap.find("/raw_data_1/sample/temperature") != fDataMap.end()) {
4551 try {
4552 auto temp_ds = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/sample/temperature"]);
4553 float temp = temp_ds.GetData()[0];
4554 if (temp_ds.HasAttribute("units")) {
4555 try {
4556 auto units = std::any_cast<std::string>(temp_ds.GetAttribute("units"));
4557 if (units == "Celsius")
4558 temp += 273.16;
4559 } catch (const std::bad_any_cast& e) {
4560 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4561 }
4562 }
4563 std::cout << std::endl << " temperature: " << temp << " K";
4564 } catch (const std::bad_any_cast& e) {
4565 std::cerr << std::endl << "Error: Failed to cast sample/temperature data" << std::endl;
4566 }
4567 } else {
4568 std::cout << std::endl << " temperature: n/a";
4569 }
4570
4571 if (fDataMap.find("/raw_data_1/sample/magnetic_field") != fDataMap.end()) {
4572 try {
4573 auto mag_field_ds = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/sample/magnetic_field"]);
4574 float mag_field = mag_field_ds.GetData()[0];
4575 if (mag_field_ds.HasAttribute("units")) {
4576 try {
4577 auto units = std::any_cast<std::string>(mag_field_ds.GetAttribute("units"));
4578 if (units == "Tesla")
4579 mag_field *= 1e4;
4580 } catch (const std::bad_any_cast& e) {
4581 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4582 }
4583 }
4584 std::cout << std::endl << " mag. field: " << mag_field << " G";
4585 } catch (const std::bad_any_cast& e) {
4586 std::cerr << std::endl << "Error: Failed to cast sample/magnetic_field data" << std::endl;
4587 }
4588 } else {
4589 std::cout << std::endl << " temperature: n/a";
4590 }
4591
4592 if (fDataMap.find("/raw_data_1/sample/shape") != fDataMap.end()) {
4593 try {
4594 auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/sample/shape"]);
4595 std::cout << std::endl << " shape : " << str.GetData()[0];
4596 } catch (const std::bad_any_cast& e) {
4597 std::cerr << std::endl << "Error: Failed to cast sample/shape data" << std::endl;
4598 }
4599 } else {
4600 std::cout << std::endl << " shape : n/a";
4601 }
4602
4603 std::cout << std::endl << "----";
4604 std::cout << std::endl << " detector_1";
4605 std::cout << std::endl << " counts:";
4606 std::cout << std::endl;
4607 try {
4608 auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/counts"]);
4609 auto dims = counts_data.GetDimensions();
4610 std::cout << " counts dimensions: " << dims[0] << " x "
4611 << dims[1] << " x " << dims[2] << std::endl;
4612 std::cout << " total elements: " << counts_data.GetNumElements() << std::endl;
4613
4614 // Check for attributes
4615 if (counts_data.HasAttribute("signal")) {
4616 try {
4617 auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
4618 std::cout << " signal : " << signal << std::endl;
4619 } catch (const std::bad_any_cast& e) {
4620 std::cerr << "Error: Failed to cast signal attribute" << std::endl;
4621 }
4622 } else {
4623 std::cout << " signal : n/a" << std::endl;
4624 }
4625
4626 if (counts_data.HasAttribute("axes")) {
4627 try {
4628 auto axes = std::any_cast<std::string>(counts_data.GetAttribute("axes"));
4629 std::cout << " axes : " << axes << std::endl;
4630 } catch (const std::bad_any_cast& e) {
4631 std::cerr << "Error: Failed to cast axes attribute" << std::endl;
4632 }
4633 } else {
4634 std::cout << " axes : n/a" << std::endl;
4635 }
4636
4637 if (counts_data.HasAttribute("long_name")) {
4638 try {
4639 auto long_name = std::any_cast<std::string>(counts_data.GetAttribute("long_name"));
4640 std::cout << " long_name : " << long_name << std::endl;
4641 } catch (const std::bad_any_cast& e) {
4642 std::cerr << "Error: Failed to cast long_name attribute" << std::endl;
4643 }
4644 } else {
4645 std::cout << " long_name : n/a" << std::endl;
4646 }
4647
4648 if (counts_data.HasAttribute("t0_bin")) {
4649 try {
4650 auto t0_bin = std::any_cast<int>(counts_data.GetAttribute("t0_bin"));
4651 std::cout << " t0_bin : " << t0_bin << std::endl;
4652 } catch (const std::bad_any_cast& e) {
4653 std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
4654 }
4655 } else {
4656 std::cout << " t0_bin : n/a" << std::endl;
4657 }
4658
4659 if (counts_data.HasAttribute("first_good_bin")) {
4660 try {
4661 first_good_bin = std::any_cast<int>(counts_data.GetAttribute("first_good_bin"));
4662 std::cout << " first_good_bin : " << first_good_bin << std::endl;
4663 } catch (const std::bad_any_cast& e) {
4664 std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
4665 }
4666 } else {
4667 std::cout << " first_good_bin : n/a" << std::endl;
4668 }
4669
4670 if (counts_data.HasAttribute("last_good_bin")) {
4671 try {
4672 auto last_good_bin = std::any_cast<int>(counts_data.GetAttribute("last_good_bin"));
4673 std::cout << " last_good_bin : " << last_good_bin << std::endl;
4674 } catch (const std::bad_any_cast& e) {
4675 std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
4676 }
4677 } else {
4678 std::cout << " last_good_bin : n/a" << std::endl;
4679 }
4680
4681 if (fDataMap.find("/raw_data_1/instrument/detector_1/resolution") != fDataMap.end()) {
4682 try {
4683 auto ivalData = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/resolution"]);
4684 std::cout << " resolution : " << ivalData.GetData()[0];
4685 if (ivalData.HasAttribute("units")) {
4686 try {
4687 auto units = std::any_cast<std::string>(ivalData.GetAttribute("units"));
4688 std::cout << " " << units << std::endl;
4689 } catch (const std::bad_any_cast& e) {
4690 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4691 }
4692 }
4693 } catch (const std::bad_any_cast& e) {
4694 std::cerr << "Error: Failed to cast resolution attribute" << std::endl;
4695 }
4696 } else {
4697 std::cout << " resolution : n/a";
4698 }
4699
4700 // dump the first couple of counts of each detector
4701 const auto& data = counts_data.GetData();
4702 std::cout << std::endl << " first couple of counts of each detector:";
4703 for (unsigned int i=0; i<dims[1]; i++) {
4704 if (i<9)
4705 std::cout << std::endl << " " << i+1 << ": ";
4706 else
4707 std::cout << std::endl << " " << i+1 << ": ";
4708 for (unsigned int j=0; j<first_good_bin+5; j++)
4709 std::cout << data[i*dims[2]+j] << ", ";
4710 std::cout << "...";
4711 }
4712 } catch (const std::bad_any_cast& e) {
4713 std::cerr << "Error: Failed to cast counts data" << std::endl;
4714 }
4715
4716 std::cout << std::endl;
4717 std::cout << std::endl << " raw_time:";
4718 std::cout << std::endl;
4719 try {
4720 auto raw_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/instrument/detector_1/raw_time"]);
4721 const auto& data = raw_time.GetData();
4722
4723 // Check for attributes
4724 if (raw_time.HasAttribute("units")) {
4725 try {
4726 auto units = std::any_cast<std::string>(raw_time.GetAttribute("units"));
4727 std::cout << " units : " << units << std::endl;
4728 } catch (const std::bad_any_cast& e) {
4729 std::cerr << "Error: Failed to cast units attribute" << std::endl;
4730 }
4731 } else {
4732 std::cout << " units : n/a" << std::endl;
4733 }
4734
4735 // dump the first couple of raw_times
4736 std::cout << " ";
4737 for (unsigned int i=0; i<first_good_bin+5; i++)
4738 std::cout << data[i] << ", ";
4739 std::cout << "...";
4740 } catch (const std::bad_any_cast& e) {
4741 std::cerr << "Error: Failed to cast raw_time data" << std::endl;
4742 }
4743
4744 std::cout << std::endl;
4745 std::cout << std::endl << " spectrum_index:";
4746 std::cout << std::endl;
4747 try {
4748 auto spectrum_index = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/spectrum_index"]);
4749 const auto& data = spectrum_index.GetData();
4750
4751 // dump the first couple of raw_times
4752 std::cout << " ";
4753 for (unsigned int i=0; i<first_good_bin+5; i++)
4754 std::cout << data[i] << ", ";
4755 std::cout << "...";
4756 } catch (const std::bad_any_cast& e) {
4757 std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
4758 }
4759
4760 std::cout << std::endl;
4761 std::cout << std::endl << " dead_time:";
4762 std::cout << std::endl;
4763 try {
4764 auto dead_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/instrument/detector_1/dead_time"]);
4765 const auto& data = dead_time.GetData();
4766
4767 // dump the first couple of raw_times
4768 std::cout << " ";
4769 for (unsigned int i=0; i<first_good_bin+5; i++)
4770 std::cout << data[i] << ", ";
4771 std::cout << "...";
4772 } catch (const std::bad_any_cast& e) {
4773 std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
4774 }
4775
4776 std::cout << std::endl;
4777 }
4778}
4779
4780//=============================================================================
4781// Read integer dataset and store in data map
4782//=============================================================================
4791void nxH5::PNeXus::ReadIntDataset(H5::H5File& file, const std::string& path)
4792{
4793 try {
4794 // Find the actual path (case-insensitive)
4795 std::string actualPath = FindDatasetPath(file, path);
4796
4797 // Open the dataset
4798 H5::DataSet dataset = file.openDataSet(actualPath);
4799 H5::DataType datatype = dataset.getDataType();
4800 H5::DataSpace dataspace = dataset.getSpace();
4801
4802 // Get dimensions
4803 int rank = dataspace.getSimpleExtentNdims();
4804 std::vector<hsize_t> dims(rank);
4805 dataspace.getSimpleExtentDims(dims.data(), nullptr);
4806
4807 // Calculate total number of elements
4808 hsize_t numElements = 1;
4809 for (int i = 0; i < rank; i++) {
4810 numElements *= dims[i];
4811 }
4812
4813 // Create PNXdata object
4814 PNXdata<int> data(datatype);
4815 data.SetDimensions(dims);
4816
4817 // Read data
4818 std::vector<int> buffer(numElements);
4819 dataset.read(buffer.data(), H5::PredType::NATIVE_INT);
4820 data.SetData(buffer);
4821
4822 // Read attributes
4823 ReadDatasetAttributes(dataset, data);
4824
4825 // Store in map
4826 fDataMap[actualPath] = data;
4827
4828 dataset.close();
4829
4830 if (fPrintDebug) {
4831 std::cout << "debug> Read integer dataset: " << actualPath
4832 << " (dims: ";
4833 for (size_t i = 0; i < dims.size(); i++) {
4834 std::cout << dims[i];
4835 if (i < dims.size() - 1) std::cout << " x ";
4836 }
4837 std::cout << ")" << std::endl;
4838 }
4839 } catch (const H5::Exception& err) {
4840 std::cerr << "Error reading integer dataset " << path << ": "
4841 << err.getDetailMsg() << std::endl;
4842 throw;
4843 }
4844}
4845
4846//=============================================================================
4847// Read float dataset and store in data map
4848//=============================================================================
4857void nxH5::PNeXus::ReadFloatDataset(H5::H5File& file, const std::string& path)
4858{
4859 try {
4860 // Find the actual path (case-insensitive)
4861 std::string actualPath = FindDatasetPath(file, path);
4862
4863 // Open the dataset
4864 H5::DataSet dataset = file.openDataSet(actualPath);
4865 H5::DataType datatype = dataset.getDataType();
4866 H5::DataSpace dataspace = dataset.getSpace();
4867
4868 // Get dimensions
4869 int rank = dataspace.getSimpleExtentNdims();
4870 std::vector<hsize_t> dims(rank);
4871 dataspace.getSimpleExtentDims(dims.data(), nullptr);
4872
4873 // Calculate total number of elements
4874 hsize_t numElements = 1;
4875 for (int i = 0; i < rank; i++) {
4876 numElements *= dims[i];
4877 }
4878
4879 // Create PNXdata object
4880 PNXdata<float> data(datatype);
4881 data.SetDimensions(dims);
4882
4883 // Read data
4884 std::vector<float> buffer(numElements);
4885 dataset.read(buffer.data(), H5::PredType::NATIVE_FLOAT);
4886 data.SetData(buffer);
4887
4888 // Read attributes
4889 ReadDatasetAttributes(dataset, data);
4890
4891 // Store in map
4892 fDataMap[actualPath] = data;
4893
4894 dataset.close();
4895
4896 if (fPrintDebug) {
4897 std::cout << "debug> Read float dataset: " << actualPath
4898 << " (dims: ";
4899 for (size_t i = 0; i < dims.size(); i++) {
4900 std::cout << dims[i];
4901 if (i < dims.size() - 1) std::cout << " x ";
4902 }
4903 std::cout << ")" << std::endl;
4904 }
4905 } catch (const H5::Exception& err) {
4906 std::cerr << "Error reading float dataset " << path << ": "
4907 << err.getDetailMsg() << std::endl;
4908 throw;
4909 }
4910}
4911
4912//=============================================================================
4913// Read string dataset and store in data map
4914//=============================================================================
4922void nxH5::PNeXus::ReadStringDataset(H5::H5File& file, const std::string& path)
4923{
4924 try {
4925 // Find the actual path (case-insensitive)
4926 std::string actualPath = FindDatasetPath(file, path);
4927
4928 // Open the dataset
4929 H5::DataSet dataset = file.openDataSet(actualPath);
4930 H5::DataType datatype = dataset.getDataType();
4931 H5::DataSpace dataspace = dataset.getSpace();
4932
4933 // Get dimensions
4934 int rank = dataspace.getSimpleExtentNdims();
4935 std::vector<hsize_t> dims(rank);
4936 dataspace.getSimpleExtentDims(dims.data(), nullptr);
4937
4938 // Calculate total number of elements
4939 hsize_t numElements = 1;
4940 for (int i = 0; i < rank; i++) {
4941 numElements *= dims[i];
4942 }
4943
4944 // Create PNXdata object
4945 PNXdata<std::string> data(datatype);
4946 data.SetDimensions(dims);
4947
4948 // Read data
4949 std::vector<std::string> buffer(numElements);
4950 if (numElements == 1) {
4951 // Single string
4952 std::string value;
4953 dataset.read(value, datatype);
4954 buffer[0] = value;
4955 } else {
4956 // Multiple strings (if needed)
4957 for (hsize_t i = 0; i < numElements; i++) {
4958 dataset.read(buffer[i], datatype);
4959 }
4960 }
4961 data.SetData(buffer);
4962
4963 // Read attributes
4964 ReadDatasetAttributes(dataset, data);
4965
4966 // Store in map
4967 fDataMap[actualPath] = data;
4968
4969 dataset.close();
4970
4971 if (fPrintDebug) {
4972 std::cout << "debug> Read string dataset: " << actualPath << std::endl;
4973 }
4974 } catch (const H5::Exception& err) {
4975 std::cerr << "Error reading string dataset " << path << ": "
4976 << err.getDetailMsg() << std::endl;
4977 throw;
4978 }
4979}
4980
4981//=============================================================================
4982// Read dataset attributes (template implementation)
4983//=============================================================================
4992template <typename T>
4993void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet& dataset, PNXdata<T>& data)
4994{
4995 int numAttrs = dataset.getNumAttrs();
4996
4997 for (int i = 0; i < numAttrs; i++) {
4998 H5::Attribute attr = dataset.openAttribute(i);
4999 std::string attrName = attr.getName();
5000 H5::DataType attrType = attr.getDataType();
5001 H5T_class_t typeClass = attrType.getClass();
5002
5003 try {
5004 if (typeClass == H5T_INTEGER) {
5005 int value;
5006 attr.read(H5::PredType::NATIVE_INT, &value);
5007 data.AddAttribute(attrName, value);
5008 } else if (typeClass == H5T_FLOAT) {
5009 float value;
5010 attr.read(H5::PredType::NATIVE_FLOAT, &value);
5011 data.AddAttribute(attrName, value);
5012 } else if (typeClass == H5T_STRING) {
5013 std::string value;
5014 attr.read(attrType, value);
5015 data.AddAttribute(attrName, value);
5016 }
5017 } catch (const H5::Exception& err) {
5018 if (fPrintDebug) {
5019 std::cerr << "Warning: Could not read attribute " << attrName
5020 << ": " << err.getDetailMsg() << std::endl;
5021 }
5022 }
5023
5024 attr.close();
5025 }
5026}
5027
5028// Explicit template instantiations
5029template void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet&, PNXdata<int>&);
5030template void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet&, PNXdata<float>&);
5031template void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet&, PNXdata<std::string>&);
5032
5033//=============================================================================
5034// WRITE METHODS
5035//=============================================================================
5036
5037//=============================================================================
5038// Create group hierarchy for a given path
5039//=============================================================================
5049H5::Group nxH5::PNeXus::CreateGroupHierarchy(H5::H5File& file, const std::string& path)
5050{
5051 std::vector<std::string> components = SplitPath(path);
5052 std::string currentPath = "";
5053 H5::Group currentGroup;
5054
5055 for (const auto& component : components) {
5056 if (component.empty()) continue; // Skip empty (root indicator)
5057
5058 currentPath += "/" + component;
5059
5060 // Check if group exists
5061 bool exists = false;
5062 try {
5063 H5::Group testGroup = file.openGroup(currentPath);
5064 testGroup.close();
5065 exists = true;
5066 } catch (const H5::Exception&) {
5067 exists = false;
5068 }
5069
5070 // Create if doesn't exist
5071 if (!exists) {
5072 if (fPrintDebug) {
5073 std::cout << "debug> Creating group: " << currentPath << std::endl;
5074 }
5075 currentGroup = file.createGroup(currentPath);
5076
5077 // Write group attributes if any exist for this path
5078 auto attrIt = fGroupAttributes.find(currentPath);
5079 if (attrIt != fGroupAttributes.end()) {
5080 if (fPrintDebug) {
5081 std::cout << "debug> Writing " << attrIt->second.size()
5082 << " attributes to group: " << currentPath << std::endl;
5083 }
5084 WriteGroupAttributes(currentGroup, attrIt->second);
5085 }
5086
5087 currentGroup.close();
5088
5089 if (fPrintDebug) {
5090 std::cout << "debug> Created group: " << currentPath << std::endl;
5091 }
5092 }
5093 }
5094
5095 // Return an invalid group handle (we just needed to create the hierarchy)
5096 return currentGroup;
5097}
5098
5099//=============================================================================
5100// Write dataset attributes from PNXdata object
5101//=============================================================================
5110template <typename T>
5111void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet& dataset, const PNXdata<T>& data)
5112{
5113 const auto& attributes = data.GetAttributes();
5114
5115 for (const auto& [attrName, attrValue] : attributes) {
5116 try {
5117 // Try int
5118 if (auto* intVal = std::any_cast<int>(&attrValue)) {
5119 H5::DataSpace attrSpace(H5S_SCALAR);
5120 H5::Attribute attr = dataset.createAttribute(
5121 attrName, H5::PredType::NATIVE_INT32, attrSpace
5122 );
5123 attr.write(H5::PredType::NATIVE_INT32, intVal);
5124 attr.close();
5125 attrSpace.close();
5126 }
5127 // Try float
5128 else if (auto* floatVal = std::any_cast<float>(&attrValue)) {
5129 H5::DataSpace attrSpace(H5S_SCALAR);
5130 H5::Attribute attr = dataset.createAttribute(
5131 attrName, H5::PredType::NATIVE_FLOAT, attrSpace
5132 );
5133 attr.write(H5::PredType::NATIVE_FLOAT, floatVal);
5134 attr.close();
5135 attrSpace.close();
5136 }
5137 // Try string
5138 else if (auto* strVal = std::any_cast<std::string>(&attrValue)) {
5139 H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
5140 H5::DataSpace attrSpace(H5S_SCALAR);
5141 H5::Attribute attr = dataset.createAttribute(
5142 attrName, strType, attrSpace
5143 );
5144 attr.write(strType, *strVal);
5145 attr.close();
5146 attrSpace.close();
5147 }
5148 // Try vector<int> for multi-element attributes
5149 else if (auto* intVec = std::any_cast<std::vector<int>>(&attrValue)) {
5150 hsize_t dims[1] = {intVec->size()};
5151 H5::DataSpace attrSpace(1, dims);
5152 H5::Attribute attr = dataset.createAttribute(
5153 attrName, H5::PredType::NATIVE_INT32, attrSpace
5154 );
5155 attr.write(H5::PredType::NATIVE_INT32, intVec->data());
5156 attr.close();
5157 attrSpace.close();
5158 }
5159 // Try vector<float> for multi-element attributes
5160 else if (auto* floatVec = std::any_cast<std::vector<float>>(&attrValue)) {
5161 hsize_t dims[1] = {floatVec->size()};
5162 H5::DataSpace attrSpace(1, dims);
5163 H5::Attribute attr = dataset.createAttribute(
5164 attrName, H5::PredType::NATIVE_FLOAT, attrSpace
5165 );
5166 attr.write(H5::PredType::NATIVE_FLOAT, floatVec->data());
5167 attr.close();
5168 attrSpace.close();
5169 }
5170 else {
5171 if (fPrintDebug) {
5172 std::cerr << "Warning: Unsupported attribute type for "
5173 << attrName << std::endl;
5174 }
5175 }
5176
5177 } catch (const H5::Exception& err) {
5178 if (fPrintDebug) {
5179 std::cerr << "Warning: Could not write attribute " << attrName
5180 << ": " << err.getDetailMsg() << std::endl;
5181 }
5182 }
5183 }
5184}
5185
5186//=============================================================================
5187// Write integer dataset with attributes
5188//=============================================================================
5198void nxH5::PNeXus::WriteIntDataset(H5::H5File& file, const std::string& path,
5199 const PNXdata<int>& data)
5200{
5201 try {
5202 // Extract parent path and dataset name
5203 size_t lastSlash = path.find_last_of('/');
5204 std::string parentPath = (lastSlash == 0) ? "/" : path.substr(0, lastSlash);
5205 std::string datasetName = path.substr(lastSlash + 1);
5206
5207 // Create parent group hierarchy
5208 if (!parentPath.empty() && parentPath != "/") {
5209 CreateGroupHierarchy(file, parentPath);
5210 }
5211
5212 // Get dimensions and data
5213 const auto& dims = data.GetDimensions();
5214 const auto& buffer = data.GetData();
5215
5216 // Validate data consistency
5217 if (buffer.size() != data.GetNumElements()) {
5218 throw std::runtime_error("Data size mismatch with dimensions");
5219 }
5220
5221 // Create dataspace
5222 H5::DataSpace dataspace(dims.size(), dims.data());
5223
5224 // Create dataset
5225 H5::DataSet dataset = file.createDataSet(
5226 path,
5227 H5::PredType::NATIVE_INT,
5228 dataspace
5229 );
5230
5231 // Write data
5232 dataset.write(buffer.data(), H5::PredType::NATIVE_INT);
5233
5234 // Write attributes
5235 WriteDatasetAttributes(dataset, data);
5236
5237 // Close resources
5238 dataset.close();
5239 dataspace.close();
5240
5241 if (fPrintDebug) {
5242 std::cout << "debug> Wrote integer dataset: " << path
5243 << " (dims: ";
5244 for (size_t i = 0; i < dims.size(); i++) {
5245 std::cout << dims[i];
5246 if (i < dims.size() - 1) std::cout << " x ";
5247 }
5248 std::cout << ")" << std::endl;
5249 }
5250
5251 } catch (const H5::Exception& err) {
5252 std::cerr << "Error writing integer dataset " << path << ": "
5253 << err.getDetailMsg() << std::endl;
5254 throw;
5255 }
5256}
5257
5258//=============================================================================
5259// Write float dataset with attributes
5260//=============================================================================
5270void nxH5::PNeXus::WriteFloatDataset(H5::H5File& file, const std::string& path,
5271 const PNXdata<float>& data)
5272{
5273 try {
5274 // Extract parent path and dataset name
5275 size_t lastSlash = path.find_last_of('/');
5276 std::string parentPath = (lastSlash == 0) ? "/" : path.substr(0, lastSlash);
5277 std::string datasetName = path.substr(lastSlash + 1);
5278
5279 // Create parent group hierarchy
5280 if (!parentPath.empty() && parentPath != "/") {
5281 CreateGroupHierarchy(file, parentPath);
5282 }
5283
5284 // Get dimensions and data
5285 const auto& dims = data.GetDimensions();
5286 const auto& buffer = data.GetData();
5287
5288 // Validate data consistency
5289 if (buffer.size() != data.GetNumElements()) {
5290 throw std::runtime_error("Data size mismatch with dimensions");
5291 }
5292
5293 // Create dataspace
5294 H5::DataSpace dataspace(dims.size(), dims.data());
5295
5296 // Create dataset
5297 H5::DataSet dataset = file.createDataSet(
5298 path,
5299 H5::PredType::NATIVE_FLOAT,
5300 dataspace
5301 );
5302
5303 // Write data
5304 dataset.write(buffer.data(), H5::PredType::NATIVE_FLOAT);
5305
5306 // Write attributes
5307 WriteDatasetAttributes(dataset, data);
5308
5309 // Close resources
5310 dataset.close();
5311 dataspace.close();
5312
5313 if (fPrintDebug) {
5314 std::cout << "debug> Wrote float dataset: " << path
5315 << " (dims: ";
5316 for (size_t i = 0; i < dims.size(); i++) {
5317 std::cout << dims[i];
5318 if (i < dims.size() - 1) std::cout << " x ";
5319 }
5320 std::cout << ")" << std::endl;
5321 }
5322
5323 } catch (const H5::Exception& err) {
5324 std::cerr << "Error writing float dataset " << path << ": "
5325 << err.getDetailMsg() << std::endl;
5326 throw;
5327 }
5328}
5329
5330//=============================================================================
5331// Write string dataset with attributes
5332//=============================================================================
5343void nxH5::PNeXus::WriteStringDataset(H5::H5File& file, const std::string& path,
5344 const PNXdata<std::string>& data)
5345{
5346 try {
5347 // Extract parent path and dataset name
5348 size_t lastSlash = path.find_last_of('/');
5349 std::string parentPath = (lastSlash == 0) ? "/" : path.substr(0, lastSlash);
5350 std::string datasetName = path.substr(lastSlash + 1);
5351
5352 // Create parent group hierarchy
5353 if (!parentPath.empty() && parentPath != "/") {
5354 CreateGroupHierarchy(file, parentPath);
5355 }
5356
5357 // Get data
5358 const auto& buffer = data.GetData();
5359 const auto& dims = data.GetDimensions();
5360
5361 /* //as35
5362 // Handle single string vs array of strings
5363 if (buffer.size() == 1 && dims.size() == 1 && dims[0] == 1) {
5364 // Single string - use variable-length string type
5365 H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
5366 H5::DataSpace dataspace(H5S_SCALAR);
5367
5368 H5::DataSet dataset = file.createDataSet(path, strType, dataspace);
5369 dataset.write(buffer[0], strType);
5370
5371 WriteDatasetAttributes(dataset, data);
5372 dataset.close();
5373 dataspace.close();
5374
5375 } else {
5376 */ //as35
5377 // Array of strings
5378 H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
5379 H5::DataSpace dataspace(dims.size(), dims.data());
5380
5381 H5::DataSet dataset = file.createDataSet(path, strType, dataspace);
5382
5383 // For arrays, need to create array of C strings
5384 std::vector<const char*> cStrings;
5385 for (const auto& str : buffer) {
5386 cStrings.push_back(str.c_str());
5387 }
5388 dataset.write(cStrings.data(), strType);
5389
5390 WriteDatasetAttributes(dataset, data);
5391 dataset.close();
5392 dataspace.close();
5393 /* //as35
5394 }
5395 */ //as35
5396
5397 if (fPrintDebug) {
5398 std::cout << "debug> Wrote string dataset: " << path << std::endl;
5399 }
5400
5401 } catch (const H5::Exception& err) {
5402 std::cerr << "Error writing string dataset " << path << ": "
5403 << err.getDetailMsg() << std::endl;
5404 throw;
5405 }
5406}
5407
5408//=============================================================================
5409// Write group attributes
5410//=============================================================================
5418void nxH5::PNeXus::WriteGroupAttributes(H5::Group& group, const std::map<std::string, std::any>& attributes)
5419{
5420 for (const auto& [attrName, attrValue] : attributes) {
5421 try {
5422 // Try int
5423 if (auto* intVal = std::any_cast<int>(&attrValue)) {
5424 H5::DataSpace attrSpace(H5S_SCALAR);
5425 H5::Attribute attr = group.createAttribute(
5426 attrName, H5::PredType::NATIVE_INT32, attrSpace
5427 );
5428 attr.write(H5::PredType::NATIVE_INT32, intVal);
5429 attr.close();
5430 attrSpace.close();
5431 }
5432 // Try float
5433 else if (auto* floatVal = std::any_cast<float>(&attrValue)) {
5434 H5::DataSpace attrSpace(H5S_SCALAR);
5435 H5::Attribute attr = group.createAttribute(
5436 attrName, H5::PredType::NATIVE_FLOAT, attrSpace
5437 );
5438 attr.write(H5::PredType::NATIVE_FLOAT, floatVal);
5439 attr.close();
5440 attrSpace.close();
5441 }
5442 // Try string
5443 else if (auto* strVal = std::any_cast<std::string>(&attrValue)) {
5444 H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
5445 H5::DataSpace attrSpace(H5S_SCALAR);
5446 H5::Attribute attr = group.createAttribute(
5447 attrName, strType, attrSpace
5448 );
5449 attr.write(strType, *strVal);
5450 attr.close();
5451 attrSpace.close();
5452 }
5453 // Try vector<int> for multi-element attributes
5454 else if (auto* intVec = std::any_cast<std::vector<int>>(&attrValue)) {
5455 hsize_t dims[1] = {intVec->size()};
5456 H5::DataSpace attrSpace(1, dims);
5457 H5::Attribute attr = group.createAttribute(
5458 attrName, H5::PredType::NATIVE_INT32, attrSpace
5459 );
5460 attr.write(H5::PredType::NATIVE_INT32, intVec->data());
5461 attr.close();
5462 attrSpace.close();
5463 }
5464 // Try vector<float> for multi-element attributes
5465 else if (auto* floatVec = std::any_cast<std::vector<float>>(&attrValue)) {
5466 hsize_t dims[1] = {floatVec->size()};
5467 H5::DataSpace attrSpace(1, dims);
5468 H5::Attribute attr = group.createAttribute(
5469 attrName, H5::PredType::NATIVE_FLOAT, attrSpace
5470 );
5471 attr.write(H5::PredType::NATIVE_FLOAT, floatVec->data());
5472 attr.close();
5473 attrSpace.close();
5474 }
5475 else {
5476 if (fPrintDebug) {
5477 std::cerr << "Warning: Unsupported attribute type for "
5478 << attrName << std::endl;
5479 }
5480 }
5481
5482 } catch (const H5::Exception& err) {
5483 if (fPrintDebug) {
5484 std::cerr << "Warning: Failed to write group attribute "
5485 << attrName << ": " << err.getDetailMsg() << std::endl;
5486 }
5487 }
5488 }
5489}
5490
5491//=============================================================================
5492// Write root-level file attributes
5493//=============================================================================
5502{
5503 try {
5504 H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
5505 H5::DataSpace attrSpace(H5S_SCALAR);
5506
5507 // Check if custom root-level attributes exist
5508 auto rootAttrIt = fGroupAttributes.find("/");
5509 bool hasCustomAttrs = (rootAttrIt != fGroupAttributes.end());
5510
5511 // NeXus_version attribute
5512 if (!fNeXusVersion.empty() &&
5513 (!hasCustomAttrs || rootAttrIt->second.find("NeXus_version") == rootAttrIt->second.end())) {
5514 H5::Attribute attr = file.createAttribute(
5515 "NeXus_version", strType, attrSpace
5516 );
5517 attr.write(strType, fNeXusVersion);
5518 attr.close();
5519 }
5520
5521 // HDF5_version attribute
5522 if (!fHdf5Version.empty() &&
5523 (!hasCustomAttrs || rootAttrIt->second.find("HDF5_version") == rootAttrIt->second.end())) {
5524 H5::Attribute attr = file.createAttribute(
5525 "HDF5_version", strType, attrSpace
5526 );
5527 attr.write(strType, fHdf5Version);
5528 attr.close();
5529 }
5530
5531 // file_name attribute
5532 if (!fFileNameNxs.empty() &&
5533 (!hasCustomAttrs || rootAttrIt->second.find("file_name") == rootAttrIt->second.end())) {
5534 H5::Attribute attr = file.createAttribute(
5535 "file_name", strType, attrSpace
5536 );
5537 attr.write(strType, fFileNameNxs);
5538 attr.close();
5539 }
5540
5541 // file_time attribute
5542 if (!fFileTimeNxs.empty() &&
5543 (!hasCustomAttrs || rootAttrIt->second.find("file_time") == rootAttrIt->second.end())) {
5544 H5::Attribute attr = file.createAttribute(
5545 "file_time", strType, attrSpace
5546 );
5547 attr.write(strType, fFileTimeNxs);
5548 attr.close();
5549 }
5550
5551 attrSpace.close();
5552
5553 // Write any custom root-level attributes from fGroupAttributes["/"]
5554 if (hasCustomAttrs) {
5555 if (fPrintDebug) {
5556 std::cout << "debug> Writing " << rootAttrIt->second.size()
5557 << " custom attributes to root level" << std::endl;
5558 }
5559
5560 for (const auto& [attrName, attrValue] : rootAttrIt->second) {
5561 try {
5562 // Try int
5563 if (auto* intVal = std::any_cast<int>(&attrValue)) {
5564 H5::DataSpace scalarSpace(H5S_SCALAR);
5565 H5::Attribute attr = file.createAttribute(
5566 attrName, H5::PredType::NATIVE_INT32, scalarSpace
5567 );
5568 attr.write(H5::PredType::NATIVE_INT32, intVal);
5569 attr.close();
5570 scalarSpace.close();
5571 }
5572 // Try float
5573 else if (auto* floatVal = std::any_cast<float>(&attrValue)) {
5574 H5::DataSpace scalarSpace(H5S_SCALAR);
5575 H5::Attribute attr = file.createAttribute(
5576 attrName, H5::PredType::NATIVE_FLOAT, scalarSpace
5577 );
5578 attr.write(H5::PredType::NATIVE_FLOAT, floatVal);
5579 attr.close();
5580 scalarSpace.close();
5581 }
5582 // Try string
5583 else if (auto* strVal = std::any_cast<std::string>(&attrValue)) {
5584 H5::StrType varStrType(H5::PredType::C_S1, H5T_VARIABLE);
5585 H5::DataSpace scalarSpace(H5S_SCALAR);
5586 H5::Attribute attr = file.createAttribute(
5587 attrName, varStrType, scalarSpace
5588 );
5589 attr.write(varStrType, *strVal);
5590 attr.close();
5591 scalarSpace.close();
5592 }
5593 else {
5594 if (fPrintDebug) {
5595 std::cerr << "Warning: Unsupported attribute type for root-level "
5596 << attrName << std::endl;
5597 }
5598 }
5599 } catch (const H5::Exception& err) {
5600 if (fPrintDebug) {
5601 std::cerr << "Warning: Failed to write root-level attribute "
5602 << attrName << ": " << err.getDetailMsg() << std::endl;
5603 }
5604 }
5605 }
5606 }
5607
5608 } catch (const H5::Exception& err) {
5609 std::cerr << "Error writing file attributes: "
5610 << err.getDetailMsg() << std::endl;
5611 throw;
5612 }
5613}
5614
5615//=============================================================================
5616// Group attribute management methods
5617//=============================================================================
5618
5627bool nxH5::PNeXus::AddGroupAttribute(const std::string& groupPath, const std::string& attrName,
5628 const std::any& attrValue)
5629{
5630 fGroupAttributes[groupPath][attrName] = attrValue;
5631 return true;
5632}
5633
5641bool nxH5::PNeXus::RemoveGroupAttribute(const std::string& groupPath, const std::string& attrName)
5642{
5643 auto groupIt = fGroupAttributes.find(groupPath);
5644 if (groupIt == fGroupAttributes.end()) {
5645 return false;
5646 }
5647
5648 auto attrIt = groupIt->second.find(attrName);
5649 if (attrIt == groupIt->second.end()) {
5650 return false;
5651 }
5652
5653 groupIt->second.erase(attrIt);
5654
5655 // Clean up empty group entry
5656 if (groupIt->second.empty()) {
5657 fGroupAttributes.erase(groupIt);
5658 }
5659
5660 return true;
5661}
5662
5670bool nxH5::PNeXus::HasGroupAttribute(const std::string& groupPath, const std::string& attrName) const
5671{
5672 auto groupIt = fGroupAttributes.find(groupPath);
5673 if (groupIt == fGroupAttributes.end()) {
5674 return false;
5675 }
5676
5677 return groupIt->second.find(attrName) != groupIt->second.end();
5678}
5679
5688std::any nxH5::PNeXus::GetGroupAttribute(const std::string& groupPath, const std::string& attrName) const
5689{
5690 return fGroupAttributes.at(groupPath).at(attrName);
5691}
5692
5699const std::map<std::string, std::any>& nxH5::PNeXus::GetGroupAttributes(const std::string& groupPath) const
5700{
5701 static const std::map<std::string, std::any> emptyMap;
5702 auto it = fGroupAttributes.find(groupPath);
5703 if (it == fGroupAttributes.end()) {
5704 return emptyMap;
5705 }
5706 return it->second;
5707}
5708
5715bool nxH5::PNeXus::ClearGroupAttributes(const std::string& groupPath)
5716{
5717 auto it = fGroupAttributes.find(groupPath);
5718 if (it == fGroupAttributes.end()) {
5719 return false;
5720 }
5721
5722 fGroupAttributes.erase(it);
5723 return true;
5724}
5725
5734bool nxH5::PNeXus::AddRootAttribute(const std::string& attrName, const std::any& attrValue)
5735{
5736 return AddGroupAttribute("/", attrName, attrValue);
5737}
5738
5739//=============================================================================
5740// Write NeXus file (main entry point)
5741//=============================================================================
5753int nxH5::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion)
5754{
5755 try {
5756 if (fPrintDebug) {
5757 std::cout << std::endl;
5758 std::cout << "debug> Creating NeXus file: " << filename << std::endl;
5759 }
5760
5761 // Validate that we have data to write
5762 if (fDataMap.empty()) {
5763 std::cerr << "Error: No data to write (data map is empty)" << std::endl;
5764 return 1;
5765 }
5766
5767 // Create new HDF5 file (truncate if exists)
5768 H5::H5File file(filename, H5F_ACC_TRUNC);
5769
5770 // Turn off auto-printing for exceptions
5771 H5::Exception::dontPrint();
5772
5773 // Write root-level file attributes
5774 WriteFileAttributes(file);
5775
5776 // Write structure based on IDF version
5777 if ((idfVersion == 1) || (idfVersion == 2)) {
5778 if (fPrintDebug) {
5779 std::cout << "debug> Writing IDF v2 structure..." << std::endl;
5780 }
5781
5782 // Iterate through all datasets in fDataMap
5783 for (const auto& [path, anyData] : fDataMap) {
5784 try {
5785 // Try PNXdata<int>
5786 try {
5787 auto intData = std::any_cast<PNXdata<int>>(anyData);
5788 WriteIntDataset(file, path, intData);
5789 continue;
5790 } catch (const std::bad_any_cast&) {}
5791
5792 // Try PNXdata<float>
5793 try {
5794 auto floatData = std::any_cast<PNXdata<float>>(anyData);
5795 WriteFloatDataset(file, path, floatData);
5796 continue;
5797 } catch (const std::bad_any_cast&) {}
5798
5799 // Try PNXdata<std::string>
5800 try {
5801 auto strData = std::any_cast<PNXdata<std::string>>(anyData);
5802 WriteStringDataset(file, path, strData);
5803 continue;
5804 } catch (const std::bad_any_cast&) {}
5805
5806 // Unknown type
5807 if (fPrintDebug) {
5808 std::cerr << "Warning: Unknown data type for path " << path
5809 << std::endl;
5810 }
5811
5812 } catch (const H5::Exception& err) {
5813 std::cerr << "Error writing dataset " << path << ": "
5814 << err.getDetailMsg() << std::endl;
5815 throw;
5816 }
5817 }
5818 } else {
5819 std::cerr << "Error: Unsupported IDF version " << idfVersion
5820 << std::endl;
5821 file.close();
5822 return 1;
5823 }
5824
5825 // Close file
5826 file.close();
5827
5828 if (fPrintDebug) {
5829 std::cout << "debug> Successfully wrote NeXus file: " << filename
5830 << std::endl;
5831 }
5832
5833 return 0;
5834
5835 } catch (const H5::FileIException& err) {
5836 std::cerr << "Error: Failed to create file '" << filename << "': "
5837 << err.getDetailMsg() << std::endl;
5838 return 1;
5839 } catch (const H5::Exception& err) {
5840 std::cerr << "Error: HDF5 exception occurred: "
5841 << err.getDetailMsg() << std::endl;
5842 return 1;
5843 } catch (const std::exception& err) {
5844 std::cerr << "Error: Unexpected exception: " << err.what() << std::endl;
5845 return 1;
5846 }
5847}
5848
5849// Explicit template instantiations for write methods
5850template void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet&, const PNXdata<int>&);
5851template void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet&, const PNXdata<float>&);
5852template void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet&, const PNXdata<std::string>&);
NeXus HDF4/HDF5 file reader and writer for muon spin rotation data.
return status
Template class for storing HDF4 dataset content with attributes.
Definition PNeXus.h:370
const std::vector< T > & GetData() const
Get the data as a vector.
Definition PNeXus.h:399
void SetData(const std::vector< T > &data)
Set the data vector.
Definition PNeXus.h:411
void SetDimensions(const std::vector< uint32_t > &dims)
Set the dimensions of the dataset.
Definition PNeXus.h:423
const std::map< std::string, std::any > & GetAttributes() const
Get all attributes.
Definition PNeXus.h:476
void AddAttribute(const std::string &name, const std::any &value)
Add an attribute to this dataset.
Definition PNeXus.h:449
const std::vector< uint32_t > & GetDimensions() const
Get the dimensions of the dataset.
Definition PNeXus.h:417
std::vector< int > fCounts
Count data for minimization.
Definition PNeXus.h:318
int fLgbBin
Last good bin for analysis.
Definition PNeXus.h:316
std::vector< uint32_t > fDims
Dimensions of count data [periods, spectra, bins].
Definition PNeXus.h:313
float fTimeResolution
Time resolution in picoseconds.
Definition PNeXus.h:312
int fT0Bin
T0 bin number (time zero reference).
Definition PNeXus.h:314
PNeXusDeadTime(const PNeXus *nxs, bool debug=false)
Constructor - initializes dead time calculator from NeXus data.
Definition PNeXus.cpp:185
int fGoodFrames
Number of good time frames for analysis.
Definition PNeXus.h:311
unsigned int fIdx
Current spectrum index being minimized.
Definition PNeXus.h:320
bool fDebug
Debug flag - if true, print additional diagnostic information.
Definition PNeXus.h:309
double operator()(const std::vector< double > &par) const
Function call operator - calculates chi-square for given parameters.
Definition PNeXus.cpp:290
bool fValid
Validity flag - true if required data was loaded successfully.
Definition PNeXus.h:310
int fFgbBin
First good bin for analysis.
Definition PNeXus.h:315
void Minimize(const int i)
Minimize dead time for a specific detector spectrum.
Definition PNeXus.cpp:322
NeXus HDF4 file reader with case-insensitive path lookup.
Definition PNeXus.h:517
bool AddRootAttribute(const std::string &attrName, const std::any &attrValue)
Definition PNeXus.cpp:2530
std::string fHdf4LibVersion
HDF4 library version used when calling PNeXus.
Definition PNeXus.h:891
int32 WriteStringDataset(int32 sd_id, const std::string &path, const PNXdata< std::string > &data)
Write a string dataset with attributes and return its SDS reference.
Definition PNeXus.cpp:2242
bool RemoveGroupAttribute(const std::string &groupPath, const std::string &attrName)
Remove an attribute from a group.
Definition PNeXus.cpp:2479
bool AddGroupAttribute(const std::string &groupPath, const std::string &attrName, const std::any &attrValue)
Add or update an attribute for a group.
Definition PNeXus.cpp:2471
int32 FindDatasetIndex(int32 sd_id, const std::string &requestedName)
Find dataset index with case-insensitive matching.
Definition PNeXus.cpp:2594
std::string fFileTimeNxs
Definition PNeXus.h:895
const std::map< std::string, std::any > & GetGroupAttributes(const std::string &groupPath) const
Get all attributes for a group.
Definition PNeXus.cpp:2509
~PNeXus()
Destructor - closes HDF4 file if open.
Definition PNeXus.cpp:378
std::string fFileNameNxs
Definition PNeXus.h:894
static H4DataType convertHdf4Type(int32 hdf4_type)
Convert HDF4 data type to H4DataType enum.
Definition PNeXus.cpp:2766
void WriteDatasetAttributes(int32 sds_id, const PNXdata< T > &data)
Write dataset attributes from PNXdata object.
Definition PNeXus.cpp:2289
std::string fFileName
NeXus HDF4 filename.
Definition PNeXus.h:889
bool HasGroupAttribute(const std::string &groupPath, const std::string &attrName) const
Check if a group has a specific attribute.
Definition PNeXus.cpp:2493
static bool CaseInsensitiveEquals(const std::string &a, const std::string &b)
Compare two strings case-insensitively.
Definition PNeXus.cpp:2538
int32 fSdId
HDF4 SD interface identifier.
Definition PNeXus.h:898
std::string fNeXusVersion
NeXus version of the file.
Definition PNeXus.h:893
void HandleIdfV1(int32 sd_id)
Read datasets for IDF version 1 file structure.
Definition PNeXus.cpp:566
std::map< std::string, std::map< std::string, std::any > > fGroupAttributes
Map of group paths to their attributes.
Definition PNeXus.h:901
int32 FindDatasetRefByPath(const std::string &path)
Find dataset reference by navigating VGroup hierarchy.
Definition PNeXus.cpp:2626
void WriteVGroupAttributes(int32 vgroup_id, const std::map< std::string, std::any > &attributes)
Write attributes to a Vgroup.
Definition PNeXus.cpp:2338
std::string fHdf4Version
HDF4 version of the file.
Definition PNeXus.h:892
void ReadStringDataset(int32 sd_id, const std::string &path)
Read a string dataset and store in data map.
Definition PNeXus.cpp:864
std::map< std::string, std::any > fDataMap
Map of HDF4 paths to PNXdata objects.
Definition PNeXus.h:900
int WriteNexusFile(const std::string &filename, int idfVersion=2)
Write the data map contents to a NeXus HDF4 file.
Definition PNeXus.cpp:1994
bool fPrintDebug
if true print additional debug information
Definition PNeXus.h:888
int32 CreateVGroupHierarchy(int32 file_id, const std::string &path, std::map< std::string, int32 > &vgroupCache)
Create the Vgroup hierarchy for a given path.
Definition PNeXus.cpp:2387
static int32 convertToHdf4Type(H4DataType dataType)
Convert H4DataType enum to HDF4 data type.
Definition PNeXus.cpp:2793
void ReadIntDataset(int32 sd_id, const std::string &path)
Read an integer dataset and store in data map.
Definition PNeXus.cpp:712
std::string FindAttributeName(int32 sd_id, const std::string &requestedName)
Definition PNeXus.cpp:2573
std::any GetGroupAttribute(const std::string &groupPath, const std::string &attrName) const
Get an attribute value from a group.
Definition PNeXus.cpp:2503
int32 WriteIntDataset(int32 sd_id, const std::string &path, const PNXdata< int > &data)
Write an integer dataset with attributes and return its SDS reference.
Definition PNeXus.cpp:2146
void HandleIdfV2(int32 sd_id)
Read datasets for IDF version 2 file structure.
Definition PNeXus.cpp:650
static std::vector< std::string > SplitPath(const std::string &path)
Definition PNeXus.cpp:2550
bool ClearGroupAttributes(const std::string &groupPath)
Clear all attributes from a group.
Definition PNeXus.cpp:2520
void WriteFileAttributes(int32 sd_id)
Write root-level file attributes.
Definition PNeXus.cpp:2101
void ReadFloatDataset(int32 sd_id, const std::string &path)
Read a float dataset and store in data map.
Definition PNeXus.cpp:788
void Dump()
Print a human-readable dump of the NeXus file contents.
Definition PNeXus.cpp:1003
int32 WriteFloatDataset(int32 sd_id, const std::string &path, const PNXdata< float > &data)
Write a float dataset with attributes and return its SDS reference.
Definition PNeXus.cpp:2194
void ReadDatasetAttributes(int32 sds_id, PNXdata< T > &data)
Read dataset attributes and add to PNXdata object.
Definition PNeXus.cpp:942
int32 fFileId
HDF4 file identifier.
Definition PNeXus.h:899
int fIdfVersion
IDF version of the NeXus file.
Definition PNeXus.h:890
int ReadNexusFile()
Read and parse the NeXus HDF4 file.
Definition PNeXus.cpp:391
Template class for storing HDF5 dataset content with attributes.
Definition PNeXus.h:1316
const std::vector< T > & GetData() const
Get the data as a vector.
Definition PNeXus.h:1345
void SetDimensions(const std::vector< hsize_t > &dims)
Set the dimensions of the dataset.
Definition PNeXus.h:1369
const std::map< std::string, std::any > & GetAttributes() const
Get all attributes.
Definition PNeXus.h:1422
void AddAttribute(const std::string &name, const std::any &value)
Add an attribute to this dataset.
Definition PNeXus.h:1395
const std::vector< hsize_t > & GetDimensions() const
Get the dimensions of the dataset.
Definition PNeXus.h:1363
size_t GetNumElements() const
Get total number of elements.
Definition PNeXus.h:1381
void SetData(const std::vector< T > &data)
Set the data vector.
Definition PNeXus.h:1357
bool fValid
Validity flag - true if required data was loaded successfully.
Definition PNeXus.h:1272
unsigned int fIdx
Current spectrum index being minimized.
Definition PNeXus.h:1283
std::vector< int > fCounts
Count data for minimization.
Definition PNeXus.h:1281
int fGoodFrames
Number of good time frames for analysis.
Definition PNeXus.h:1273
PNeXusDeadTime(const PNeXus *nxs, bool debug=false)
Constructor - initializes dead time calculator from NeXus data.
Definition PNeXus.cpp:2815
std::vector< hsize_t > fDims
Dimensions of count data [periods, spectra, bins].
Definition PNeXus.h:1275
int fT0Bin
T0 bin number (time zero reference).
Definition PNeXus.h:1276
double operator()(const std::vector< double > &par) const
Function call operator - calculates chi-square for given parameters.
Definition PNeXus.cpp:2942
void Minimize(const int i)
Minimize dead time for a specific detector spectrum.
Definition PNeXus.cpp:2982
bool fDebug
Debug flag - if true, print additional diagnostic information.
Definition PNeXus.h:1271
float fTimeResolution
Time resolution in picoseconds.
Definition PNeXus.h:1274
std::vector< float > fDeadTimeEstimated
Dead time values per detector (microseconds) as estimated from the data.
Definition PNeXus.h:1280
int fFgbBin
First good bin for analysis.
Definition PNeXus.h:1277
std::vector< float > fDeadTime
Dead time values per detector (microseconds) from the file.
Definition PNeXus.h:1279
int fLgbBin
Last good bin for analysis.
Definition PNeXus.h:1278
NeXus HDF5 file reader with case-insensitive path lookup.
Definition PNeXus.h:1463
void WriteFloatDataset(H5::H5File &file, const std::string &path, const PNXdata< float > &data)
Write a float dataset with attributes.
Definition PNeXus.cpp:5270
void HandleIdfV2(H5::H5File &file)
Read datasets for IDF version 2 file structure.
Definition PNeXus.cpp:3715
std::string FindGroupPath(H5::H5File &parent, const std::string &requestedPath)
Definition PNeXus.cpp:3153
void Dump()
Definition PNeXus.cpp:3783
void HandleIdfV1(H5::H5File &file)
Read datasets for IDF version 1 file structure.
Definition PNeXus.cpp:3653
std::string fFileTimeNxs
Definition PNeXus.h:1881
std::string FindAttributeName(H5::H5File &obj, const std::string &requestedName)
Definition PNeXus.cpp:3112
bool RemoveGroupAttribute(const std::string &groupPath, const std::string &attrName)
Remove an attribute from a group.
Definition PNeXus.cpp:5641
void WriteFileAttributes(H5::H5File &file)
Write root-level file attributes.
Definition PNeXus.cpp:5501
bool AddRootAttribute(const std::string &attrName, const std::any &attrValue)
Definition PNeXus.cpp:5734
void ReadFloatDataset(H5::H5File &file, const std::string &path)
Read a float dataset and store in data map.
Definition PNeXus.cpp:4857
std::string fFileName
NeXus HDF5 filename.
Definition PNeXus.h:1875
int ReadNexusFile()
Read and parse the NeXus HDF5 file.
Definition PNeXus.cpp:3553
std::string fHdf5Version
HDF5 version of the file.
Definition PNeXus.h:1878
std::map< std::string, std::map< std::string, std::any > > fGroupAttributes
Map of group paths to their attributes.
Definition PNeXus.h:1885
void ReadIntDataset(H5::H5File &file, const std::string &path)
Read an integer dataset and store in data map.
Definition PNeXus.cpp:4791
void WriteGroupAttributes(H5::Group &group, const std::map< std::string, std::any > &attributes)
Write attributes to a group.
Definition PNeXus.cpp:5418
void WriteDatasetAttributes(H5::DataSet &dataset, const PNXdata< T > &data)
Write dataset attributes from PNXdata object.
Definition PNeXus.cpp:5111
static std::vector< std::string > SplitPath(const std::string &path)
Definition PNeXus.cpp:3074
bool AddGroupAttribute(const std::string &groupPath, const std::string &attrName, const std::any &attrValue)
Definition PNeXus.cpp:5627
void ReadDatasetAttributes(H5::DataSet &dataset, PNXdata< T > &data)
Read dataset attributes and add to PNXdata object.
Definition PNeXus.cpp:4993
H5::Group CreateGroupHierarchy(H5::H5File &file, const std::string &path)
Create nested group hierarchy for a given path.
Definition PNeXus.cpp:5049
void WriteIntDataset(H5::H5File &file, const std::string &path, const PNXdata< int > &data)
Write an integer dataset with attributes.
Definition PNeXus.cpp:5198
bool HasGroupAttribute(const std::string &groupPath, const std::string &attrName) const
Check if a group has a specific attribute.
Definition PNeXus.cpp:5670
std::map< std::string, std::any > fDataMap
Map of HDF5 paths to PNXdata objects.
Definition PNeXus.h:1884
bool ClearGroupAttributes(const std::string &groupPath)
Clear all attributes from a group.
Definition PNeXus.cpp:5715
void ReadStringDataset(H5::H5File &file, const std::string &path)
Read a string dataset and store in data map.
Definition PNeXus.cpp:4922
std::string fHdf5LibVersion
HDF5 library version used when calling PNeXus.
Definition PNeXus.h:1877
const std::map< std::string, std::any > & GetGroupAttributes(const std::string &groupPath) const
Get all attributes for a group.
Definition PNeXus.cpp:5699
bool fPrintDebug
if true print additional debug information
Definition PNeXus.h:1874
std::any GetGroupAttribute(const std::string &groupPath, const std::string &attrName) const
Get an attribute value from a group.
Definition PNeXus.cpp:5688
int fIdfVersion
IDF version of the NeXus file.
Definition PNeXus.h:1876
std::string FindDatasetPath(H5::H5File &parent, const std::string &requestedPath)
Definition PNeXus.cpp:3314
int WriteNexusFile(const std::string &filename, int idfVersion=2)
Definition PNeXus.cpp:5753
std::string fUserV1
Definition PNeXus.h:1883
std::string fFileNameNxs
Definition PNeXus.h:1880
void WriteStringDataset(H5::H5File &file, const std::string &path, const PNXdata< std::string > &data)
Write a string dataset with attributes.
Definition PNeXus.cpp:5343
std::string fNeXusVersion
NeXus version of the file.
Definition PNeXus.h:1879
static bool CaseInsensitiveEquals(const std::string &a, const std::string &b)
Compare two strings case-insensitively.
Definition PNeXus.cpp:3037
H4DataType
HDF4 data type enumeration.
Definition PNeXus.h:329
@ kUINT8
8-bit unsigned integer (DFNT_UINT8)
Definition PNeXus.h:338
@ kFLOAT32
32-bit floating point (DFNT_FLOAT32)
Definition PNeXus.h:331
@ kUINT16
16-bit unsigned integer (DFNT_UINT16)
Definition PNeXus.h:336
@ kINT16
16-bit signed integer (DFNT_INT16)
Definition PNeXus.h:335
@ kUINT32
32-bit unsigned integer (DFNT_UINT32)
Definition PNeXus.h:334
@ kINT8
8-bit signed integer (DFNT_INT8)
Definition PNeXus.h:337
@ kCHAR8
8-bit character (DFNT_CHAR8)
Definition PNeXus.h:333
@ kFLOAT64
64-bit floating point (DFNT_FLOAT64)
Definition PNeXus.h:332
@ kINT32
32-bit signed integer (DFNT_INT32)
Definition PNeXus.h:330
Common utilities for NeXus file handling.
HDFType
Enumeration of supported HDF file types.
Definition PNeXus.h:141
@ HDF5
HDF5 format (magic: 0x89 'H' 'D' 'F' 0x0d 0x0a 0x1a 0x0a).
Definition PNeXus.h:143
@ Unknown
Unrecognized file format.
Definition PNeXus.h:144
@ HDF4
HDF4 format (magic: 0x0e 0x03 0x13 0x01).
Definition PNeXus.h:142
HDFType checkHDFType(const std::string &filename)
Determine the HDF format type of a file by reading its header.
Definition PNeXus.cpp:100
std::string getIso8601TimestampLocal()
get the current time and return it as na IOS8601 time stamp.
Definition PNeXus.cpp:139