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