5594 lines
200 KiB
C++
5594 lines
200 KiB
C++
/***************************************************************************
|
|
|
|
PNeXus.cpp
|
|
|
|
Author: Andreas Suter
|
|
e-mail: andreas.suter@psi.ch
|
|
|
|
***************************************************************************/
|
|
|
|
/***************************************************************************
|
|
* Copyright (C) 2007-2026 by Andreas Suter *
|
|
* andreas.suter@psi.ch *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
* *
|
|
* This program is distributed in the hope that it will be useful, *
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
* GNU General Public License for more details. *
|
|
* *
|
|
* You should have received a copy of the GNU General Public License *
|
|
* along with this program; if not, write to the *
|
|
* Free Software Foundation, Inc., *
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
***************************************************************************/
|
|
|
|
/**
|
|
* @file PNeXus.cpp
|
|
* @brief Implementation of the PNeXus class - NeXus HDF4/HDF5 file reader and writer
|
|
*
|
|
* This file contains the implementation of the PNeXus class for reading and
|
|
* writing ISIS muon NeXus HDF5 files. It provides case-insensitive path
|
|
* lookup functionality using the HDF4 C / HDF5 C++ API.
|
|
*
|
|
* The implementation includes:
|
|
*
|
|
* **Read Functionality:**
|
|
* - String utilities for case-insensitive comparison
|
|
* - Path parsing for HDF4 / HDF5 hierarchical paths
|
|
* - Recursive path traversal with case-insensitive matching
|
|
* - Attribute, group, and dataset lookup functions
|
|
* - Support for IDF version 1 and 2
|
|
*
|
|
* **Write Functionality:**
|
|
* - Group hierarchy creation
|
|
* - Dataset writing for int, float, and string types
|
|
* - Attribute preservation
|
|
* - Multi-dimensional array support
|
|
* - Type-safe operations using PNXdata template class
|
|
*
|
|
* **Error Handling:**
|
|
* - Comprehensive error handling with descriptive exceptions
|
|
* - Validation of data consistency
|
|
* - HDF4 error propagation / HDF5 exception propagation
|
|
*
|
|
* @author Andreas Suter
|
|
* @date 2007-2026
|
|
* @copyright GNU General Public License v2
|
|
* @version 1.0
|
|
*
|
|
* @see nxH4::PNeXus
|
|
* @see nxH4::PNXdata
|
|
* @see nxH5::PNeXus
|
|
* @see nxH5::PNXdata
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <cstring>
|
|
#include <cmath>
|
|
|
|
#include "Minuit2/MnStrategy.h"
|
|
#include "Minuit2/MnMinimize.h"
|
|
#include "Minuit2/FunctionMinimum.h"
|
|
|
|
#include "PNeXus.h"
|
|
|
|
//=============================================================================
|
|
// nxs::checkHDFType - Determine HDF file format from magic bytes
|
|
//=============================================================================
|
|
/**
|
|
* @brief Determine the HDF format type of a file by reading its header
|
|
*
|
|
* Opens the file in binary mode and reads the first 8 bytes to compare
|
|
* against known HDF4 and HDF5 magic byte signatures. This allows automatic
|
|
* format detection before attempting to open the file with the appropriate
|
|
* library.
|
|
*
|
|
* @param filename Path to the file to check
|
|
* @return nxs::HDFType indicating the format (HDF4, HDF5, or Unknown)
|
|
*/
|
|
nxs::HDFType nxs::checkHDFType(const std::string& filename) {
|
|
|
|
std::ifstream file(filename, std::ios::binary);
|
|
if (!file.is_open()) {
|
|
std::cerr << "Error: Cannot open file '" << filename << "'" << std::endl;
|
|
return nxs::HDFType::Unknown;
|
|
}
|
|
|
|
// Read first 8 bytes (enough for both signatures)
|
|
unsigned char header[8];
|
|
file.read(reinterpret_cast<char*>(header), 8);
|
|
|
|
if (file.gcount() < 4) {
|
|
std::cerr << "**Error**: File too small to be HDF4 or HDF5" << std::endl;
|
|
return nxs::HDFType::Unknown;
|
|
}
|
|
|
|
// HDF5 signature: 0x89 'H' 'D' 'F' 0x0d 0x0a 0x1a 0x0a
|
|
const unsigned char hdf5_sig[8] = {0x89, 0x48, 0x44, 0x46, 0x0d, 0x0a, 0x1a, 0x0a};
|
|
|
|
// HDF4 signature: 0x0e 0x03 0x13 0x01
|
|
const unsigned char hdf4_sig[4] = {0x0e, 0x03, 0x13, 0x01};
|
|
|
|
// Check HDF5 first (needs 8 bytes)
|
|
if (file.gcount() >= 8 && std::memcmp(header, hdf5_sig, 8) == 0) {
|
|
return nxs::HDFType::HDF5;
|
|
}
|
|
|
|
// Check HDF4 (needs 4 bytes)
|
|
if (std::memcmp(header, hdf4_sig, 4) == 0) {
|
|
return nxs::HDFType::HDF4;
|
|
}
|
|
|
|
return nxs::HDFType::Unknown;
|
|
}
|
|
|
|
#ifdef HAVE_HDF4
|
|
//=============================================================================
|
|
// nxH4::PNeXusDeadTime Constructor
|
|
//=============================================================================
|
|
nxH4::PNeXusDeadTime::PNeXusDeadTime(const nxH4::PNeXus *nxs, bool debug) : fDebug(debug)
|
|
{
|
|
std::map<std::string, std::any> dataMap = nxs->GetDataMap();
|
|
|
|
int idfVersion = nxs->GetIdfVersion();
|
|
if ((idfVersion != 1) && (idfVersion != 2)) {
|
|
std::stringstream err;
|
|
err << "**ERROR** Found unsupported IDF version '" << idfVersion << "'";
|
|
std::cout << err.str() << std::endl;
|
|
fValid = false;
|
|
return;
|
|
}
|
|
|
|
if (idfVersion == 1) {
|
|
// not yet implemented
|
|
} else { // idfVersion == 2
|
|
// get counts
|
|
if (dataMap.find("/raw_data_1/instrument/detector_1/counts") != dataMap.end()) {
|
|
auto counts_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/counts"]);
|
|
const auto& counts = counts_data.GetData();
|
|
fCounts = counts;
|
|
auto dims = counts_data.GetDimensions();
|
|
if (fDebug) {
|
|
std::cout << " counts dimensions: " << dims[0] << " x "
|
|
<< dims[1] << " x " << dims[2] << std::endl;
|
|
}
|
|
fDims = dims;
|
|
} else {
|
|
std::cout << "**ERROR** Couldn't find 'counts' dataset" << std::endl;
|
|
fValid = false;
|
|
return;
|
|
}
|
|
|
|
// get time_resolution
|
|
if (dataMap.find("/raw_data_1/instrument/detector_1/time_resolution") != dataMap.end()) {
|
|
auto time_res_data = std::any_cast<PNXdata<float>>(dataMap["/raw_data_1/instrument/detector_1/time_resolution"]);
|
|
const auto& time_res = time_res_data.GetData();
|
|
if (time_res.size() > 0) {
|
|
fTimeResolution = time_res[0];
|
|
if (fDebug) {
|
|
std::cout << " time resolution: " << fTimeResolution << " ps" << std::endl;
|
|
}
|
|
}
|
|
} else {
|
|
std::cout << "**ERROR** Couldn't find 'time_resolution' dataset" << std::endl;
|
|
fValid = false;
|
|
return;
|
|
}
|
|
|
|
// get good_frames
|
|
if (dataMap.find("/raw_data_1/good_frames") != dataMap.end()) {
|
|
auto good_frames_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/good_frames"]);
|
|
const auto& good_frames = good_frames_data.GetData();
|
|
if (good_frames.size() > 0) {
|
|
fGoodFrames = good_frames[0];
|
|
if (fDebug) {
|
|
std::cout << " good frames: " << fGoodFrames << std::endl;
|
|
}
|
|
}
|
|
} else {
|
|
std::cout << "**ERROR** Couldn't find 'good_frames' dataset" << std::endl;
|
|
fValid = false;
|
|
return;
|
|
}
|
|
|
|
// get t0_bin, first_good_bin, last_good_bin
|
|
if (dataMap.find("/raw_data_1/instrument/detector_1/spectrum_index") != dataMap.end()) {
|
|
auto spec_idx_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/spectrum_index"]);
|
|
if (spec_idx_data.HasAttribute("t0_bin")) {
|
|
try {
|
|
fT0Bin = std::any_cast<int>(spec_idx_data.GetAttribute("t0_bin"));
|
|
} catch (...) {
|
|
std::cout << "**WARNING** Couldn't read t0_bin attribute" << std::endl;
|
|
}
|
|
}
|
|
|
|
if (spec_idx_data.HasAttribute("first_good_bin")) {
|
|
try {
|
|
fFgbBin = std::any_cast<int>(spec_idx_data.GetAttribute("first_good_bin"));
|
|
} catch (...) {
|
|
std::cout << "**WARNING** Couldn't read first_good_bin attribute" << std::endl;
|
|
}
|
|
}
|
|
|
|
if (spec_idx_data.HasAttribute("last_good_bin")) {
|
|
try {
|
|
fLgbBin = std::any_cast<int>(spec_idx_data.GetAttribute("last_good_bin"));
|
|
} catch (...) {
|
|
std::cout << "**WARNING** Couldn't read last_good_bin attribute" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fDebug) {
|
|
std::cout << " t0_bin: " << fT0Bin << ", first_good_bin: " << fFgbBin
|
|
<< ", last_good_bin: " << fLgbBin << std::endl;
|
|
}
|
|
}
|
|
|
|
fValid = true;
|
|
}
|
|
|
|
//=============================================================================
|
|
// PNeXusDeadTime::operator()
|
|
//=============================================================================
|
|
double nxH4::PNeXusDeadTime::operator()(const std::vector<double> &par) const
|
|
{
|
|
double chisq = 0.0;
|
|
double tau = par[0]; // dead time in microseconds
|
|
|
|
// Convert time resolution from ps to microseconds
|
|
double dt = fTimeResolution * 1.0e-6; // ps -> us
|
|
|
|
// Calculate chi-square for the specified spectrum
|
|
int period = 0; // assuming single period
|
|
|
|
for (unsigned int bin = fFgbBin; bin <= static_cast<unsigned int>(fLgbBin); bin++) {
|
|
int idx = period * fDims[1] * fDims[2] + fIdx * fDims[2] + bin;
|
|
double n_obs = static_cast<double>(fCounts[idx]);
|
|
|
|
if (n_obs > 0) {
|
|
// Dead time correction: n_true = n_obs / (1 - n_obs * tau / (good_frames * dt))
|
|
double correction = 1.0 - (n_obs * tau) / (fGoodFrames * dt);
|
|
if (correction > 0) {
|
|
double n_expected = n_obs / correction;
|
|
double diff = n_obs - n_expected;
|
|
chisq += (diff * diff) / n_expected;
|
|
}
|
|
}
|
|
}
|
|
|
|
return chisq;
|
|
}
|
|
|
|
//=============================================================================
|
|
// PNeXusDeadTime::minimize
|
|
//=============================================================================
|
|
void nxH4::PNeXusDeadTime::minimize(const int i)
|
|
{
|
|
if (i < 0 || i >= static_cast<int>(fDims[1])) {
|
|
std::cerr << "**ERROR** Invalid spectrum index: " << i << std::endl;
|
|
return;
|
|
}
|
|
|
|
fIdx = static_cast<unsigned int>(i);
|
|
|
|
ROOT::Minuit2::MnUserParameters upar;
|
|
upar.Add("dead_time", 0.1, 0.01); // initial value, step size
|
|
upar.SetLimits("dead_time", 0.0, 10.0); // limits in microseconds
|
|
|
|
ROOT::Minuit2::MnMinimize minimize(*this, upar);
|
|
ROOT::Minuit2::FunctionMinimum min = minimize();
|
|
|
|
std::cout << "Spectrum " << i << ": ";
|
|
if (min.IsValid()) {
|
|
double dt = min.UserState().Value("dead_time");
|
|
double dt_err = min.UserState().Error("dead_time");
|
|
std::cout << "Dead time = " << dt << " +/- " << dt_err << " us" << std::endl;
|
|
} else {
|
|
std::cout << "Minimization failed" << std::endl;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// PNeXus Default Constructor
|
|
//=============================================================================
|
|
nxH4::PNeXus::PNeXus() : fSdId(-1), fFileId(-1)
|
|
{
|
|
}
|
|
|
|
//=============================================================================
|
|
// PNeXus Constructor with filename
|
|
//=============================================================================
|
|
nxH4::PNeXus::PNeXus(const std::string fln, const bool printDebug)
|
|
: fPrintDebug(printDebug), fFileName(fln), fSdId(-1), fFileId(-1)
|
|
{
|
|
if (ReadNexusFile() != 0) {
|
|
throw std::runtime_error("Failed to read NeXus file: " + fln);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// PNeXus Destructor
|
|
//=============================================================================
|
|
nxH4::PNeXus::~PNeXus()
|
|
{
|
|
if (fSdId != -1) {
|
|
SDend(fSdId);
|
|
}
|
|
if (fFileId != -1) {
|
|
Hclose(fFileId);
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// PNeXus::ReadNexusFile
|
|
//=============================================================================
|
|
int nxH4::PNeXus::ReadNexusFile()
|
|
{
|
|
// Open the HDF4 file
|
|
fSdId = SDstart(fFileName.c_str(), DFACC_READ);
|
|
if (fSdId == FAIL) {
|
|
std::cerr << "**ERROR** Failed to open HDF4 file: " << fFileName << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
fFileId = Hopen(fFileName.c_str(), DFACC_READ, 0);
|
|
if (fFileId == FAIL) {
|
|
std::cerr << "**ERROR** Failed to open HDF4 file (H interface): " << fFileName << std::endl;
|
|
SDend(fSdId);
|
|
fSdId = -1;
|
|
return 1;
|
|
}
|
|
|
|
// Read file-level attributes
|
|
int32 n_datasets, n_file_attrs;
|
|
if (SDfileinfo(fSdId, &n_datasets, &n_file_attrs) == FAIL) {
|
|
std::cerr << "**ERROR** Failed to get file info" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "Number of datasets: " << n_datasets << std::endl;
|
|
std::cout << "Number of file attributes: " << n_file_attrs << std::endl;
|
|
}
|
|
|
|
// Read HDF4 version and NeXus version from attributes
|
|
char attr_name[H4_MAX_NC_NAME];
|
|
int32 attr_type, attr_count;
|
|
|
|
for (int32 i = 0; i < n_file_attrs; i++) {
|
|
if (SDattrinfo(fSdId, i, attr_name, &attr_type, &attr_count) == FAIL) {
|
|
continue;
|
|
}
|
|
|
|
if (caseInsensitiveEquals(attr_name, "HDF_version") ||
|
|
caseInsensitiveEquals(attr_name, "HDF4_version")) {
|
|
std::vector<char> buffer(attr_count + 1, '\0');
|
|
if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
|
|
fHdf4Version = std::string(buffer.data());
|
|
}
|
|
} else if (caseInsensitiveEquals(attr_name, "NeXus_version")) {
|
|
std::vector<char> buffer(attr_count + 1, '\0');
|
|
if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
|
|
fNeXusVersion = std::string(buffer.data());
|
|
}
|
|
} else if (caseInsensitiveEquals(attr_name, "file_name")) {
|
|
std::vector<char> buffer(attr_count + 1, '\0');
|
|
if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
|
|
fFileNameNxs = std::string(buffer.data());
|
|
}
|
|
} else if (caseInsensitiveEquals(attr_name, "file_time")) {
|
|
std::vector<char> buffer(attr_count + 1, '\0');
|
|
if (SDreadattr(fSdId, i, buffer.data()) != FAIL) {
|
|
fFileTimeNxs = std::string(buffer.data());
|
|
}
|
|
}
|
|
}
|
|
|
|
// Determine IDF version
|
|
// Try to find IDF_version dataset - check multiple possible locations
|
|
|
|
// First try simple root-level name
|
|
int32 idf_idx = SDnametoindex(fSdId, "IDF_version");
|
|
if (idf_idx != FAIL) {
|
|
int32 sds_id = SDselect(fSdId, idf_idx);
|
|
if (sds_id != FAIL) {
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
char name[H4_MAX_NC_NAME];
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
|
|
if (data_type == DFNT_INT32) {
|
|
int32 idf_val;
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
int32 edges[H4_MAX_VAR_DIMS] = {1};
|
|
if (SDreaddata(sds_id, start, nullptr, edges, &idf_val) != FAIL) {
|
|
fIdfVersion = idf_val;
|
|
}
|
|
}
|
|
}
|
|
SDendaccess(sds_id);
|
|
}
|
|
}
|
|
|
|
// If not found, try /run/IDF_version (IDF version 1 location)
|
|
if (fIdfVersion == -1) {
|
|
try {
|
|
int32 sds_ref = findDatasetRefByPath("/run/IDF_version");
|
|
if (sds_ref != -1) {
|
|
int32 sds_idx = SDreftoindex(fSdId, sds_ref);
|
|
if (sds_idx != FAIL) {
|
|
int32 sds_id = SDselect(fSdId, sds_idx);
|
|
if (sds_id != FAIL) {
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
char name[H4_MAX_NC_NAME];
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
|
|
if (data_type == DFNT_INT32) {
|
|
int32 idf_val;
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
int32 edges[H4_MAX_VAR_DIMS] = {1};
|
|
if (SDreaddata(sds_id, start, nullptr, edges, &idf_val) != FAIL) {
|
|
fIdfVersion = idf_val;
|
|
}
|
|
}
|
|
}
|
|
SDendaccess(sds_id);
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
// Path not found, continue to next attempt
|
|
}
|
|
}
|
|
|
|
// If still not found, try /raw_data_1/idf_version (IDF version 2 location)
|
|
if (fIdfVersion == -1) {
|
|
try {
|
|
int32 sds_ref = findDatasetRefByPath("/raw_data_1/idf_version");
|
|
if (sds_ref != -1) {
|
|
int32 sds_idx = SDreftoindex(fSdId, sds_ref);
|
|
if (sds_idx != FAIL) {
|
|
int32 sds_id = SDselect(fSdId, sds_idx);
|
|
if (sds_id != FAIL) {
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
char name[H4_MAX_NC_NAME];
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
|
|
if (data_type == DFNT_INT32) {
|
|
int32 idf_val;
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
int32 edges[H4_MAX_VAR_DIMS] = {1};
|
|
if (SDreaddata(sds_id, start, nullptr, edges, &idf_val) != FAIL) {
|
|
fIdfVersion = idf_val;
|
|
}
|
|
}
|
|
}
|
|
SDendaccess(sds_id);
|
|
}
|
|
}
|
|
}
|
|
} catch (...) {
|
|
// Path not found, will use default
|
|
}
|
|
}
|
|
|
|
if (fIdfVersion == -1) {
|
|
// Try alternate approach - check for raw_data_1 structure
|
|
// If we find raw_data_1/detector_1, assume IDF v2
|
|
// Otherwise assume IDF v1
|
|
fIdfVersion = 2; // default to version 2
|
|
}
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "HDF4 Version: " << fHdf4Version << std::endl;
|
|
std::cout << "NeXus Version: " << fNeXusVersion << std::endl;
|
|
std::cout << "IDF Version: " << fIdfVersion << std::endl;
|
|
}
|
|
|
|
// Read datasets based on IDF version
|
|
if (fIdfVersion == 1) {
|
|
HandleIdfV1(fSdId);
|
|
} else if (fIdfVersion == 2) {
|
|
HandleIdfV2(fSdId);
|
|
} else {
|
|
std::cerr << "**ERROR** Unsupported IDF version: " << fIdfVersion << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::HandleIdfV1
|
|
//=============================================================================
|
|
void nxH4::PNeXus::HandleIdfV1(int32 sd_id)
|
|
{
|
|
// IDF version 1 handling - not yet implemented
|
|
if (fPrintDebug) {
|
|
std::cout << "IDF version 1 handling ..." << std::endl;
|
|
}
|
|
|
|
std::vector<std::string> int_datasets = {
|
|
"/run/IDF_version",
|
|
"/run/number",
|
|
"/run/switching_states",
|
|
"/run/instrument/detector/number",
|
|
"/run/instrument/beam/frames_good",
|
|
"/run/histogram_data_1/counts",
|
|
"/run/histogram_data_1/resolution",
|
|
"/run/histogram_data_1/grouping"
|
|
};
|
|
|
|
std::vector<std::string> float_datasets = {
|
|
"/run/sample/temperature",
|
|
"/run/sample/magnetic_field",
|
|
"/run/sample/magnetic_field_vector",
|
|
"/run/instrument/detector/deadtimes",
|
|
"/run/histogram_data_1/time_zero",
|
|
"/run/histogram_data_1/raw_time",
|
|
"/run/histogram_data_1/corrected_time",
|
|
"/run/histogram_data_1/alpha"
|
|
};
|
|
|
|
std::vector<std::string> string_datasets = {
|
|
"/run/program_name",
|
|
"/run/title",
|
|
"/run/notes",
|
|
"/run/analysis",
|
|
"/run/lab",
|
|
"/run/beamline",
|
|
"/run/start_time",
|
|
"/run/stop_time",
|
|
"/run/user/name",
|
|
"/run/user/experiment_number",
|
|
"/run/sample/name",
|
|
"/run/sample/magnetic_field_state",
|
|
"/run/sample/environment",
|
|
"/run/instrument/name",
|
|
"/run/instrument/collimator/type"
|
|
};
|
|
|
|
// Read integer datasets
|
|
for (const auto& path : int_datasets) {
|
|
try {
|
|
ReadIntDataset(sd_id, path);
|
|
} catch (const std::exception& e) {
|
|
if (fPrintDebug) {
|
|
std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read float datasets
|
|
for (const auto& path : float_datasets) {
|
|
try {
|
|
ReadFloatDataset(sd_id, path);
|
|
} catch (const std::exception& e) {
|
|
if (fPrintDebug) {
|
|
std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read string datasets
|
|
for (const auto& path : string_datasets) {
|
|
try {
|
|
ReadStringDataset(sd_id, path);
|
|
} catch (const std::exception& e) {
|
|
if (fPrintDebug) {
|
|
std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::HandleIdfV2
|
|
//=============================================================================
|
|
void nxH4::PNeXus::HandleIdfV2(int32 sd_id)
|
|
{
|
|
// Read common datasets for IDF version 2
|
|
// These are typical datasets found in muon NeXus files
|
|
|
|
std::vector<std::string> int_datasets = {
|
|
"/raw_data_1/instrument/detector_1/counts",
|
|
"/raw_data_1/instrument/detector_1/spectrum_index",
|
|
"/raw_data_1/good_frames",
|
|
"/raw_data_1/instrument/detector_1/raw_time",
|
|
"/raw_data_1/run_number"
|
|
};
|
|
|
|
std::vector<std::string> float_datasets = {
|
|
"/raw_data_1/instrument/detector_1/resolution",
|
|
"/raw_data_1/instrument/detector_1/time_zero"
|
|
};
|
|
|
|
std::vector<std::string> string_datasets = {
|
|
"/raw_data_1/name",
|
|
"/raw_data_1/title",
|
|
"/raw_data_1/start_time",
|
|
"/raw_data_1/end_time"
|
|
};
|
|
|
|
// Read integer datasets
|
|
for (const auto& path : int_datasets) {
|
|
try {
|
|
ReadIntDataset(sd_id, path);
|
|
} catch (const std::exception& e) {
|
|
if (fPrintDebug) {
|
|
std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read float datasets
|
|
for (const auto& path : float_datasets) {
|
|
try {
|
|
ReadFloatDataset(sd_id, path);
|
|
} catch (const std::exception& e) {
|
|
if (fPrintDebug) {
|
|
std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Read string datasets
|
|
for (const auto& path : string_datasets) {
|
|
try {
|
|
ReadStringDataset(sd_id, path);
|
|
} catch (const std::exception& e) {
|
|
if (fPrintDebug) {
|
|
std::cout << "Note: Could not read " << path << ": " << e.what() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::ReadIntDataset
|
|
//=============================================================================
|
|
void nxH4::PNeXus::ReadIntDataset(int32 sd_id, const std::string& path)
|
|
{
|
|
// Extract dataset name from path
|
|
std::vector<std::string> components = splitPath(path);
|
|
if (components.empty()) {
|
|
throw std::runtime_error("Invalid path: " + path);
|
|
}
|
|
std::string dataset_name = components.back();
|
|
|
|
// Use VGroup navigation to find the correct dataset reference
|
|
int32 sds_ref = -1;
|
|
if (components.size() > 1) {
|
|
// Try to resolve via VGroup hierarchy
|
|
sds_ref = findDatasetRefByPath(path);
|
|
}
|
|
|
|
int32 sds_idx = -1;
|
|
if (sds_ref != -1) {
|
|
// Find SDS index from reference
|
|
sds_idx = SDreftoindex(sd_id, sds_ref);
|
|
}
|
|
|
|
// Fallback to name-based search if VGroup navigation failed
|
|
if (sds_idx == FAIL || sds_idx == -1) {
|
|
sds_idx = findDatasetIndex(sd_id, dataset_name);
|
|
}
|
|
|
|
int32 sds_id = SDselect(sd_id, sds_idx);
|
|
if (sds_id == FAIL) {
|
|
throw std::runtime_error("Failed to select dataset: " + dataset_name);
|
|
}
|
|
|
|
// Get dataset info
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
char name[H4_MAX_NC_NAME];
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to get dataset info: " + dataset_name);
|
|
}
|
|
|
|
// Calculate total number of elements
|
|
int32 n_elements = 1;
|
|
std::vector<uint32_t> dimensions;
|
|
for (int32 i = 0; i < rank; i++) {
|
|
n_elements *= dim_sizes[i];
|
|
dimensions.push_back(static_cast<uint32_t>(dim_sizes[i]));
|
|
}
|
|
|
|
// Read data
|
|
std::vector<int32> data(n_elements);
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
if (SDreaddata(sds_id, start, nullptr, dim_sizes, data.data()) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to read dataset: " + dataset_name);
|
|
}
|
|
|
|
// Convert to int vector
|
|
std::vector<int> int_data(data.begin(), data.end());
|
|
|
|
// Create PNXdata object
|
|
PNXdata<int> pnx_data(H4DataType::INT32);
|
|
pnx_data.SetData(int_data);
|
|
pnx_data.SetDimensions(dimensions);
|
|
|
|
// Read attributes
|
|
ReadDatasetAttributes(sds_id, pnx_data);
|
|
|
|
// Store in data map
|
|
fDataMap[path] = pnx_data;
|
|
|
|
SDendaccess(sds_id);
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::ReadFloatDataset
|
|
//=============================================================================
|
|
void nxH4::PNeXus::ReadFloatDataset(int32 sd_id, const std::string& path)
|
|
{
|
|
// Extract dataset name from path
|
|
std::vector<std::string> components = splitPath(path);
|
|
if (components.empty()) {
|
|
throw std::runtime_error("Invalid path: " + path);
|
|
}
|
|
std::string dataset_name = components.back();
|
|
|
|
// Use VGroup navigation to find the correct dataset reference
|
|
int32 sds_ref = -1;
|
|
if (components.size() > 1) {
|
|
// Try to resolve via VGroup hierarchy
|
|
sds_ref = findDatasetRefByPath(path);
|
|
}
|
|
|
|
int32 sds_idx = -1;
|
|
if (sds_ref != -1) {
|
|
// Find SDS index from reference
|
|
sds_idx = SDreftoindex(sd_id, sds_ref);
|
|
}
|
|
|
|
// Fallback to name-based search if VGroup navigation failed
|
|
if (sds_idx == FAIL || sds_idx == -1) {
|
|
sds_idx = findDatasetIndex(sd_id, dataset_name);
|
|
}
|
|
|
|
int32 sds_id = SDselect(sd_id, sds_idx);
|
|
if (sds_id == FAIL) {
|
|
throw std::runtime_error("Failed to select dataset: " + dataset_name);
|
|
}
|
|
|
|
// Get dataset info
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
char name[H4_MAX_NC_NAME];
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to get dataset info: " + dataset_name);
|
|
}
|
|
|
|
// Calculate total number of elements
|
|
int32 n_elements = 1;
|
|
std::vector<uint32_t> dimensions;
|
|
for (int32 i = 0; i < rank; i++) {
|
|
n_elements *= dim_sizes[i];
|
|
dimensions.push_back(static_cast<uint32_t>(dim_sizes[i]));
|
|
}
|
|
|
|
// Read data
|
|
std::vector<float32> data(n_elements);
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
if (SDreaddata(sds_id, start, nullptr, dim_sizes, data.data()) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to read dataset: " + dataset_name);
|
|
}
|
|
|
|
// Convert to float vector
|
|
std::vector<float> float_data(data.begin(), data.end());
|
|
|
|
// Create PNXdata object
|
|
PNXdata<float> pnx_data(H4DataType::FLOAT32);
|
|
pnx_data.SetData(float_data);
|
|
pnx_data.SetDimensions(dimensions);
|
|
|
|
// Read attributes
|
|
ReadDatasetAttributes(sds_id, pnx_data);
|
|
|
|
// Store in data map
|
|
fDataMap[path] = pnx_data;
|
|
|
|
SDendaccess(sds_id);
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::ReadStringDataset
|
|
//=============================================================================
|
|
void nxH4::PNeXus::ReadStringDataset(int32 sd_id, const std::string& path)
|
|
{
|
|
// Extract dataset name from path
|
|
std::vector<std::string> components = splitPath(path);
|
|
if (components.empty()) {
|
|
throw std::runtime_error("Invalid path: " + path);
|
|
}
|
|
std::string dataset_name = components.back();
|
|
|
|
// Use VGroup navigation to find the correct dataset reference
|
|
int32 sds_ref = -1;
|
|
if (components.size() > 1) {
|
|
// Try to resolve via VGroup hierarchy
|
|
sds_ref = findDatasetRefByPath(path);
|
|
}
|
|
|
|
int32 sds_idx = -1;
|
|
if (sds_ref != -1) {
|
|
// Find SDS index from reference
|
|
sds_idx = SDreftoindex(sd_id, sds_ref);
|
|
}
|
|
|
|
// Fallback to name-based search if VGroup navigation failed
|
|
if (sds_idx == FAIL || sds_idx == -1) {
|
|
sds_idx = findDatasetIndex(sd_id, dataset_name);
|
|
}
|
|
|
|
int32 sds_id = SDselect(sd_id, sds_idx);
|
|
if (sds_id == FAIL) {
|
|
throw std::runtime_error("Failed to select dataset: " + dataset_name);
|
|
}
|
|
|
|
// Get dataset info
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
char name[H4_MAX_NC_NAME];
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to get dataset info: " + dataset_name);
|
|
}
|
|
|
|
// Calculate total size for string
|
|
int32 n_elements = 1;
|
|
std::vector<uint32_t> dimensions;
|
|
for (int32 i = 0; i < rank; i++) {
|
|
n_elements *= dim_sizes[i];
|
|
dimensions.push_back(static_cast<uint32_t>(dim_sizes[i]));
|
|
}
|
|
|
|
// Read data as char array
|
|
std::vector<char> data(n_elements + 1, '\0');
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
if (SDreaddata(sds_id, start, nullptr, dim_sizes, data.data()) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to read dataset: " + dataset_name);
|
|
}
|
|
|
|
// Create string
|
|
std::vector<std::string> string_data;
|
|
string_data.push_back(std::string(data.data()));
|
|
|
|
// Create PNXdata object
|
|
PNXdata<std::string> pnx_data(H4DataType::CHAR8);
|
|
pnx_data.SetData(string_data);
|
|
pnx_data.SetDimensions(dimensions);
|
|
|
|
// Read attributes
|
|
ReadDatasetAttributes(sds_id, pnx_data);
|
|
|
|
// Store in data map
|
|
fDataMap[path] = pnx_data;
|
|
|
|
SDendaccess(sds_id);
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::ReadDatasetAttributes (template specializations)
|
|
//=============================================================================
|
|
template <typename T>
|
|
void nxH4::PNeXus::ReadDatasetAttributes(int32 sds_id, PNXdata<T>& data)
|
|
{
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
char name[H4_MAX_NC_NAME];
|
|
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_attrs) == FAIL) {
|
|
return;
|
|
}
|
|
|
|
for (int32 i = 0; i < n_attrs; i++) {
|
|
char attr_name[H4_MAX_NC_NAME];
|
|
int32 attr_type, attr_count;
|
|
|
|
if (SDattrinfo(sds_id, i, attr_name, &attr_type, &attr_count) == FAIL) {
|
|
continue;
|
|
}
|
|
|
|
// Read attribute based on type - store as primitives like h5nexus
|
|
if (attr_type == DFNT_INT32) {
|
|
if (attr_count == 1) {
|
|
// Single value - store as int directly
|
|
int32 value;
|
|
if (SDreadattr(sds_id, i, &value) != FAIL) {
|
|
data.AddAttribute(attr_name, static_cast<int>(value));
|
|
}
|
|
} else {
|
|
// Multiple values - store as vector
|
|
std::vector<int32> attr_data(attr_count);
|
|
if (SDreadattr(sds_id, i, attr_data.data()) != FAIL) {
|
|
std::vector<int> int_data(attr_data.begin(), attr_data.end());
|
|
data.AddAttribute(attr_name, int_data);
|
|
}
|
|
}
|
|
} else if (attr_type == DFNT_FLOAT32) {
|
|
if (attr_count == 1) {
|
|
// Single value - store as float directly
|
|
float32 value;
|
|
if (SDreadattr(sds_id, i, &value) != FAIL) {
|
|
data.AddAttribute(attr_name, static_cast<float>(value));
|
|
}
|
|
} else {
|
|
// Multiple values - store as vector
|
|
std::vector<float32> attr_data(attr_count);
|
|
if (SDreadattr(sds_id, i, attr_data.data()) != FAIL) {
|
|
std::vector<float> float_data(attr_data.begin(), attr_data.end());
|
|
data.AddAttribute(attr_name, float_data);
|
|
}
|
|
}
|
|
} else if (attr_type == DFNT_CHAR8 || attr_type == DFNT_UCHAR8) {
|
|
// String attributes - always store as string
|
|
std::vector<char> attr_data(attr_count + 1, '\0');
|
|
if (SDreadattr(sds_id, i, attr_data.data()) != FAIL) {
|
|
data.AddAttribute(attr_name, std::string(attr_data.data()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::Dump
|
|
//=============================================================================
|
|
void nxH4::PNeXus::Dump()
|
|
{
|
|
int first_good_bin{0};
|
|
|
|
if (fIdfVersion == 1) {
|
|
std::cout << std::endl;
|
|
std::cout << "========================================" << std::endl;
|
|
std::cout << "NeXus File Dump" << std::endl;
|
|
std::cout << "========================================" << std::endl;
|
|
std::cout << "Filename: " << fFileName << std::endl;
|
|
std::cout << "HDF4 Version: " << fHdf4Version << std::endl;
|
|
std::cout << "NeXus Version: " << fNeXusVersion << std::endl;
|
|
std::cout << "IDF Version: " << fIdfVersion << std::endl;
|
|
std::cout << "----------------------------------------" << std::endl;
|
|
|
|
std::cout << std::endl << "++++";
|
|
std::cout << std::endl << "run";
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " IDF_version: " << fIdfVersion;
|
|
if (fDataMap.find("/run/program_name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/program_name"]);
|
|
std::cout << std::endl << " program_name : " << str_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (str_data.HasAttribute("version")) {
|
|
try {
|
|
auto version = std::any_cast<std::string>(str_data.GetAttribute("version"));
|
|
std::cout << " version : " << version;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast version attribute" << std::endl;
|
|
}
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast program_name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " program_name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/number") != fDataMap.end()) {
|
|
try {
|
|
auto number = std::any_cast<PNXdata<int>>(fDataMap["/run/number"]);
|
|
std::cout << std::endl << " number : " << number.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " number : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/title") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/title"]);
|
|
std::cout << std::endl << " title : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast title data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " title : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/notes") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/notes"]);
|
|
std::cout << std::endl << " notes : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast notes data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " notes : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/analysis") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/analysis"]);
|
|
std::cout << std::endl << " analysis : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast analysis data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " analysis : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/lab") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/lab"]);
|
|
std::cout << std::endl << " lab : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast lab data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " lab : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/beamline") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/beamline"]);
|
|
std::cout << std::endl << " beamline : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast beamline data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " beamline : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/start_time") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/start_time"]);
|
|
std::cout << std::endl << " start_time : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast start_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " start_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/stop_time") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/stop_time"]);
|
|
std::cout << std::endl << " stop_time : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast stop_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " stop_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/switching_state") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/switching_state"]);
|
|
std::cout << std::endl << " switching_state : " << int_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast switching_state data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " switching_state : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " user";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/user/name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/name"]);
|
|
std::cout << std::endl << " name : '" << str_data.GetData()[0] << "'";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/user/experiment_number") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/experiment_number"]);
|
|
std::cout << std::endl << " experiment_number : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast experiment_number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " experiment_number : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " sample";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/sample/name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/name"]);
|
|
std::cout << std::endl << " name : '" << str_data.GetData()[0] << "'";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/temperature") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/temperature"]);
|
|
std::cout << std::endl << " temperature : " << float_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast temperature data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " temperature : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/magnetic_field") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field"]);
|
|
std::cout << std::endl << " magnetic_field : " << float_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast magnetic_field data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " magnetic_field : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/magnetic_field_vector") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field_vector"]);
|
|
std::cout << std::endl << " magnetic_field_vector : " << float_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (float_data.HasAttribute("coordinate_system")) {
|
|
try {
|
|
auto coordinate_system = std::any_cast<std::string>(float_data.GetAttribute("coordinate_system"));
|
|
std::cout << std::endl << " coordinate_system : " << coordinate_system << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast coordinate_system attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " coordinate_system : n/a" << std::endl;
|
|
}
|
|
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
|
|
if (float_data.HasAttribute("available")) {
|
|
try {
|
|
auto available = std::any_cast<int>(float_data.GetAttribute("available"));
|
|
std::cout << " available : " << available;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast available attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " available : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast magnetic_field_vector data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " magnetic_field_vector : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/environment") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/environment"]);
|
|
std::cout << std::endl << " environment : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast environment data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " environment : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " instrument";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/name"]);
|
|
std::cout << std::endl << " name : '" << str_data.GetData()[0] << "'";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " detector";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/detector/number") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/instrument/detector/number"]);
|
|
std::cout << std::endl << " number : " << int_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " number : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/instrument/detector/deadtimes") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/instrument/detector/deadtimes"]);
|
|
auto data = float_data.GetData();
|
|
unsigned int end{15};
|
|
if (data.size() < end)
|
|
end = data.size();
|
|
std::cout << std::endl << " deadtimes: ";
|
|
for (unsigned int i=0; i<end; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast deadtimes data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " deadtimes: n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " beam";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/beam/frames_good") != fDataMap.end()) {
|
|
try {
|
|
auto fg = std::any_cast<PNXdata<int>>(fDataMap["/run/instrument/beam/frames_good"]);
|
|
std::cout << std::endl << " good_frames : " << fg.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast good_frames data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " good_frames : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " collimator";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/collimator/type") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/collimator/type"]);
|
|
std::cout << std::endl << " type : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast type data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " type : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " histogram_data_1";
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " counts:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/counts"]);
|
|
|
|
// Check for attributes
|
|
if (counts_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(counts_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("signal")) {
|
|
try {
|
|
auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
|
|
std::cout << " signal : " << signal << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast signal attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " signal : n/a" << std::endl;
|
|
}
|
|
|
|
int noOfHistos{0};
|
|
if (counts_data.HasAttribute("number")) {
|
|
try {
|
|
noOfHistos = std::any_cast<int>(counts_data.GetAttribute("number"));
|
|
std::cout << " number : " << noOfHistos << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast number attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " number : n/a" << std::endl;
|
|
}
|
|
|
|
int histoLength{0};
|
|
if (counts_data.HasAttribute("length")) {
|
|
try {
|
|
histoLength = std::any_cast<int>(counts_data.GetAttribute("length"));
|
|
std::cout << " length : " << histoLength << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast length attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " length : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("t0_bin")) {
|
|
try {
|
|
auto t0_bin = std::any_cast<int>(counts_data.GetAttribute("t0_bin"));
|
|
std::cout << " t0_bin : " << t0_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast t0_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " t0_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("first_good_bin")) {
|
|
try {
|
|
first_good_bin = std::any_cast<int>(counts_data.GetAttribute("first_good_bin"));
|
|
std::cout << " first_good_bin : " << first_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast first_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " first_good_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("last_good_bin")) {
|
|
try {
|
|
auto last_good_bin = std::any_cast<int>(counts_data.GetAttribute("last_good_bin"));
|
|
std::cout << " last_good_bin : " << last_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast last_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " last_good_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("offset")) {
|
|
try {
|
|
auto offset = std::any_cast<float>(counts_data.GetAttribute("offset"));
|
|
std::cout << " offset : " << offset << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast offset attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " offset : n/a" << std::endl;
|
|
}
|
|
|
|
// dump the first couple of counts of each detector
|
|
const auto& data = counts_data.GetData();
|
|
std::cout << std::endl << " first couple of counts of each detector:";
|
|
for (unsigned int i=0; i<noOfHistos; i++) {
|
|
if (i<9)
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
else
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
for (unsigned int j=0; j<first_good_bin+5; j++)
|
|
std::cout << data[i*histoLength+j] << ", ";
|
|
std::cout << "...";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast counts data" << std::endl;
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/resolution") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/resolution"]);
|
|
std::cout << std::endl << " resolution : " << int_data.GetData()[0];
|
|
|
|
if (int_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
|
|
std::cout << " units : " << units;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " units : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast resolution data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " resolution : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/time_zero") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/time_zero"]);
|
|
std::cout << std::endl << " time_zero : " << float_data.GetData()[0];
|
|
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << std::endl << " units : " << units;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " units : n/a";
|
|
}
|
|
|
|
if (float_data.HasAttribute("available")) {
|
|
try {
|
|
auto available = std::any_cast<int>(float_data.GetAttribute("available"));
|
|
std::cout << std::endl << " available : " << available;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast available attribute";
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " available : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast time_zero data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " time_zero : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/raw_time") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/raw_time"]);
|
|
std::cout << std::endl << " raw_time : " << float_data.GetData()[0];
|
|
if (float_data.HasAttribute("axis")) {
|
|
try {
|
|
auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
|
|
std::cout << std::endl << " axis : " << axis;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast axis attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " axis : n/a";
|
|
}
|
|
|
|
if (float_data.HasAttribute("primary")) {
|
|
try {
|
|
auto primary = std::any_cast<int>(float_data.GetAttribute("primary"));
|
|
std::cout << std::endl << " primary : " << primary;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast primary attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " primary : n/a";
|
|
}
|
|
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << std::endl << " units : " << units;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " units : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast raw_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " raw_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/corrected_time") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/corrected_time"]);
|
|
std::cout << std::endl << " corrected_time : " << float_data.GetData()[0];
|
|
if (float_data.HasAttribute("axis")) {
|
|
try {
|
|
auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
|
|
std::cout << std::endl << " axis : " << axis;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast axis attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " axis : n/a";
|
|
}
|
|
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << std::endl << " units : " << units;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " units : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast raw_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " corrected_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/grouping") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/grouping"]);
|
|
auto data = int_data.GetData();
|
|
unsigned int end{15};
|
|
if (data.size() < end)
|
|
end = data.size();
|
|
std::cout << std::endl << " grouping : ";
|
|
for (unsigned int i=0; i<end; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
if (int_data.HasAttribute("available")) {
|
|
try {
|
|
auto available = std::any_cast<int>(int_data.GetAttribute("available"));
|
|
std::cout << std::endl << " available : " << available;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast available attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " available : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast grouping data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " grouping : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/alpha") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/alpha"]);
|
|
auto data = float_data.GetData();
|
|
unsigned int end{15};
|
|
if (data.size() < end)
|
|
end = data.size();
|
|
std::cout << std::endl << " alpha : ";
|
|
for (unsigned int i=0; i<end; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
if (float_data.HasAttribute("available")) {
|
|
try {
|
|
auto available = std::any_cast<int>(float_data.GetAttribute("available"));
|
|
std::cout << std::endl << " available : " << available;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "**ERROR**: Failed to cast available attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " alpha : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "**ERROR**: Failed to cast alpha data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " available : n/a";
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << "========================================" << std::endl;
|
|
std::cout << std::endl;
|
|
} else { // IDF Version 2
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << "hdf5-NeXus file content of file:' " << fFileName << "'";
|
|
std::cout << std::endl << "****";
|
|
std::cout << std::endl << "Top Level Attributes:";
|
|
std::cout << std::endl << " HDF4 Version : " << fHdf4Version;
|
|
std::cout << std::endl << " NeXus Version: " << fNeXusVersion;
|
|
std::cout << std::endl << " file_name : " << fFileNameNxs;
|
|
std::cout << std::endl << " file_time : " << fFileTimeNxs;
|
|
std::cout << std::endl << "++++";
|
|
std::cout << std::endl << "raw_data_1";
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " IDF_version: " << fIdfVersion;
|
|
if (fDataMap.find("/raw_data_1/beamline") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/beamline"]);
|
|
std::cout << std::endl << " beamline : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " beamline : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/definition") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/definition"]);
|
|
std::cout << std::endl << " definition : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast definition data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " definition : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/run_number") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/run_number"]);
|
|
std::cout << std::endl << " run_number : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast run_number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " run_number : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/title") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/title"]);
|
|
std::cout << std::endl << " title : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast title data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " title : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/start_time") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/start_time"]);
|
|
std::cout << std::endl << " start_time : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast start_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " start_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/end_time") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/end_time"]);
|
|
std::cout << std::endl << " end_time : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast end_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " end_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/good_frames") != fDataMap.end()) {
|
|
try {
|
|
auto good_frames = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/good_frames"]);
|
|
std::cout << std::endl << " good_frames: " << good_frames.GetData()[0];
|
|
} catch(const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast good_frames data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " good_frames: n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/experiment_identifier") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/experiment_identifier"]);
|
|
std::cout << std::endl << " experiment_identifier: " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast experiment_identifier data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " experiment_identifier: n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " instrument";
|
|
if (fDataMap.find("/raw_data_1/instrument/name") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/name"]);
|
|
std::cout << std::endl << " name : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " source";
|
|
if (fDataMap.find("/raw_data_1/instrument/source/name") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/name"]);
|
|
std::cout << std::endl << " name : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/source/name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/instrument/source/type") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/type"]);
|
|
std::cout << std::endl << " type : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/source/type data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " type : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/instrument/source/probe") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/probe"]);
|
|
std::cout << std::endl << " probe : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/source/probe data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " probe : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " detector_1";
|
|
std::cout << std::endl << " counts:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/counts"]);
|
|
auto dims = counts_data.GetDimensions();
|
|
std::cout << " counts dimensions: " << dims[0] << " x "
|
|
<< dims[1] << " x " << dims[2] << std::endl;
|
|
std::cout << " total elements: " << counts_data.GetNumElements() << std::endl;
|
|
|
|
// Check for attributes
|
|
if (counts_data.HasAttribute("signal")) {
|
|
try {
|
|
auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
|
|
std::cout << " signal : " << signal << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast signal attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " signal : n/a";
|
|
}
|
|
|
|
if (counts_data.HasAttribute("axes")) {
|
|
try {
|
|
auto axes = std::any_cast<std::string>(counts_data.GetAttribute("axes"));
|
|
std::cout << " axes : " << axes << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast axes attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " axes : n/a";
|
|
}
|
|
|
|
if (counts_data.HasAttribute("long_name")) {
|
|
try {
|
|
auto long_name = std::any_cast<std::string>(counts_data.GetAttribute("long_name"));
|
|
std::cout << " long_name : " << long_name << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast long_name attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " long_name : n/a";
|
|
}
|
|
|
|
if (counts_data.HasAttribute("t0_bin")) {
|
|
try {
|
|
auto t0_bin = std::any_cast<int32_t>(counts_data.GetAttribute("t0_bin"));
|
|
std::cout << " t0_bin : " << t0_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " t0_bin : n/a";
|
|
}
|
|
|
|
if (counts_data.HasAttribute("first_good_bin")) {
|
|
try {
|
|
first_good_bin = std::any_cast<int32_t>(counts_data.GetAttribute("first_good_bin"));
|
|
std::cout << " first_good_bin : " << first_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " first_good_bin : n/a";
|
|
}
|
|
|
|
if (counts_data.HasAttribute("last_good_bin")) {
|
|
try {
|
|
auto last_good_bin = std::any_cast<int32_t>(counts_data.GetAttribute("last_good_bin"));
|
|
std::cout << " last_good_bin : " << last_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " last_good_bin : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/instrument/detector_1/resolution") != fDataMap.end()) {
|
|
try {
|
|
auto ivalData = std::any_cast<PNXdata<int32_t>>(fDataMap["/raw_data_1/instrument/detector_1/resolution"]);
|
|
std::cout << " resolution : " << ivalData.GetData()[0];
|
|
if (ivalData.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(ivalData.GetAttribute("units"));
|
|
std::cout << " " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " unit : n/a" << std::endl;
|
|
}
|
|
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast resolution attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " resolution : n/a";
|
|
}
|
|
|
|
// dump the first couple of counts of each detector
|
|
const auto& data = counts_data.GetData();
|
|
std::cout << std::endl << " first couple of counts of each detector:";
|
|
for (unsigned int i=0; i<dims[1]; i++) {
|
|
if (i<9)
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
else
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
for (unsigned int j=0; j<first_good_bin+5; j++)
|
|
std::cout << data[i*dims[2]+j] << ", ";
|
|
std::cout << "...";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast counts data" << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << " raw_time:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto raw_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/instrument/detector_1/raw_time"]);
|
|
const auto& data = raw_time.GetData();
|
|
|
|
// Check for attributes
|
|
if (raw_time.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(raw_time.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " units : n/a" << std::endl;
|
|
}
|
|
|
|
// dump the first couple of raw_times
|
|
std::cout << " ";
|
|
for (unsigned int i=0; i<first_good_bin+5; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast raw_time data" << std::endl;
|
|
}
|
|
|
|
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << " spectrum_index:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto spectrum_index = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/spectrum_index"]);
|
|
const auto& data = spectrum_index.GetData();
|
|
|
|
// dump the first couple of raw_times
|
|
std::cout << " ";
|
|
for (unsigned int i=0; i<first_good_bin+5; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << " dead_time:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto dead_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/instrument/detector_1/dead_time"]);
|
|
const auto& data = dead_time.GetData();
|
|
|
|
// dump the first couple of raw_times
|
|
std::cout << " ";
|
|
for (unsigned int i=0; i<first_good_bin+5; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::WriteNexusFile
|
|
//=============================================================================
|
|
int nxH4::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion)
|
|
{
|
|
// Create new HDF4 file
|
|
int32 sd_id = SDstart(filename.c_str(), DFACC_CREATE);
|
|
if (sd_id == FAIL) {
|
|
std::cerr << "**ERROR** Failed to create HDF4 file: " << filename << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Write file attributes
|
|
WriteFileAttributes(sd_id);
|
|
|
|
// Write datasets based on IDF version
|
|
if (idfVersion == 2) {
|
|
WriteIdfV2(sd_id);
|
|
} else {
|
|
std::cerr << "**ERROR** Unsupported IDF version for writing: " << idfVersion << std::endl;
|
|
SDend(sd_id);
|
|
return 1;
|
|
}
|
|
|
|
SDend(sd_id);
|
|
return 0;
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::WriteFileAttributes
|
|
//=============================================================================
|
|
void nxH4::PNeXus::WriteFileAttributes(int32 sd_id)
|
|
{
|
|
// Write HDF4 version
|
|
if (!fHdf4Version.empty()) {
|
|
SDsetattr(sd_id, "HDF4_version", DFNT_CHAR8,
|
|
fHdf4Version.length(), fHdf4Version.c_str());
|
|
}
|
|
|
|
// Write NeXus version
|
|
if (!fNeXusVersion.empty()) {
|
|
SDsetattr(sd_id, "NeXus_version", DFNT_CHAR8,
|
|
fNeXusVersion.length(), fNeXusVersion.c_str());
|
|
}
|
|
|
|
// Write file name
|
|
if (!fFileNameNxs.empty()) {
|
|
SDsetattr(sd_id, "file_name", DFNT_CHAR8,
|
|
fFileNameNxs.length(), fFileNameNxs.c_str());
|
|
}
|
|
|
|
// Write file time
|
|
if (!fFileTimeNxs.empty()) {
|
|
SDsetattr(sd_id, "file_time", DFNT_CHAR8,
|
|
fFileTimeNxs.length(), fFileTimeNxs.c_str());
|
|
}
|
|
|
|
// Write root level attributes from fGroupAttributes
|
|
if (fGroupAttributes.find("/") != fGroupAttributes.end()) {
|
|
const auto& attrs = fGroupAttributes.at("/");
|
|
for (const auto& [attr_name, attr_value] : attrs) {
|
|
// Handle different attribute types
|
|
try {
|
|
auto str_attr = std::any_cast<std::string>(attr_value);
|
|
SDsetattr(sd_id, attr_name.c_str(), DFNT_CHAR8,
|
|
str_attr.length(), str_attr.c_str());
|
|
} catch (...) {
|
|
// Try other types if needed
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::WriteIdfV2
|
|
//=============================================================================
|
|
void nxH4::PNeXus::WriteIdfV2(int32 sd_id)
|
|
{
|
|
// Write all datasets from data map
|
|
for (const auto& [path, data_any] : fDataMap) {
|
|
// Try to write as int dataset
|
|
try {
|
|
auto data = std::any_cast<PNXdata<int>>(data_any);
|
|
WriteIntDataset(sd_id, path, data);
|
|
continue;
|
|
} catch (...) {}
|
|
|
|
// Try to write as float dataset
|
|
try {
|
|
auto data = std::any_cast<PNXdata<float>>(data_any);
|
|
WriteFloatDataset(sd_id, path, data);
|
|
continue;
|
|
} catch (...) {}
|
|
|
|
// Try to write as string dataset
|
|
try {
|
|
auto data = std::any_cast<PNXdata<std::string>>(data_any);
|
|
WriteStringDataset(sd_id, path, data);
|
|
continue;
|
|
} catch (...) {}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::WriteIntDataset
|
|
//=============================================================================
|
|
void nxH4::PNeXus::WriteIntDataset(int32 sd_id, const std::string& path,
|
|
const PNXdata<int>& data)
|
|
{
|
|
// Extract dataset name from path
|
|
std::vector<std::string> components = splitPath(path);
|
|
if (components.empty()) {
|
|
throw std::runtime_error("Invalid path: " + path);
|
|
}
|
|
std::string dataset_name = components.back();
|
|
|
|
const auto& dims = data.GetDimensions();
|
|
int32 rank = static_cast<int32>(dims.size());
|
|
std::vector<int32> dim_sizes(rank);
|
|
for (int32 i = 0; i < rank; i++) {
|
|
dim_sizes[i] = static_cast<int32>(dims[i]);
|
|
}
|
|
|
|
// Create dataset
|
|
int32 sds_id = SDcreate(sd_id, dataset_name.c_str(), DFNT_INT32, rank, dim_sizes.data());
|
|
if (sds_id == FAIL) {
|
|
throw std::runtime_error("Failed to create dataset: " + dataset_name);
|
|
}
|
|
|
|
// Convert data to int32
|
|
const auto& int_data = data.GetData();
|
|
std::vector<int32> data32(int_data.begin(), int_data.end());
|
|
|
|
// Write data
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
if (SDwritedata(sds_id, start, nullptr, dim_sizes.data(), data32.data()) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to write dataset: " + dataset_name);
|
|
}
|
|
|
|
// Write attributes
|
|
WriteDatasetAttributes(sds_id, data);
|
|
|
|
SDendaccess(sds_id);
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::WriteFloatDataset
|
|
//=============================================================================
|
|
void nxH4::PNeXus::WriteFloatDataset(int32 sd_id, const std::string& path,
|
|
const PNXdata<float>& data)
|
|
{
|
|
// Extract dataset name from path
|
|
std::vector<std::string> components = splitPath(path);
|
|
if (components.empty()) {
|
|
throw std::runtime_error("Invalid path: " + path);
|
|
}
|
|
std::string dataset_name = components.back();
|
|
|
|
const auto& dims = data.GetDimensions();
|
|
int32 rank = static_cast<int32>(dims.size());
|
|
std::vector<int32> dim_sizes(rank);
|
|
for (int32 i = 0; i < rank; i++) {
|
|
dim_sizes[i] = static_cast<int32>(dims[i]);
|
|
}
|
|
|
|
// Create dataset
|
|
int32 sds_id = SDcreate(sd_id, dataset_name.c_str(), DFNT_FLOAT32, rank, dim_sizes.data());
|
|
if (sds_id == FAIL) {
|
|
throw std::runtime_error("Failed to create dataset: " + dataset_name);
|
|
}
|
|
|
|
// Convert data to float32
|
|
const auto& float_data = data.GetData();
|
|
std::vector<float32> data32(float_data.begin(), float_data.end());
|
|
|
|
// Write data
|
|
int32 start[H4_MAX_VAR_DIMS] = {0};
|
|
if (SDwritedata(sds_id, start, nullptr, dim_sizes.data(), data32.data()) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to write dataset: " + dataset_name);
|
|
}
|
|
|
|
// Write attributes
|
|
WriteDatasetAttributes(sds_id, data);
|
|
|
|
SDendaccess(sds_id);
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::WriteStringDataset
|
|
//=============================================================================
|
|
void nxH4::PNeXus::WriteStringDataset(int32 sd_id, const std::string& path,
|
|
const PNXdata<std::string>& data)
|
|
{
|
|
// Extract dataset name from path
|
|
std::vector<std::string> components = splitPath(path);
|
|
if (components.empty()) {
|
|
throw std::runtime_error("Invalid path: " + path);
|
|
}
|
|
std::string dataset_name = components.back();
|
|
|
|
const auto& string_data = data.GetData();
|
|
if (string_data.empty()) {
|
|
return;
|
|
}
|
|
|
|
std::string str = string_data[0];
|
|
int32 dims[1] = {static_cast<int32>(str.length())};
|
|
|
|
// Create dataset
|
|
int32 sds_id = SDcreate(sd_id, dataset_name.c_str(), DFNT_CHAR8, 1, dims);
|
|
if (sds_id == FAIL) {
|
|
throw std::runtime_error("Failed to create dataset: " + dataset_name);
|
|
}
|
|
|
|
// Write data - HDF4 requires non-const pointer
|
|
std::vector<char> str_buffer(str.begin(), str.end());
|
|
int32 start[1] = {0};
|
|
if (SDwritedata(sds_id, start, nullptr, dims, str_buffer.data()) == FAIL) {
|
|
SDendaccess(sds_id);
|
|
throw std::runtime_error("Failed to write dataset: " + dataset_name);
|
|
}
|
|
|
|
// Write attributes
|
|
WriteDatasetAttributes(sds_id, data);
|
|
|
|
SDendaccess(sds_id);
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH4::PNeXus::WriteDatasetAttributes (template specializations)
|
|
//=============================================================================
|
|
template <typename T>
|
|
void nxH4::PNeXus::WriteDatasetAttributes(int32 sds_id, const PNXdata<T>& data)
|
|
{
|
|
const auto& attributes = data.GetAttributes();
|
|
|
|
for (const auto& [attr_name, attr_value] : attributes) {
|
|
// Try different attribute types - primitives first (like h5nexus)
|
|
|
|
// Try scalar int
|
|
try {
|
|
int32 value = std::any_cast<int>(attr_value);
|
|
SDsetattr(sds_id, attr_name.c_str(), DFNT_INT32, 1, &value);
|
|
continue;
|
|
} catch (...) {}
|
|
|
|
// Try scalar float
|
|
try {
|
|
float32 value = std::any_cast<float>(attr_value);
|
|
SDsetattr(sds_id, attr_name.c_str(), DFNT_FLOAT32, 1, &value);
|
|
continue;
|
|
} catch (...) {}
|
|
|
|
// Try string
|
|
try {
|
|
std::string str = std::any_cast<std::string>(attr_value);
|
|
SDsetattr(sds_id, attr_name.c_str(), DFNT_CHAR8, str.length(), str.c_str());
|
|
continue;
|
|
} catch (...) {}
|
|
|
|
// Try vector<int> for multi-element attributes
|
|
try {
|
|
std::vector<int> int_vec = std::any_cast<std::vector<int>>(attr_value);
|
|
std::vector<int32> data32(int_vec.begin(), int_vec.end());
|
|
SDsetattr(sds_id, attr_name.c_str(), DFNT_INT32, data32.size(), data32.data());
|
|
continue;
|
|
} catch (...) {}
|
|
|
|
// Try vector<float> for multi-element attributes
|
|
try {
|
|
std::vector<float> float_vec = std::any_cast<std::vector<float>>(attr_value);
|
|
std::vector<float32> data32(float_vec.begin(), float_vec.end());
|
|
SDsetattr(sds_id, attr_name.c_str(), DFNT_FLOAT32, data32.size(), data32.data());
|
|
continue;
|
|
} catch (...) {}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Group attribute methods - manage attributes associated with HDF4 groups
|
|
//=============================================================================
|
|
|
|
/**
|
|
* @brief Add or update an attribute for a group
|
|
*
|
|
* Stores an attribute value in the internal group attributes map. These
|
|
* attributes will be written when WriteNexusFile() is called. The root
|
|
* group "/" can be used for file-level attributes.
|
|
*
|
|
* @param groupPath HDF4 path of the group (e.g., "/raw_data_1")
|
|
* @param attrName Attribute name
|
|
* @param attrValue Attribute value (stored as std::any)
|
|
* @return true if attribute was added successfully
|
|
*/
|
|
bool nxH4::PNeXus::AddGroupAttribute(const std::string& groupPath,
|
|
const std::string& attrName,
|
|
const std::any& attrValue)
|
|
{
|
|
fGroupAttributes[groupPath][attrName] = attrValue;
|
|
return true;
|
|
}
|
|
|
|
bool nxH4::PNeXus::RemoveGroupAttribute(const std::string& groupPath,
|
|
const std::string& attrName)
|
|
{
|
|
auto it = fGroupAttributes.find(groupPath);
|
|
if (it != fGroupAttributes.end()) {
|
|
auto attr_it = it->second.find(attrName);
|
|
if (attr_it != it->second.end()) {
|
|
it->second.erase(attr_it);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool nxH4::PNeXus::HasGroupAttribute(const std::string& groupPath,
|
|
const std::string& attrName) const
|
|
{
|
|
auto it = fGroupAttributes.find(groupPath);
|
|
if (it != fGroupAttributes.end()) {
|
|
return it->second.find(attrName) != it->second.end();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::any nxH4::PNeXus::GetGroupAttribute(const std::string& groupPath,
|
|
const std::string& attrName) const
|
|
{
|
|
return fGroupAttributes.at(groupPath).at(attrName);
|
|
}
|
|
|
|
const std::map<std::string, std::any>& nxH4::PNeXus::GetGroupAttributes(
|
|
const std::string& groupPath) const
|
|
{
|
|
static std::map<std::string, std::any> empty_map;
|
|
auto it = fGroupAttributes.find(groupPath);
|
|
if (it != fGroupAttributes.end()) {
|
|
return it->second;
|
|
}
|
|
return empty_map;
|
|
}
|
|
|
|
bool nxH4::PNeXus::ClearGroupAttributes(const std::string& groupPath)
|
|
{
|
|
auto it = fGroupAttributes.find(groupPath);
|
|
if (it != fGroupAttributes.end()) {
|
|
it->second.clear();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool nxH4::PNeXus::AddRootAttribute(const std::string& attrName, const std::any& attrValue)
|
|
{
|
|
return AddGroupAttribute("/", attrName, attrValue);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Helper methods
|
|
//=============================================================================
|
|
bool nxH4::PNeXus::caseInsensitiveEquals(const std::string& a, const std::string& b)
|
|
{
|
|
if (a.length() != b.length()) {
|
|
return false;
|
|
}
|
|
return std::equal(a.begin(), a.end(), b.begin(),
|
|
[](char a, char b) {
|
|
return std::tolower(static_cast<unsigned char>(a)) ==
|
|
std::tolower(static_cast<unsigned char>(b));
|
|
});
|
|
}
|
|
|
|
std::vector<std::string> nxH4::PNeXus::splitPath(const std::string& path)
|
|
{
|
|
std::vector<std::string> components;
|
|
std::string current;
|
|
|
|
for (char c : path) {
|
|
if (c == '/') {
|
|
if (!current.empty()) {
|
|
components.push_back(current);
|
|
current.clear();
|
|
}
|
|
} else {
|
|
current += c;
|
|
}
|
|
}
|
|
|
|
if (!current.empty()) {
|
|
components.push_back(current);
|
|
}
|
|
|
|
return components;
|
|
}
|
|
|
|
std::string nxH4::PNeXus::findAttributeName(int32 sd_id, const std::string& requestedName)
|
|
{
|
|
int32 n_datasets, n_attrs;
|
|
if (SDfileinfo(sd_id, &n_datasets, &n_attrs) == FAIL) {
|
|
throw std::runtime_error("Failed to get file info");
|
|
}
|
|
|
|
char attr_name[H4_MAX_NC_NAME];
|
|
int32 attr_type, attr_count;
|
|
|
|
for (int32 i = 0; i < n_attrs; i++) {
|
|
if (SDattrinfo(sd_id, i, attr_name, &attr_type, &attr_count) != FAIL) {
|
|
if (caseInsensitiveEquals(attr_name, requestedName)) {
|
|
return std::string(attr_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("Attribute not found: " + requestedName);
|
|
}
|
|
|
|
int32 nxH4::PNeXus::findDatasetIndex(int32 sd_id, const std::string& requestedName)
|
|
{
|
|
// First try exact match
|
|
int32 idx = SDnametoindex(sd_id, requestedName.c_str());
|
|
if (idx != FAIL) {
|
|
return idx;
|
|
}
|
|
|
|
// Try case-insensitive search
|
|
int32 n_datasets, n_attrs;
|
|
if (SDfileinfo(sd_id, &n_datasets, &n_attrs) == FAIL) {
|
|
throw std::runtime_error("Failed to get file info");
|
|
}
|
|
|
|
for (int32 i = 0; i < n_datasets; i++) {
|
|
int32 sds_id = SDselect(sd_id, i);
|
|
if (sds_id != FAIL) {
|
|
char name[H4_MAX_NC_NAME];
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_ds_attrs;
|
|
if (SDgetinfo(sds_id, name, &rank, dim_sizes, &data_type, &n_ds_attrs) != FAIL) {
|
|
if (caseInsensitiveEquals(name, requestedName)) {
|
|
SDendaccess(sds_id);
|
|
return i;
|
|
}
|
|
}
|
|
SDendaccess(sds_id);
|
|
}
|
|
}
|
|
|
|
throw std::runtime_error("Dataset not found: " + requestedName);
|
|
}
|
|
|
|
int32 nxH4::PNeXus::findDatasetRefByPath(const std::string& path)
|
|
{
|
|
// Navigate VGroup hierarchy to find the dataset reference
|
|
// Path format: /group1/group2/.../dataset_name
|
|
|
|
std::vector<std::string> components = splitPath(path);
|
|
if (components.empty()) {
|
|
return -1;
|
|
}
|
|
|
|
// Open file with V interface
|
|
if (Vstart(fFileId) == FAIL) {
|
|
return -1;
|
|
}
|
|
|
|
int32 result_ref = -1;
|
|
int32 current_vg_ref = -1;
|
|
|
|
// Find root VGroup (first component after /)
|
|
// For IDF version 1, root is typically "run"
|
|
if (components.size() >= 1) {
|
|
// Get all lone vgroups
|
|
int32 n_vgroups = Vlone(fFileId, nullptr, 0);
|
|
if (n_vgroups > 0) {
|
|
std::vector<int32> vgroup_refs(n_vgroups);
|
|
Vlone(fFileId, vgroup_refs.data(), n_vgroups);
|
|
|
|
// Find the root VGroup
|
|
for (int32 i = 0; i < n_vgroups; i++) {
|
|
int32 vg_id = Vattach(fFileId, vgroup_refs[i], "r");
|
|
if (vg_id != FAIL) {
|
|
char vg_name[VGNAMELENMAX];
|
|
if (Vgetname(vg_id, vg_name) != FAIL) {
|
|
if (caseInsensitiveEquals(vg_name, components[0])) {
|
|
current_vg_ref = vgroup_refs[i];
|
|
Vdetach(vg_id);
|
|
break;
|
|
}
|
|
}
|
|
Vdetach(vg_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Navigate through intermediate VGroups
|
|
for (size_t comp_idx = 1; comp_idx < components.size() - 1; comp_idx++) {
|
|
if (current_vg_ref == -1) break;
|
|
|
|
int32 vg_id = Vattach(fFileId, current_vg_ref, "r");
|
|
if (vg_id == FAIL) {
|
|
current_vg_ref = -1;
|
|
break;
|
|
}
|
|
|
|
int32 n_entries = Vntagrefs(vg_id);
|
|
bool found = false;
|
|
|
|
for (int32 i = 0; i < n_entries; i++) {
|
|
int32 tag, ref;
|
|
if (Vgettagref(vg_id, i, &tag, &ref) != FAIL) {
|
|
// Check if it's a VGroup
|
|
if (tag == DFTAG_VG) {
|
|
int32 child_vg_id = Vattach(fFileId, ref, "r");
|
|
if (child_vg_id != FAIL) {
|
|
char child_name[VGNAMELENMAX];
|
|
if (Vgetname(child_vg_id, child_name) != FAIL) {
|
|
if (caseInsensitiveEquals(child_name, components[comp_idx])) {
|
|
current_vg_ref = ref;
|
|
found = true;
|
|
Vdetach(child_vg_id);
|
|
break;
|
|
}
|
|
}
|
|
Vdetach(child_vg_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Vdetach(vg_id);
|
|
|
|
if (!found) {
|
|
current_vg_ref = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Find the dataset in the final VGroup
|
|
if (current_vg_ref != -1 && components.size() >= 1) {
|
|
int32 vg_id = Vattach(fFileId, current_vg_ref, "r");
|
|
if (vg_id != FAIL) {
|
|
int32 n_entries = Vntagrefs(vg_id);
|
|
std::string target_name = components.back();
|
|
|
|
for (int32 i = 0; i < n_entries; i++) {
|
|
int32 tag, ref;
|
|
if (Vgettagref(vg_id, i, &tag, &ref) != FAIL) {
|
|
// Check if it's a Numeric Data (SDS)
|
|
if (tag == DFTAG_NDG) {
|
|
// Try to get the name by converting ref to index
|
|
int32 sds_idx = SDreftoindex(fSdId, ref);
|
|
if (sds_idx != FAIL) {
|
|
int32 sds_id = SDselect(fSdId, sds_idx);
|
|
if (sds_id != FAIL) {
|
|
char ds_name[H4_MAX_NC_NAME];
|
|
int32 rank, dim_sizes[H4_MAX_VAR_DIMS], data_type, n_attrs;
|
|
if (SDgetinfo(sds_id, ds_name, &rank, dim_sizes, &data_type, &n_attrs) != FAIL) {
|
|
if (caseInsensitiveEquals(ds_name, target_name)) {
|
|
result_ref = ref;
|
|
SDendaccess(sds_id);
|
|
break;
|
|
}
|
|
}
|
|
SDendaccess(sds_id);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Vdetach(vg_id);
|
|
}
|
|
}
|
|
|
|
Vend(fFileId);
|
|
return result_ref;
|
|
}
|
|
|
|
/**
|
|
* @brief Convert HDF4 numeric type constant to H4DataType enum
|
|
*
|
|
* Maps HDF4 DFNT_* type constants to the library's H4DataType enum for
|
|
* type-safe handling of dataset types.
|
|
*
|
|
* @param hdf4_type HDF4 numeric type constant (e.g., DFNT_INT32)
|
|
* @return Corresponding H4DataType enum value
|
|
*
|
|
* @note Unknown types default to H4DataType::INT32
|
|
*/
|
|
nxH4::H4DataType nxH4::PNeXus::convertHdf4Type(int32 hdf4_type)
|
|
{
|
|
switch (hdf4_type) {
|
|
case DFNT_INT32: return H4DataType::INT32;
|
|
case DFNT_FLOAT32: return H4DataType::FLOAT32;
|
|
case DFNT_FLOAT64: return H4DataType::FLOAT64;
|
|
case DFNT_CHAR8: return H4DataType::CHAR8;
|
|
case DFNT_UINT32: return H4DataType::UINT32;
|
|
case DFNT_INT16: return H4DataType::INT16;
|
|
case DFNT_UINT16: return H4DataType::UINT16;
|
|
case DFNT_INT8: return H4DataType::INT8;
|
|
case DFNT_UINT8: return H4DataType::UINT8;
|
|
default: return H4DataType::INT32;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Convert H4DataType enum to HDF4 numeric type constant
|
|
*
|
|
* Maps the library's H4DataType enum to HDF4 DFNT_* type constants
|
|
* for writing datasets to HDF4 files.
|
|
*
|
|
* @param dataType H4DataType enum value
|
|
* @return Corresponding HDF4 numeric type constant (e.g., DFNT_INT32)
|
|
*
|
|
* @note Unknown types default to DFNT_INT32
|
|
*/
|
|
int32 nxH4::PNeXus::convertToHdf4Type(H4DataType dataType)
|
|
{
|
|
switch (dataType) {
|
|
case H4DataType::INT32: return DFNT_INT32;
|
|
case H4DataType::FLOAT32: return DFNT_FLOAT32;
|
|
case H4DataType::FLOAT64: return DFNT_FLOAT64;
|
|
case H4DataType::CHAR8: return DFNT_CHAR8;
|
|
case H4DataType::UINT32: return DFNT_UINT32;
|
|
case H4DataType::INT16: return DFNT_INT16;
|
|
case H4DataType::UINT16: return DFNT_UINT16;
|
|
case H4DataType::INT8: return DFNT_INT8;
|
|
case H4DataType::UINT8: return DFNT_UINT8;
|
|
default: return DFNT_INT32;
|
|
}
|
|
}
|
|
#endif // HAVE_HDF4
|
|
|
|
// ** HDF5 ********************************************************************
|
|
|
|
//=============================================================================
|
|
// nxH5::PNeXusDeadTime Constructor
|
|
//=============================================================================
|
|
nxH5::PNeXusDeadTime::PNeXusDeadTime(const PNeXus *nxs, bool debug) : fDebug(debug)
|
|
{
|
|
std::map<std::string, std::any> dataMap = nxs->GetDataMap();
|
|
|
|
int idfVersion = nxs->GetIdfVersion();
|
|
if ((idfVersion != 1) && (idfVersion != 2)) {
|
|
std::stringstream err;
|
|
err << "**ERROR** Found unsupported IDF version '" << idfVersion << "'";
|
|
std::cout << err.str() << std::endl;
|
|
fValid = false;
|
|
return;
|
|
}
|
|
|
|
if (idfVersion == 1) {
|
|
// not yet implemented
|
|
} else { // idfVersion == 2
|
|
// get counts
|
|
if (dataMap.find("/raw_data_1/instrument/detector_1/counts") != dataMap.end()) {
|
|
auto counts_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/counts"]);
|
|
const auto& counts = counts_data.GetData();
|
|
fCounts = counts;
|
|
auto dims = counts_data.GetDimensions();
|
|
if (fDebug) {
|
|
std::cout << " counts dimensions: " << dims[0] << " x "
|
|
<< dims[1] << " x " << dims[2] << std::endl;
|
|
}
|
|
fDims = dims;
|
|
|
|
// get t0
|
|
if (counts_data.HasAttribute("t0_bin")) {
|
|
try {
|
|
auto t0_bin = std::any_cast<int32_t>(counts_data.GetAttribute("t0_bin"));
|
|
fT0Bin = t0_bin;
|
|
if (fDebug)
|
|
std::cout << " fT0Bin: " << fT0Bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
|
|
}
|
|
}
|
|
// get fgb
|
|
if (counts_data.HasAttribute("first_good_bin")) {
|
|
try {
|
|
auto first_good_bin = std::any_cast<int32_t>(counts_data.GetAttribute("first_good_bin"));
|
|
fFgbBin = first_good_bin;
|
|
if (fDebug)
|
|
std::cout << " fFgbBin: " << fFgbBin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
|
|
}
|
|
}
|
|
// get lgb
|
|
if (counts_data.HasAttribute("last_good_bin")) {
|
|
try {
|
|
auto last_good_bin = std::any_cast<int32_t>(counts_data.GetAttribute("last_good_bin"));
|
|
fLgbBin = last_good_bin;
|
|
if (fDebug)
|
|
std::cout << " fLgbBin: " << fLgbBin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// get dead time parameters
|
|
if (dataMap.find("/raw_data_1/instrument/detector_1/dead_time") != dataMap.end()) {
|
|
auto dead_time = std::any_cast<PNXdata<float>>(dataMap["/raw_data_1/instrument/detector_1/dead_time"]);
|
|
const auto& dt_data = dead_time.GetData();
|
|
fDeadTime = dt_data;
|
|
}
|
|
fDeadTimeEstimated.resize(fDeadTime.size());
|
|
|
|
// get good_frames
|
|
if (dataMap.find("/raw_data_1/good_frames") != dataMap.end()) {
|
|
auto intData = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/good_frames"]);
|
|
const auto& ival = intData.GetData();
|
|
fGoodFrames = ival[0];
|
|
if (fDebug)
|
|
std::cout << " good_frames: " << fGoodFrames << std::endl;
|
|
}
|
|
// get resolution with units
|
|
if (dataMap.find("/raw_data_1/instrument/detector_1/resolution") != dataMap.end()) {
|
|
auto int_data = std::any_cast<PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/resolution"]);
|
|
const auto& ival = int_data.GetData();
|
|
float scale{1.0};
|
|
if (int_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
|
|
if (units == "picoseconds")
|
|
scale = 1.0e-6;
|
|
else if (units == "nanoseconds")
|
|
scale = 1.0e-3;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
}
|
|
fTimeResolution = ival[0] * scale;
|
|
if (fDebug)
|
|
std::cout << " time_resolution: " << fTimeResolution << " (us)" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH5::PNeXusDeadTime::operator()
|
|
//=============================================================================
|
|
/**
|
|
* @brief Calculate the negative log-likelihood for dead time correction
|
|
*
|
|
* This function implements the objective function for ROOT Minuit2 minimization.
|
|
* It calculates the negative log-likelihood comparing observed counts to
|
|
* the expected count rate model with dead time correction.
|
|
*
|
|
* **Model:**
|
|
* The expected count rate includes:
|
|
* - Exponential muon decay: N(t) = N0 * exp(-t/tau_mu) + N_bkg
|
|
* - Dead time correction: N_corrected = N(t) / (1 + N(t) * dtc)
|
|
*
|
|
* **Parameters:**
|
|
* - par[0] (dtc): Dead time correction parameter
|
|
* - par[1] (N0): Initial count rate amplitude
|
|
* - par[2] (N_bkg): Background count rate
|
|
*
|
|
* @param par Vector of parameters [dtc, N0, N_bkg]
|
|
* @return Negative log-likelihood value to be minimized
|
|
*
|
|
* @note Uses the muon lifetime tau_mu = 2.1969811 microseconds
|
|
*/
|
|
double nxH5::PNeXusDeadTime::operator()(const std::vector<double> &par) const
|
|
{
|
|
// par[0]: dtc, par[1]: N0, par[2]: N_bkg
|
|
|
|
double nt=0, nd=0, ll=0;
|
|
const double tau_mu=2.1969811;
|
|
for (unsigned int i=fFgbBin; i<fLgbBin; i++) {
|
|
nt = par[1]*exp(-(((double)i-(double)fT0Bin+0.5)*fTimeResolution)/tau_mu)+par[2]; // theory
|
|
if (nt <= 0) nt=1.0e-6;
|
|
nt = nt/(1.0+nt*par[0]); // dead time corrected
|
|
if (nt <= 1.0e-12) nt=1.0e-12;
|
|
nd = (double)fCounts[i+fIdx*fDims[2]]; // data
|
|
ll += nt - nd/(fGoodFrames*fTimeResolution) * log(nt);
|
|
}
|
|
|
|
return ll;
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH5::PNeXusDeadTime::minimize()
|
|
//=============================================================================
|
|
/**
|
|
* @brief Minimize dead time for a specific detector spectrum
|
|
*
|
|
* Performs ROOT Minuit2 minimization to find the optimal dead time parameter
|
|
* and count rate model parameters for the specified spectrum index. Uses
|
|
* MnMinimize with strategy 2 (high precision) and stores the result.
|
|
*
|
|
* **Minimization Parameters:**
|
|
* - dtc: Dead time correction (initial: 0.01, step: 0.01)
|
|
* - N0: Initial count rate (initial: estimated from data)
|
|
* - N_bkg: Background rate (initial: 0.1)
|
|
*
|
|
* @param i Spectrum index to minimize (0-based, must be < dims[1])
|
|
*
|
|
* @note Results are stored in fDeadTimeEstimated[i] and optionally printed
|
|
* to stdout if debug mode is enabled
|
|
*
|
|
* @see GetDeadTimeEstimated() to retrieve results after minimization
|
|
*/
|
|
void nxH5::PNeXusDeadTime::minimize(const int i)
|
|
{
|
|
fIdx = i;
|
|
|
|
double n0 = 1.07*(double)fCounts[fFgbBin+fIdx*fDims[2]];
|
|
ROOT::Minuit2::MnUserParameters params;
|
|
params.Add("dtc", 0.01, 0.01);
|
|
params.Add("N0", n0/fDims[2], 1.0);
|
|
params.Add("N_bkg", 0.1/fDims[2], 0.1);
|
|
|
|
// create minimizer object
|
|
ROOT::Minuit2::MnMinimize minimize(*this, params, ROOT::Minuit2::MnStrategy{2});
|
|
|
|
// minimize
|
|
// maxfcn is MINUIT2 Default maxfcn
|
|
unsigned int maxfcn = std::numeric_limits<unsigned int>::max();
|
|
double tolerance = 0.1;
|
|
ROOT::Minuit2::FunctionMinimum min = minimize(maxfcn, tolerance);
|
|
|
|
std::unique_ptr<ROOT::Minuit2::FunctionMinimum> fcnMin; ///< Minuit2 function minimum result
|
|
|
|
// keep FunctionMinimum object
|
|
fcnMin.reset(new ROOT::Minuit2::FunctionMinimum(min));
|
|
|
|
// keep user parameters
|
|
if (fcnMin)
|
|
params = fcnMin->UserParameters();
|
|
|
|
fDeadTimeEstimated[i] = params.Value(0);
|
|
|
|
if (fDebug)
|
|
std::cout << "debug> " << i << ": dtc fitted=" << params.Value(0) << ", dtc from file=" << fDeadTime[i] << std::endl;
|
|
}
|
|
|
|
//=============================================================================
|
|
// Case-insensitive string comparison helper
|
|
//=============================================================================
|
|
/**
|
|
* Compares two strings character-by-character using case-insensitive matching.
|
|
* The function first checks if the lengths match, then converts each character
|
|
* to lowercase using std::tolower before comparison.
|
|
*
|
|
* @param a First string to compare
|
|
* @param b Second string to compare
|
|
* @return true if strings are equal (ignoring case), false otherwise
|
|
*
|
|
* @note Uses static_cast to unsigned char for std::tolower to avoid undefined
|
|
* behavior with negative char values
|
|
*
|
|
* @example
|
|
* @code
|
|
* caseInsensitiveEquals("NeXus", "nexus") // returns true
|
|
* caseInsensitiveEquals("IDF_VERSION", "idf_version") // returns true
|
|
* @endcode
|
|
*/
|
|
bool nxH5::PNeXus::caseInsensitiveEquals(const std::string& a, const std::string& b)
|
|
{
|
|
if (a.length() != b.length()) {
|
|
return false;
|
|
}
|
|
|
|
for (size_t i = 0; i < a.length(); ++i) {
|
|
if (std::tolower(static_cast<unsigned char>(a[i])) !=
|
|
std::tolower(static_cast<unsigned char>(b[i]))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//=============================================================================
|
|
// Split HDF5 path into components
|
|
//=============================================================================
|
|
/**
|
|
* Splits an HDF5 path string into individual components using '/' as delimiter.
|
|
* The function preserves the information about absolute vs relative paths by
|
|
* keeping an empty first component for absolute paths.
|
|
*
|
|
* @param path HDF5 path string to split
|
|
* @return Vector of path components
|
|
*
|
|
* @note Empty components are filtered out except for the first component
|
|
* which indicates an absolute path when empty
|
|
*
|
|
* @example
|
|
* @code
|
|
* splitPath("/raw_data_1/IDF_version") // returns ["", "raw_data_1", "IDF_version"]
|
|
* splitPath("detector_1/counts") // returns ["detector_1", "counts"]
|
|
* splitPath("/") // returns [""]
|
|
* @endcode
|
|
*/
|
|
std::vector<std::string> nxH5::PNeXus::splitPath(const std::string& path)
|
|
{
|
|
std::vector<std::string> components;
|
|
std::string component;
|
|
std::istringstream stream(path);
|
|
|
|
while (std::getline(stream, component, '/')) {
|
|
// Keep empty first component to indicate absolute path
|
|
if (!component.empty() || components.empty()) {
|
|
components.push_back(component);
|
|
}
|
|
}
|
|
|
|
return components;
|
|
}
|
|
|
|
//=============================================================================
|
|
// Find attribute with case-insensitive name matching
|
|
//=============================================================================
|
|
/**
|
|
* Searches for an attribute by name using case-insensitive matching.
|
|
* Lists all attributes on the given HDF5 object and compares each name
|
|
* case-insensitively against the requested name.
|
|
*
|
|
* @param obj HDF5 File object to search for attributes
|
|
* @param requestedName Attribute name to find (any case)
|
|
* @return The correctly-cased attribute name as it exists in the file
|
|
* @throws H5::AttributeIException if no matching attribute is found
|
|
*
|
|
* @note Useful for handling NeXus files where attribute names may vary in case
|
|
* (e.g., "NeXus_version" vs "nexus_version")
|
|
*
|
|
* @example
|
|
* @code
|
|
* std::string actualName = findAttributeName(file, "nexus_VERSION");
|
|
* // actualName might be "NeXus_version" if that's how it's stored
|
|
* @endcode
|
|
*/
|
|
std::string nxH5::PNeXus::findAttributeName(H5::H5File& obj, const std::string& requestedName)
|
|
{
|
|
int numAttrs = obj.getNumAttrs();
|
|
|
|
for (int i = 0; i < numAttrs; i++) {
|
|
H5::Attribute attr = obj.openAttribute(i);
|
|
std::string attrName = attr.getName();
|
|
attr.close();
|
|
|
|
if (caseInsensitiveEquals(attrName, requestedName)) {
|
|
return attrName;
|
|
}
|
|
}
|
|
|
|
// Not found - throw exception
|
|
std::ostringstream msg;
|
|
msg << "Could not find attribute matching case-insensitive name: '" << requestedName << "'";
|
|
throw H5::AttributeIException("findAttributeName", msg.str());
|
|
}
|
|
|
|
//=============================================================================
|
|
// Find group path with case-insensitive matching (File overload)
|
|
//=============================================================================
|
|
/**
|
|
* Finds an HDF5 group path using case-insensitive matching, starting from
|
|
* the file root. Traverses the group hierarchy by splitting the path into
|
|
* components and matching each component case-insensitively.
|
|
*
|
|
* @param parent HDF5 File object (file root)
|
|
* @param requestedPath Group path to find (any case, absolute or relative)
|
|
* @return The correctly-cased group path as it exists in the file
|
|
* @throws H5::GroupIException if group not found or path component is not a group
|
|
*
|
|
* @note Each path component is verified to be a Group using getObjectType()
|
|
*
|
|
* @example
|
|
* @code
|
|
* std::string path = findGroupPath(file, "/RAW_DATA_1/detector_1");
|
|
* // path might be "/raw_data_1/detector_1" if that's the actual casing
|
|
* @endcode
|
|
*/
|
|
std::string nxH5::PNeXus::findGroupPath(H5::H5File& parent, const std::string& requestedPath)
|
|
{
|
|
std::vector<std::string> components = splitPath(requestedPath);
|
|
|
|
// Handle empty path
|
|
if (components.empty()) {
|
|
return "/";
|
|
}
|
|
|
|
// Check if absolute path
|
|
bool isAbsolute = (components[0].empty());
|
|
std::string currentPath = isAbsolute ? "" : "";
|
|
|
|
// Start from index 1 if absolute path (skip empty component)
|
|
size_t startIdx = isAbsolute ? 1 : 0;
|
|
|
|
// Traverse path components
|
|
for (size_t i = startIdx; i < components.size(); ++i) {
|
|
const std::string& requestedComponent = components[i];
|
|
|
|
// Build the parent path for querying
|
|
std::string parentPath = (currentPath.empty() || currentPath == "") ? "/" : currentPath;
|
|
H5::Group currentGroup = (parentPath == "/") ? parent.openGroup("/") : parent.openGroup(parentPath);
|
|
|
|
// List object names using iteration
|
|
hsize_t numObjs = currentGroup.getNumObjs();
|
|
std::vector<std::string> objectNames;
|
|
for (hsize_t j = 0; j < numObjs; j++) {
|
|
objectNames.push_back(currentGroup.getObjnameByIdx(j));
|
|
}
|
|
|
|
// Find case-insensitive match
|
|
std::string matchedName;
|
|
bool found = false;
|
|
|
|
for (const auto& objName : objectNames) {
|
|
if (caseInsensitiveEquals(objName, requestedComponent)) {
|
|
matchedName = objName;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
std::ostringstream msg;
|
|
msg << "Could not find group component matching case-insensitive path: '"
|
|
<< requestedPath << "' (failed at component: '" << requestedComponent << "')";
|
|
throw H5::GroupIException("findGroupPath", msg.str());
|
|
}
|
|
|
|
// Build current path
|
|
currentPath += "/" + matchedName;
|
|
|
|
// Verify it's a group
|
|
H5O_type_t objType = currentGroup.childObjType(matchedName);
|
|
if (objType != H5O_TYPE_GROUP) {
|
|
std::ostringstream msg;
|
|
msg << "Path component '" << matchedName << "' in '" << requestedPath
|
|
<< "' is not a group";
|
|
throw H5::GroupIException("findGroupPath", msg.str());
|
|
}
|
|
}
|
|
|
|
return currentPath;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Find group path with case-insensitive matching (Group overload)
|
|
//-----------------------------------------------------------------------------
|
|
std::string nxH5::PNeXus::findGroupPath(H5::Group& parent, const std::string& requestedPath)
|
|
{
|
|
std::vector<std::string> components = splitPath(requestedPath);
|
|
|
|
// Handle empty path
|
|
if (components.empty()) {
|
|
return "/";
|
|
}
|
|
|
|
// Check if absolute path
|
|
bool isAbsolute = (components[0].empty());
|
|
std::string currentPath = isAbsolute ? "" : "";
|
|
H5::Group currentGroup = parent;
|
|
|
|
// Start from index 1 if absolute path (skip empty component)
|
|
size_t startIdx = isAbsolute ? 1 : 0;
|
|
|
|
// Traverse path components
|
|
for (size_t i = startIdx; i < components.size(); ++i) {
|
|
const std::string& requestedComponent = components[i];
|
|
|
|
// List object names using iteration
|
|
hsize_t numObjs = currentGroup.getNumObjs();
|
|
std::vector<std::string> objectNames;
|
|
for (hsize_t j = 0; j < numObjs; j++) {
|
|
objectNames.push_back(currentGroup.getObjnameByIdx(j));
|
|
}
|
|
|
|
// Find case-insensitive match
|
|
std::string matchedName;
|
|
bool found = false;
|
|
|
|
for (const auto& objName : objectNames) {
|
|
if (caseInsensitiveEquals(objName, requestedComponent)) {
|
|
matchedName = objName;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
std::ostringstream msg;
|
|
msg << "Could not find group component matching case-insensitive path: '"
|
|
<< requestedPath << "' (failed at component: '" << requestedComponent << "')";
|
|
throw H5::GroupIException("findGroupPath", msg.str());
|
|
}
|
|
|
|
// Build current path
|
|
currentPath += "/" + matchedName;
|
|
|
|
// Verify it's a group (unless it's the last component, which is checked by caller)
|
|
H5O_type_t objType = currentGroup.childObjType(matchedName);
|
|
if (objType != H5O_TYPE_GROUP) {
|
|
std::ostringstream msg;
|
|
msg << "Path component '" << matchedName << "' in '" << requestedPath
|
|
<< "' is not a group";
|
|
throw H5::GroupIException("findGroupPath", msg.str());
|
|
}
|
|
|
|
// Open the group for next iteration
|
|
if (i < components.size() - 1) {
|
|
currentGroup = currentGroup.openGroup(matchedName);
|
|
}
|
|
}
|
|
|
|
return currentPath;
|
|
}
|
|
|
|
//=============================================================================
|
|
// Find dataset path with case-insensitive matching (File overload)
|
|
//=============================================================================
|
|
/**
|
|
* Finds an HDF5 dataset path using case-insensitive matching, starting from
|
|
* the file root. Traverses the group hierarchy and verifies that the final
|
|
* path component is actually a dataset.
|
|
*
|
|
* @param parent HDF5 File object (file root)
|
|
* @param requestedPath Dataset path to find (any case, absolute or relative)
|
|
* @return The correctly-cased dataset path as it exists in the file
|
|
* @throws H5::DataSetIException if dataset not found, path invalid, or
|
|
* final component is not a dataset
|
|
*
|
|
* @note Intermediate path components must be groups, and the final component
|
|
* must be a dataset, otherwise an exception is thrown
|
|
*
|
|
* @example
|
|
* @code
|
|
* std::string path = findDatasetPath(file, "/RAW_DATA_1/idf_VERSION");
|
|
* // path might be "/raw_data_1/IDF_version" if that's the actual casing
|
|
* std::int data = H5Easy::load<int>(file, path);
|
|
* @endcode
|
|
*/
|
|
std::string nxH5::PNeXus::findDatasetPath(H5::H5File& parent, const std::string& requestedPath)
|
|
{
|
|
std::vector<std::string> components = splitPath(requestedPath);
|
|
|
|
// Handle empty path
|
|
if (components.empty()) {
|
|
throw H5::DataSetIException("findDatasetPath", "Empty dataset path provided");
|
|
}
|
|
|
|
// Check if absolute path
|
|
bool isAbsolute = (components[0].empty());
|
|
std::string currentPath = isAbsolute ? "" : "";
|
|
|
|
// Start from index 1 if absolute path (skip empty component)
|
|
size_t startIdx = isAbsolute ? 1 : 0;
|
|
|
|
// Traverse path components
|
|
for (size_t i = startIdx; i < components.size(); ++i) {
|
|
const std::string& requestedComponent = components[i];
|
|
|
|
// Build the parent path for querying
|
|
std::string parentPath = (currentPath.empty() || currentPath == "") ? "/" : currentPath;
|
|
H5::Group currentGroup = (parentPath == "/") ? parent.openGroup("/") : parent.openGroup(parentPath);
|
|
|
|
// List object names using iteration
|
|
hsize_t numObjs = currentGroup.getNumObjs();
|
|
std::vector<std::string> objectNames;
|
|
for (hsize_t j = 0; j < numObjs; j++) {
|
|
objectNames.push_back(currentGroup.getObjnameByIdx(j));
|
|
}
|
|
|
|
// Find case-insensitive match
|
|
std::string matchedName;
|
|
bool found = false;
|
|
|
|
for (const auto& objName : objectNames) {
|
|
if (caseInsensitiveEquals(objName, requestedComponent)) {
|
|
matchedName = objName;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
std::ostringstream msg;
|
|
msg << "Could not find dataset matching case-insensitive path: '"
|
|
<< requestedPath << "' (failed at component: '" << requestedComponent << "')";
|
|
throw H5::DataSetIException("findDatasetPath", msg.str());
|
|
}
|
|
|
|
// Build current path
|
|
currentPath += "/" + matchedName;
|
|
|
|
// Check if this is the last component (should be a dataset)
|
|
if (i == components.size() - 1) {
|
|
H5O_type_t objType = currentGroup.childObjType(matchedName);
|
|
if (objType != H5O_TYPE_DATASET) {
|
|
std::ostringstream msg;
|
|
msg << "Path '" << requestedPath << "' resolves to '" << currentPath
|
|
<< "' which is not a dataset";
|
|
throw H5::DataSetIException("findDatasetPath", msg.str());
|
|
}
|
|
} else {
|
|
// Intermediate component - should be a group
|
|
H5O_type_t objType = currentGroup.childObjType(matchedName);
|
|
if (objType != H5O_TYPE_GROUP) {
|
|
std::ostringstream msg;
|
|
msg << "Path component '" << matchedName << "' in '" << requestedPath
|
|
<< "' is not a group";
|
|
throw H5::DataSetIException("findDatasetPath", msg.str());
|
|
}
|
|
}
|
|
}
|
|
|
|
return currentPath;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Find dataset path with case-insensitive matching (Group overload)
|
|
//-----------------------------------------------------------------------------
|
|
std::string nxH5::PNeXus::findDatasetPath(H5::Group& parent, const std::string& requestedPath)
|
|
{
|
|
std::vector<std::string> components = splitPath(requestedPath);
|
|
|
|
// Handle empty path
|
|
if (components.empty()) {
|
|
throw H5::DataSetIException("findDatasetPath", "Empty dataset path provided");
|
|
}
|
|
|
|
// Check if absolute path
|
|
bool isAbsolute = (components[0].empty());
|
|
std::string currentPath = isAbsolute ? "" : "";
|
|
H5::Group currentGroup = parent;
|
|
|
|
// Start from index 1 if absolute path (skip empty component)
|
|
size_t startIdx = isAbsolute ? 1 : 0;
|
|
|
|
// Traverse path components
|
|
for (size_t i = startIdx; i < components.size(); ++i) {
|
|
const std::string& requestedComponent = components[i];
|
|
|
|
// List object names using iteration
|
|
hsize_t numObjs = currentGroup.getNumObjs();
|
|
std::vector<std::string> objectNames;
|
|
for (hsize_t j = 0; j < numObjs; j++) {
|
|
objectNames.push_back(currentGroup.getObjnameByIdx(j));
|
|
}
|
|
|
|
// Find case-insensitive match
|
|
std::string matchedName;
|
|
bool found = false;
|
|
|
|
for (const auto& objName : objectNames) {
|
|
if (caseInsensitiveEquals(objName, requestedComponent)) {
|
|
matchedName = objName;
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!found) {
|
|
std::ostringstream msg;
|
|
msg << "Could not find dataset matching case-insensitive path: '"
|
|
<< requestedPath << "' (failed at component: '" << requestedComponent << "')";
|
|
throw H5::DataSetIException("findDatasetPath", msg.str());
|
|
}
|
|
|
|
// Build current path
|
|
currentPath += "/" + matchedName;
|
|
|
|
// Check if this is the last component (should be a dataset)
|
|
if (i == components.size() - 1) {
|
|
H5O_type_t objType = currentGroup.childObjType(matchedName);
|
|
if (objType != H5O_TYPE_DATASET) {
|
|
std::ostringstream msg;
|
|
msg << "Path '" << requestedPath << "' resolves to '" << currentPath
|
|
<< "' which is not a dataset";
|
|
throw H5::DataSetIException("findDatasetPath", msg.str());
|
|
}
|
|
} else {
|
|
// Intermediate component - should be a group
|
|
H5O_type_t objType = currentGroup.childObjType(matchedName);
|
|
if (objType != H5O_TYPE_GROUP) {
|
|
std::ostringstream msg;
|
|
msg << "Path component '" << matchedName << "' in '" << requestedPath
|
|
<< "' is not a group";
|
|
throw H5::DataSetIException("findDatasetPath", msg.str());
|
|
}
|
|
// Open the group for next iteration
|
|
currentGroup = currentGroup.openGroup(matchedName);
|
|
}
|
|
}
|
|
|
|
return currentPath;
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH5::PNeXus Constructor
|
|
//=============================================================================
|
|
nxH5::PNeXus::PNeXus()
|
|
{
|
|
// empty
|
|
}
|
|
|
|
//=============================================================================
|
|
// nxH5::PNeXus Constructor
|
|
//=============================================================================
|
|
/**
|
|
* Constructs a PNeXus object and immediately reads the specified NeXus file.
|
|
* The constructor initializes the filename member and calls ReadNexusFile()
|
|
* to open and parse the HDF5 file.
|
|
*
|
|
* @param fln Path to the NeXus HDF5 file to read
|
|
* @throws H5::FileIException if the file cannot be opened
|
|
* @throws H5::AttributeIException if required attributes are missing
|
|
* @throws H5::DataSetIException if required datasets are missing
|
|
*
|
|
* @note The file is read immediately upon construction. If reading fails,
|
|
* an exception is thrown and the object is not fully constructed.
|
|
*
|
|
* @example
|
|
* @code
|
|
* try {
|
|
* PNeXus nexus("data/emu00139040.nxs");
|
|
* std::cout << "Opened: " << nexus.GetFileName() << std::endl;
|
|
* } catch (const H5::Exception& e) {
|
|
* std::cerr << "Failed to open file: " << e.getDetailMsg() << std::endl;
|
|
* }
|
|
* @endcode
|
|
*/
|
|
nxH5::PNeXus::PNeXus(const std::string fln, const bool printDebug) : fFileName(fln), fPrintDebug(printDebug)
|
|
{
|
|
ReadNexusFile();
|
|
}
|
|
|
|
|
|
//=============================================================================
|
|
// Read NeXus File
|
|
//=============================================================================
|
|
/**
|
|
* Reads and parses the NeXus HDF5 file. Opens the file in read-only mode,
|
|
* loads the NeXus version attribute, and reads the IDF version dataset.
|
|
* Uses case-insensitive path lookup to handle varying path casings.
|
|
*
|
|
* @return 0 on success, 1 on error
|
|
* @throws H5::FileIException if file cannot be opened
|
|
* @throws H5::AttributeIException if NeXus_version attribute is missing
|
|
* @throws H5::DataSetIException if IDF_version dataset is missing
|
|
*
|
|
* @note This method is called automatically by the constructor. It performs
|
|
* case-insensitive lookups for both attributes and datasets to handle
|
|
* NeXus files with varying naming conventions.
|
|
*
|
|
* @internal
|
|
* Current implementation reads:
|
|
* - Attribute: NeXus_version (from root "/")
|
|
* - Dataset: /raw_data_1/IDF_version
|
|
*
|
|
* Both paths use case-insensitive lookup, so variations like
|
|
* "/RAW_DATA_1/idf_VERSION" will work correctly.
|
|
*/
|
|
int nxH5::PNeXus::ReadNexusFile()
|
|
{
|
|
try {
|
|
// Open the HDF5/NeXus file
|
|
H5::H5File file(fFileName, H5F_ACC_RDONLY);
|
|
|
|
// Turn off the auto-printing when failure occurs so that we can
|
|
// handle the errors appropriately
|
|
H5::Exception::dontPrint();
|
|
|
|
// Load NeXus version attribute (with case-insensitive lookup)
|
|
std::string version;
|
|
try {
|
|
std::string versionNeXusAttr = findAttributeName(file, "NeXus_version");
|
|
H5::Attribute attr = file.openAttribute(versionNeXusAttr);
|
|
H5::DataType dtype = attr.getDataType();
|
|
attr.read(dtype, version);
|
|
attr.close();
|
|
fNeXusVersion = version;
|
|
if (fPrintDebug)
|
|
std::cout << "debug> NeXus version=" << fNeXusVersion << std::endl;
|
|
} catch (const H5::AttributeIException& err) {
|
|
std::cerr << "Error: Failed to read NeXus_version attribute: " << err.getDetailMsg() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Load IDF version from dataset (with case-insensitive lookup)
|
|
int idf_vers{-1};
|
|
try {
|
|
std::string idfPath = findDatasetPath(file, "/run/IDF_version");
|
|
H5::DataSet dataset = file.openDataSet(idfPath);
|
|
dataset.read(&idf_vers, H5::PredType::NATIVE_INT);
|
|
dataset.close();
|
|
fIdfVersion = idf_vers;
|
|
} catch (const H5::DataSetIException& err) {
|
|
if (fPrintDebug)
|
|
std::cout << "Info: not IDF version 1, will check for IDF version 2" << std::endl;
|
|
}
|
|
|
|
if (fIdfVersion == -1) {
|
|
try {
|
|
std::string idfPath = findDatasetPath(file, "/raw_data_1/IDF_version");
|
|
H5::DataSet dataset = file.openDataSet(idfPath);
|
|
dataset.read(&idf_vers, H5::PredType::NATIVE_INT);
|
|
dataset.close();
|
|
fIdfVersion = idf_vers;
|
|
} catch (const H5::DataSetIException& err) {
|
|
std::cerr << "Error: Failed to read IDF_version dataset: " << err.getDetailMsg() << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
try {
|
|
std::string versionHdf5Attr = findAttributeName(file, "HDF5_version");
|
|
H5::Attribute attr = file.openAttribute(versionHdf5Attr);
|
|
H5::DataType dtype = attr.getDataType();
|
|
attr.read(dtype, version);
|
|
attr.close();
|
|
fHdf5Version = version;
|
|
if (fPrintDebug)
|
|
std::cout << "debug> HDF5 version=" << fHdf5Version << std::endl;
|
|
} catch (const H5::AttributeIException& err) {
|
|
std::cerr << "Error: Failed to read HDF5_version attribute: " << err.getDetailMsg() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
if (fPrintDebug)
|
|
std::cout << "debug> IDF_version=" << fIdfVersion << std::endl;
|
|
|
|
if (fIdfVersion == 1) {
|
|
try {
|
|
HandleIdfV1(file);
|
|
} catch (...) {
|
|
|
|
}
|
|
} else { // fIdfVersion == 2
|
|
try {
|
|
HandleIdfV2(file);
|
|
} catch (...) {
|
|
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
} catch (const H5::FileIException& err) {
|
|
std::cerr << "Error: Failed to open file '" << fFileName << "': " << err.getDetailMsg() << std::endl;
|
|
return 1;
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error: HDF5 exception occurred: " << err.getDetailMsg() << std::endl;
|
|
return 1;
|
|
} catch (const std::exception& err) {
|
|
std::cerr << "Error: Unexpected exception: " << err.what() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Handle NeXus IDF version 1 file
|
|
//=============================================================================
|
|
void nxH5::PNeXus::HandleIdfV1(H5::H5File &file)
|
|
{
|
|
if (fPrintDebug)
|
|
std::cout << "debug> in HandleIdfV1 ..." << std::endl;
|
|
|
|
std::string attrStr{""}, attrStrName{""};
|
|
// get file_name attribute
|
|
try {
|
|
attrStrName = findAttributeName(file, "user");
|
|
H5::Attribute attr = file.openAttribute(attrStrName);
|
|
H5::DataType dtype = attr.getDataType();
|
|
attr.read(dtype, attrStr);
|
|
attr.close();
|
|
fUserV1 = attrStr;
|
|
if (fPrintDebug)
|
|
std::cout << "debug> user =" << fUserV1 << std::endl;
|
|
} catch (const H5::AttributeIException& err) {
|
|
std::cerr << "Error: Failed to read user attribute: " << err.getDetailMsg() << std::endl;
|
|
}
|
|
|
|
// Read the mandatory key datasets and store them in the data map
|
|
try {
|
|
ReadStringDataset(file, "/run/program_name");
|
|
ReadIntDataset(file, "/run/number");
|
|
ReadStringDataset(file, "/run/title");
|
|
ReadStringDataset(file, "/run/notes");
|
|
ReadStringDataset(file, "/run/analysis");
|
|
ReadStringDataset(file, "/run/lab");
|
|
ReadStringDataset(file, "/run/beamline");
|
|
ReadStringDataset(file, "/run/start_time");
|
|
ReadStringDataset(file, "/run/stop_time");
|
|
ReadIntDataset(file, "/run/switching_states");
|
|
ReadStringDataset(file, "/run/user/name");
|
|
ReadStringDataset(file, "/run/user/experiment_number");
|
|
ReadStringDataset(file, "/run/sample/name");
|
|
ReadFloatDataset(file, "/run/sample/temperature");
|
|
ReadFloatDataset(file, "/run/sample/magnetic_field");
|
|
ReadStringDataset(file, "/run/sample/magnetic_field_state");
|
|
ReadFloatDataset(file, "/run/sample/magnetic_field_vector");
|
|
ReadStringDataset(file, "/run/sample/environment");
|
|
ReadStringDataset(file, "/run/instrument/name");
|
|
ReadIntDataset(file, "/run/instrument/detector/number");
|
|
ReadFloatDataset(file, "/run/instrument/detector/deadtimes");
|
|
ReadStringDataset(file, "/run/instrument/collimator/type");
|
|
ReadStringDataset(file, "/run/instrument/beam/beamline");
|
|
ReadIntDataset(file, "/run/instrument/beam/frames_good");
|
|
ReadIntDataset(file, "/run/histogram_data_1/counts");
|
|
ReadIntDataset(file, "/run/histogram_data_1/resolution");
|
|
ReadIntDataset(file, "/run/histogram_data_1/time_zero");
|
|
ReadFloatDataset(file, "/run/histogram_data_1/raw_time");
|
|
ReadFloatDataset(file, "/run/histogram_data_1/corrected_time");
|
|
ReadIntDataset(file, "/run/histogram_data_1/grouping");
|
|
ReadFloatDataset(file, "/run/histogram_data_1/alpha");
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error in HandleIdfV1: " << err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Handle NeXus IDF version 2 file
|
|
//=============================================================================
|
|
void nxH5::PNeXus::HandleIdfV2(H5::H5File &file)
|
|
{
|
|
if (fPrintDebug)
|
|
std::cout << "debug> in HandleIdfV2 ..." << std::endl;
|
|
|
|
std::string attrStr{""}, attrStrName{""};
|
|
// get file_name attribute
|
|
try {
|
|
attrStrName = findAttributeName(file, "file_name");
|
|
H5::Attribute attr = file.openAttribute(attrStrName);
|
|
H5::DataType dtype = attr.getDataType();
|
|
attr.read(dtype, attrStr);
|
|
attr.close();
|
|
fFileNameNxs = attrStr;
|
|
if (fPrintDebug)
|
|
std::cout << "debug> NXS file_name =" << fFileNameNxs << std::endl;
|
|
} catch (const H5::AttributeIException& err) {
|
|
std::cerr << "Error: Failed to read file_name attribute: " << err.getDetailMsg() << std::endl;
|
|
}
|
|
|
|
// get file_time attribute
|
|
attrStr="";
|
|
try {
|
|
attrStrName = findAttributeName(file, "file_time");
|
|
H5::Attribute attr = file.openAttribute(attrStrName);
|
|
H5::DataType dtype = attr.getDataType();
|
|
attr.read(dtype, attrStr);
|
|
attr.close();
|
|
fFileTimeNxs = attrStr;
|
|
if (fPrintDebug)
|
|
std::cout << "debug> NXS file_time =" << fFileNameNxs << std::endl;
|
|
} catch (const H5::AttributeIException& err) {
|
|
std::cerr << "Error: Failed to read file_time attribute: " << err.getDetailMsg() << std::endl;
|
|
}
|
|
|
|
// Read the mandatory key datasets and store them in the data map
|
|
try {
|
|
ReadIntDataset(file, "/raw_data_1/IDF_version");
|
|
ReadStringDataset(file, "/raw_data_1/beamline");
|
|
ReadStringDataset(file, "/raw_data_1/definition");
|
|
ReadIntDataset(file, "/raw_data_1/good_frames");
|
|
ReadIntDataset(file, "/raw_data_1/run_number");
|
|
ReadStringDataset(file, "/raw_data_1/title");
|
|
ReadStringDataset(file, "/raw_data_1/start_time");
|
|
ReadStringDataset(file, "/raw_data_1/end_time");
|
|
ReadStringDataset(file, "/raw_data_1/experiment_identifier");
|
|
ReadStringDataset(file, "/raw_data_1/instrument/name");
|
|
ReadStringDataset(file, "/raw_data_1/instrument/source/name");
|
|
ReadStringDataset(file, "/raw_data_1/instrument/source/type");
|
|
ReadStringDataset(file, "/raw_data_1/instrument/source/probe");
|
|
ReadIntDataset(file, "/raw_data_1/instrument/detector_1/resolution");
|
|
ReadIntDataset(file, "/raw_data_1/instrument/detector_1/counts");
|
|
ReadFloatDataset(file, "/raw_data_1/instrument/detector_1/raw_time");
|
|
ReadIntDataset(file, "/raw_data_1/instrument/detector_1/spectrum_index");
|
|
ReadFloatDataset(file, "/raw_data_1/detector_1/dead_time");
|
|
ReadStringDataset(file, "/raw_data_1/sample/name");
|
|
ReadFloatDataset(file, "/raw_data_1/sample/temperature");
|
|
ReadFloatDataset(file, "/raw_data_1/sample/magnetic_field");
|
|
ReadStringDataset(file, "/raw_data_1/sample/shape");
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error in HandleIdfV2: " << err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Dump hdf5-NeXus file content which was read
|
|
//=============================================================================
|
|
void nxH5::PNeXus::Dump()
|
|
{
|
|
int32_t first_good_bin{0};
|
|
|
|
if (fIdfVersion == 1) {
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << "hdf5-NeXus file content of file:' " << fFileName << "'"; std::cout << std::endl << "****";
|
|
std::cout << std::endl << "****";
|
|
std::cout << std::endl << "Top Level Attributes:";
|
|
std::cout << std::endl << " NeXus Version: " << fNeXusVersion;
|
|
std::cout << std::endl << " user: " << fUserV1;
|
|
std::cout << std::endl << "++++";
|
|
std::cout << std::endl << "run";
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " IDF_version: " << fIdfVersion;
|
|
if (fDataMap.find("/run/program_name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/program_name"]);
|
|
std::cout << std::endl << " program_name : " << str_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (str_data.HasAttribute("version")) {
|
|
try {
|
|
auto version = std::any_cast<std::string>(str_data.GetAttribute("version"));
|
|
std::cout << " version : " << version << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast version attribute" << std::endl;
|
|
}
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast program_name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " program_name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/number") != fDataMap.end()) {
|
|
try {
|
|
auto number = std::any_cast<PNXdata<int>>(fDataMap["/run/number"]);
|
|
std::cout << std::endl << " number : " << number.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " number : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/title") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/title"]);
|
|
std::cout << std::endl << " title : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast title data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " title : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/notes") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/notes"]);
|
|
std::cout << std::endl << " notes : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast notes data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " notes : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/analysis") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/analysis"]);
|
|
std::cout << std::endl << " analysis : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast analysis data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " analysis : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/lab") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/lab"]);
|
|
std::cout << std::endl << " lab : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast lab data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " lab : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/beamline") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/beamline"]);
|
|
std::cout << std::endl << " beamline : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " beamline : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/start_time") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/start_time"]);
|
|
std::cout << std::endl << " start_time : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast start_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " start_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/end_time") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/end_time"]);
|
|
std::cout << std::endl << " end_time : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast end_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " end_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/switching_state") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/switching_state"]);
|
|
std::cout << std::endl << " switching_state : " << int_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast switching_state data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " switching_state : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << " user";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/usr/name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/name"]);
|
|
std::cout << std::endl << " name : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/usr/experiment_number") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/user/experiment_number"]);
|
|
std::cout << std::endl << " experiment_number : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast experiment_number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " experiment_number : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << " sample";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/sample/name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/name"]);
|
|
std::cout << std::endl << " name : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/temperature") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/temperature"]);
|
|
std::cout << std::endl << " temperature : " << float_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast temperature data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " temperature : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/magnetic_field") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field"]);
|
|
std::cout << std::endl << " magnetic_field : " << float_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast magnetic_field data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " magnetic_field : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/magnetic_field_vector") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/sample/magnetic_field_vector"]);
|
|
std::cout << std::endl << " magnetic_field_vector : " << float_data.GetData()[0];
|
|
|
|
// Check for attributes
|
|
if (float_data.HasAttribute("coordinate_system")) {
|
|
try {
|
|
auto coordinate_system = std::any_cast<std::string>(float_data.GetAttribute("coordinate_system"));
|
|
std::cout << " coordinate_system : " << coordinate_system << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast coordinate_system attribute" << std::endl;
|
|
}
|
|
}
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
}
|
|
if (float_data.HasAttribute("available")) {
|
|
try {
|
|
auto available = std::any_cast<int>(float_data.GetAttribute("available"));
|
|
std::cout << " available : " << available << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast available attribute" << std::endl;
|
|
}
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast magnetic_field_vector data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " magnetic_field_vector : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/sample/environment") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/sample/environment"]);
|
|
std::cout << std::endl << " environment : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast environment data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " environment : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << " instrument";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/name") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/name"]);
|
|
std::cout << std::endl << " name : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << " detector";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/detector/number") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/instrument/detector/number"]);
|
|
std::cout << std::endl << " number : " << int_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " number : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/instrument/detector/deadtimes") != fDataMap.end()) {
|
|
try {
|
|
const auto& dead_times = std::any_cast<PNXdata<float>>("/run/instrument/detector/deadtimes").GetData();
|
|
std::cout << std::endl << " deadtimes: ";
|
|
for (unsigned int i=0; i<10; i++)
|
|
std::cout << dead_times[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast dead time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " deadtimes: n/a";
|
|
}
|
|
|
|
std::cout << std::endl << " collimator";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/collimator/type") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/collimator/type"]);
|
|
std::cout << std::endl << " type : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast type data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " type : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << " beam";
|
|
std::cout << std::endl << "----";
|
|
if (fDataMap.find("/run/instrument/beam/beamline") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/beam/beamline"]);
|
|
std::cout << std::endl << " beamline : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " beamline : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/instrument/beam/frames_good") != fDataMap.end()) {
|
|
try {
|
|
auto str_data = std::any_cast<PNXdata<std::string>>(fDataMap["/run/instrument/beam/frames_good"]);
|
|
std::cout << std::endl << " frames_good : " << str_data.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast frames_good data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " frames_good : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << " histogram_data_1";
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " counts:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/counts"]);
|
|
|
|
// Check for attributes
|
|
if (counts_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(counts_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("signal")) {
|
|
try {
|
|
auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
|
|
std::cout << " signal : " << signal << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast signal attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " signal : n/a" << std::endl;
|
|
}
|
|
|
|
int noOfHistos{0};
|
|
if (counts_data.HasAttribute("number")) {
|
|
try {
|
|
noOfHistos = std::any_cast<int>(counts_data.GetAttribute("number"));
|
|
std::cout << " number : " << noOfHistos << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast number attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " number : n/a" << std::endl;
|
|
}
|
|
|
|
int histoLength{0};
|
|
if (counts_data.HasAttribute("length")) {
|
|
try {
|
|
histoLength = std::any_cast<int>(counts_data.GetAttribute("length"));
|
|
std::cout << " length : " << noOfHistos << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast length attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " length : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("t0_bin")) {
|
|
try {
|
|
auto t0_bin = std::any_cast<int>(counts_data.GetAttribute("t0_bin"));
|
|
std::cout << " t0_bin : " << t0_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " t0_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("first_good_bin")) {
|
|
try {
|
|
first_good_bin = std::any_cast<int>(counts_data.GetAttribute("first_good_bin"));
|
|
std::cout << " first_good_bin : " << first_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " first_good_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("last_good_bin")) {
|
|
try {
|
|
auto last_good_bin = std::any_cast<int>(counts_data.GetAttribute("last_good_bin"));
|
|
std::cout << " last_good_bin : " << last_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " last_good_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("offset")) {
|
|
try {
|
|
auto offset = std::any_cast<float>(counts_data.GetAttribute("offset"));
|
|
std::cout << " offset : " << offset << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast offset attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " offset : n/a" << std::endl;
|
|
}
|
|
|
|
// dump the first couple of counts of each detector
|
|
const auto& data = counts_data.GetData();
|
|
std::cout << std::endl << " first couple of counts of each detector:";
|
|
for (unsigned int i=0; i<noOfHistos; i++) {
|
|
if (i<9)
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
else
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
for (unsigned int j=0; j<first_good_bin+5; j++)
|
|
std::cout << data[i*histoLength+j] << ", ";
|
|
std::cout << "...";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast counts data" << std::endl;
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/resolution") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/resolution"]);
|
|
std::cout << std::endl << " resolution : " << int_data.GetData()[0];
|
|
|
|
if (int_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast resolution data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " resolution : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/time_zero") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/time_zero"]);
|
|
std::cout << std::endl << " time_zero : " << int_data.GetData()[0];
|
|
|
|
if (int_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(int_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
|
|
if (int_data.HasAttribute("available")) {
|
|
try {
|
|
auto available = std::any_cast<int>(int_data.GetAttribute("available"));
|
|
std::cout << " available : " << available << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast available attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " available : n/a" << std::endl;
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast time_zero data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " time_zero : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/raw_time") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/raw_time"]);
|
|
std::cout << std::endl << " raw_time : " << float_data.GetData()[0];
|
|
if (float_data.HasAttribute("axis")) {
|
|
try {
|
|
auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
|
|
std::cout << " axis : " << axis << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast axis attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " axis : n/a" << std::endl;
|
|
}
|
|
|
|
if (float_data.HasAttribute("primary")) {
|
|
try {
|
|
auto primary = std::any_cast<int>(float_data.GetAttribute("primary"));
|
|
std::cout << " primary : " << primary << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast primary attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " primary : n/a" << std::endl;
|
|
}
|
|
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast raw_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " raw_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/corrected_time") != fDataMap.end()) {
|
|
try {
|
|
auto float_data = std::any_cast<PNXdata<float>>(fDataMap["/run/histogram_data_1/corrected_time"]);
|
|
std::cout << std::endl << " corrected_time : " << float_data.GetData()[0];
|
|
if (float_data.HasAttribute("axis")) {
|
|
try {
|
|
auto axis = std::any_cast<int>(float_data.GetAttribute("axis"));
|
|
std::cout << " axis : " << axis << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast axis attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " axis : n/a" << std::endl;
|
|
}
|
|
|
|
if (float_data.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(float_data.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast raw_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " corrected_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/run/histogram_data_1/grouping") != fDataMap.end()) {
|
|
try {
|
|
auto int_data = std::any_cast<PNXdata<int>>(fDataMap["/run/histogram_data_1/grouping"]);
|
|
std::cout << std::endl << " grouping : " << int_data.GetData()[0];
|
|
if (int_data.HasAttribute("available")) {
|
|
try {
|
|
auto available = std::any_cast<int>(int_data.GetAttribute("available"));
|
|
std::cout << " available : " << available << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast available attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " available : n/a";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast grouping data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " grouping : n/a";
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
} else { // IDF Version 2
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << "hdf5-NeXus file content of file:' " << fFileName << "'";
|
|
std::cout << std::endl << "****";
|
|
std::cout << std::endl << "Top Level Attributes:";
|
|
std::cout << std::endl << " HDF5 Version : " << fHdf5Version;
|
|
std::cout << std::endl << " NeXus Version: " << fNeXusVersion;
|
|
std::cout << std::endl << " file_name : " << fFileNameNxs;
|
|
std::cout << std::endl << " file_time : " << fFileTimeNxs;
|
|
std::cout << std::endl << "++++";
|
|
std::cout << std::endl << "raw_data_1";
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " IDF_version: " << fIdfVersion;
|
|
if (fDataMap.find("/raw_data_1/beamline") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/beamline"]);
|
|
std::cout << std::endl << " beamline : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast beamline data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " beamline : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/definition") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/definition"]);
|
|
std::cout << std::endl << " definition : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast definition data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " definition : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/run_number") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/run_number"]);
|
|
std::cout << std::endl << " run_number : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast run_number data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " run_number : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/title") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/title"]);
|
|
std::cout << std::endl << " title : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast title data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " title : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/start_time") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/start_time"]);
|
|
std::cout << std::endl << " start_time : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast start_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " start_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/end_time") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/end_time"]);
|
|
std::cout << std::endl << " end_time : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast end_time data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " end_time : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/good_frames") != fDataMap.end()) {
|
|
try {
|
|
auto good_frames = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/good_frames"]);
|
|
std::cout << std::endl << " good_frames: " << good_frames.GetData()[0];
|
|
} catch(const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast good_frames data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " good_frames: n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/experiment_identifier") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/experiment_identifier"]);
|
|
std::cout << std::endl << " experiment_identifier: " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast experiment_identifier data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " experiment_identifier: n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " instrument";
|
|
if (fDataMap.find("/raw_data_1/instrument/name") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/name"]);
|
|
std::cout << std::endl << " name : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " source";
|
|
if (fDataMap.find("/raw_data_1/instrument/source/name") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/name"]);
|
|
std::cout << std::endl << " name : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/source/name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/instrument/source/type") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/type"]);
|
|
std::cout << std::endl << " type : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/source/type data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " type : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/instrument/source/probe") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/instrument/source/probe"]);
|
|
std::cout << std::endl << " probe : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast instrument/source/probe data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " probe : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " sample";
|
|
if (fDataMap.find("/raw_data_1/sample/name") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/sample/name"]);
|
|
std::cout << std::endl << " name : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast sample/name data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " name : n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/sample/temperature") != fDataMap.end()) {
|
|
try {
|
|
auto temp_ds = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/sample/temperature"]);
|
|
float temp = temp_ds.GetData()[0];
|
|
if (temp_ds.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(temp_ds.GetAttribute("units"));
|
|
if (units == "Celsius")
|
|
temp += 273.16;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
}
|
|
std::cout << std::endl << " temperature: " << temp << " K";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast sample/temperature data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " temperature: n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/sample/magnetic_field") != fDataMap.end()) {
|
|
try {
|
|
auto mag_field_ds = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/sample/magnetic_field"]);
|
|
float mag_field = mag_field_ds.GetData()[0];
|
|
if (mag_field_ds.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(mag_field_ds.GetAttribute("units"));
|
|
if (units == "Tesla")
|
|
mag_field *= 1e4;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
}
|
|
std::cout << std::endl << " mag. field: " << mag_field << " G";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast sample/magnetic_field data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " temperature: n/a";
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/sample/shape") != fDataMap.end()) {
|
|
try {
|
|
auto str = std::any_cast<PNXdata<std::string>>(fDataMap["/raw_data_1/sample/shape"]);
|
|
std::cout << std::endl << " shape : " << str.GetData()[0];
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << std::endl << "Error: Failed to cast sample/shape data" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << std::endl << " shape : n/a";
|
|
}
|
|
|
|
std::cout << std::endl << "----";
|
|
std::cout << std::endl << " detector_1";
|
|
std::cout << std::endl << " counts:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto counts_data = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/counts"]);
|
|
auto dims = counts_data.GetDimensions();
|
|
std::cout << " counts dimensions: " << dims[0] << " x "
|
|
<< dims[1] << " x " << dims[2] << std::endl;
|
|
std::cout << " total elements: " << counts_data.GetNumElements() << std::endl;
|
|
|
|
// Check for attributes
|
|
if (counts_data.HasAttribute("signal")) {
|
|
try {
|
|
auto signal = std::any_cast<int>(counts_data.GetAttribute("signal"));
|
|
std::cout << " signal : " << signal << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast signal attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " signal : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("axes")) {
|
|
try {
|
|
auto axes = std::any_cast<std::string>(counts_data.GetAttribute("axes"));
|
|
std::cout << " axes : " << axes << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast axes attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " axes : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("long_name")) {
|
|
try {
|
|
auto long_name = std::any_cast<std::string>(counts_data.GetAttribute("long_name"));
|
|
std::cout << " long_name : " << long_name << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast long_name attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " long_name : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("t0_bin")) {
|
|
try {
|
|
auto t0_bin = std::any_cast<int32_t>(counts_data.GetAttribute("t0_bin"));
|
|
std::cout << " t0_bin : " << t0_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast t0_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " t0_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("first_good_bin")) {
|
|
try {
|
|
first_good_bin = std::any_cast<int32_t>(counts_data.GetAttribute("first_good_bin"));
|
|
std::cout << " first_good_bin : " << first_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast first_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " first_good_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (counts_data.HasAttribute("last_good_bin")) {
|
|
try {
|
|
auto last_good_bin = std::any_cast<int32_t>(counts_data.GetAttribute("last_good_bin"));
|
|
std::cout << " last_good_bin : " << last_good_bin << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast last_good_bin attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " last_good_bin : n/a" << std::endl;
|
|
}
|
|
|
|
if (fDataMap.find("/raw_data_1/instrument/detector_1/resolution") != fDataMap.end()) {
|
|
try {
|
|
auto ivalData = std::any_cast<PNXdata<int32_t>>(fDataMap["/raw_data_1/instrument/detector_1/resolution"]);
|
|
std::cout << " resolution : " << ivalData.GetData()[0];
|
|
if (ivalData.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(ivalData.GetAttribute("units"));
|
|
std::cout << " " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast resolution attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " resolution : n/a";
|
|
}
|
|
|
|
// dump the first couple of counts of each detector
|
|
const auto& data = counts_data.GetData();
|
|
std::cout << std::endl << " first couple of counts of each detector:";
|
|
for (unsigned int i=0; i<dims[1]; i++) {
|
|
if (i<9)
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
else
|
|
std::cout << std::endl << " " << i+1 << ": ";
|
|
for (unsigned int j=0; j<first_good_bin+5; j++)
|
|
std::cout << data[i*dims[2]+j] << ", ";
|
|
std::cout << "...";
|
|
}
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast counts data" << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << " raw_time:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto raw_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/instrument/detector_1/raw_time"]);
|
|
const auto& data = raw_time.GetData();
|
|
|
|
// Check for attributes
|
|
if (raw_time.HasAttribute("units")) {
|
|
try {
|
|
auto units = std::any_cast<std::string>(raw_time.GetAttribute("units"));
|
|
std::cout << " units : " << units << std::endl;
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast units attribute" << std::endl;
|
|
}
|
|
} else {
|
|
std::cout << " units : n/a" << std::endl;
|
|
}
|
|
|
|
// dump the first couple of raw_times
|
|
std::cout << " ";
|
|
for (unsigned int i=0; i<first_good_bin+5; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast raw_time data" << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << " spectrum_index:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto spectrum_index = std::any_cast<PNXdata<int>>(fDataMap["/raw_data_1/instrument/detector_1/spectrum_index"]);
|
|
const auto& data = spectrum_index.GetData();
|
|
|
|
// dump the first couple of raw_times
|
|
std::cout << " ";
|
|
for (unsigned int i=0; i<first_good_bin+5; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
std::cout << std::endl << " dead_time:";
|
|
std::cout << std::endl;
|
|
try {
|
|
auto dead_time = std::any_cast<PNXdata<float>>(fDataMap["/raw_data_1/detector_1/dead_time"]);
|
|
const auto& data = dead_time.GetData();
|
|
|
|
// dump the first couple of raw_times
|
|
std::cout << " ";
|
|
for (unsigned int i=0; i<first_good_bin+5; i++)
|
|
std::cout << data[i] << ", ";
|
|
std::cout << "...";
|
|
} catch (const std::bad_any_cast& e) {
|
|
std::cerr << "Error: Failed to cast spectrum_index data" << std::endl;
|
|
}
|
|
|
|
std::cout << std::endl;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Read integer dataset and store in data map
|
|
//=============================================================================
|
|
/**
|
|
* Reads an integer dataset from the HDF5 file and stores it in the data map.
|
|
* Handles multi-dimensional datasets by flattening them into a vector.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @param path Path to the dataset
|
|
* @throws H5::Exception if reading fails
|
|
*/
|
|
void nxH5::PNeXus::ReadIntDataset(H5::H5File& file, const std::string& path)
|
|
{
|
|
try {
|
|
// Find the actual path (case-insensitive)
|
|
std::string actualPath = findDatasetPath(file, path);
|
|
|
|
// Open the dataset
|
|
H5::DataSet dataset = file.openDataSet(actualPath);
|
|
H5::DataType datatype = dataset.getDataType();
|
|
H5::DataSpace dataspace = dataset.getSpace();
|
|
|
|
// Get dimensions
|
|
int rank = dataspace.getSimpleExtentNdims();
|
|
std::vector<hsize_t> dims(rank);
|
|
dataspace.getSimpleExtentDims(dims.data(), nullptr);
|
|
|
|
// Calculate total number of elements
|
|
hsize_t numElements = 1;
|
|
for (int i = 0; i < rank; i++) {
|
|
numElements *= dims[i];
|
|
}
|
|
|
|
// Create PNXdata object
|
|
PNXdata<int> data(datatype);
|
|
data.SetDimensions(dims);
|
|
|
|
// Read data
|
|
std::vector<int> buffer(numElements);
|
|
dataset.read(buffer.data(), H5::PredType::NATIVE_INT);
|
|
data.SetData(buffer);
|
|
|
|
// Read attributes
|
|
ReadDatasetAttributes(dataset, data);
|
|
|
|
// Store in map
|
|
fDataMap[actualPath] = data;
|
|
|
|
dataset.close();
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Read integer dataset: " << actualPath
|
|
<< " (dims: ";
|
|
for (size_t i = 0; i < dims.size(); i++) {
|
|
std::cout << dims[i];
|
|
if (i < dims.size() - 1) std::cout << " x ";
|
|
}
|
|
std::cout << ")" << std::endl;
|
|
}
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error reading integer dataset " << path << ": "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Read float dataset and store in data map
|
|
//=============================================================================
|
|
/**
|
|
* Reads a float dataset from the HDF5 file and stores it in the data map.
|
|
* Handles multi-dimensional datasets by flattening them into a vector.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @param path Path to the dataset
|
|
* @throws H5::Exception if reading fails
|
|
*/
|
|
void nxH5::PNeXus::ReadFloatDataset(H5::H5File& file, const std::string& path)
|
|
{
|
|
try {
|
|
// Find the actual path (case-insensitive)
|
|
std::string actualPath = findDatasetPath(file, path);
|
|
|
|
// Open the dataset
|
|
H5::DataSet dataset = file.openDataSet(actualPath);
|
|
H5::DataType datatype = dataset.getDataType();
|
|
H5::DataSpace dataspace = dataset.getSpace();
|
|
|
|
// Get dimensions
|
|
int rank = dataspace.getSimpleExtentNdims();
|
|
std::vector<hsize_t> dims(rank);
|
|
dataspace.getSimpleExtentDims(dims.data(), nullptr);
|
|
|
|
// Calculate total number of elements
|
|
hsize_t numElements = 1;
|
|
for (int i = 0; i < rank; i++) {
|
|
numElements *= dims[i];
|
|
}
|
|
|
|
// Create PNXdata object
|
|
PNXdata<float> data(datatype);
|
|
data.SetDimensions(dims);
|
|
|
|
// Read data
|
|
std::vector<float> buffer(numElements);
|
|
dataset.read(buffer.data(), H5::PredType::NATIVE_FLOAT);
|
|
data.SetData(buffer);
|
|
|
|
// Read attributes
|
|
ReadDatasetAttributes(dataset, data);
|
|
|
|
// Store in map
|
|
fDataMap[actualPath] = data;
|
|
|
|
dataset.close();
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Read float dataset: " << actualPath
|
|
<< " (dims: ";
|
|
for (size_t i = 0; i < dims.size(); i++) {
|
|
std::cout << dims[i];
|
|
if (i < dims.size() - 1) std::cout << " x ";
|
|
}
|
|
std::cout << ")" << std::endl;
|
|
}
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error reading float dataset " << path << ": "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Read string dataset and store in data map
|
|
//=============================================================================
|
|
/**
|
|
* Reads a string dataset from the HDF5 file and stores it in the data map.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @param path Path to the dataset
|
|
* @throws H5::Exception if reading fails
|
|
*/
|
|
void nxH5::PNeXus::ReadStringDataset(H5::H5File& file, const std::string& path)
|
|
{
|
|
try {
|
|
// Find the actual path (case-insensitive)
|
|
std::string actualPath = findDatasetPath(file, path);
|
|
|
|
// Open the dataset
|
|
H5::DataSet dataset = file.openDataSet(actualPath);
|
|
H5::DataType datatype = dataset.getDataType();
|
|
H5::DataSpace dataspace = dataset.getSpace();
|
|
|
|
// Get dimensions
|
|
int rank = dataspace.getSimpleExtentNdims();
|
|
std::vector<hsize_t> dims(rank);
|
|
dataspace.getSimpleExtentDims(dims.data(), nullptr);
|
|
|
|
// Calculate total number of elements
|
|
hsize_t numElements = 1;
|
|
for (int i = 0; i < rank; i++) {
|
|
numElements *= dims[i];
|
|
}
|
|
|
|
// Create PNXdata object
|
|
PNXdata<std::string> data(datatype);
|
|
data.SetDimensions(dims);
|
|
|
|
// Read data
|
|
std::vector<std::string> buffer(numElements);
|
|
if (numElements == 1) {
|
|
// Single string
|
|
std::string value;
|
|
dataset.read(value, datatype);
|
|
buffer[0] = value;
|
|
} else {
|
|
// Multiple strings (if needed)
|
|
for (hsize_t i = 0; i < numElements; i++) {
|
|
dataset.read(buffer[i], datatype);
|
|
}
|
|
}
|
|
data.SetData(buffer);
|
|
|
|
// Read attributes
|
|
ReadDatasetAttributes(dataset, data);
|
|
|
|
// Store in map
|
|
fDataMap[actualPath] = data;
|
|
|
|
dataset.close();
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Read string dataset: " << actualPath << std::endl;
|
|
}
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error reading string dataset " << path << ": "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Read dataset attributes (template implementation)
|
|
//=============================================================================
|
|
/**
|
|
* Reads all attributes from an HDF5 dataset and adds them to a PNXdata object.
|
|
* Attributes can be integers, floats, or strings.
|
|
*
|
|
* @tparam T The data type of the PNXdata object
|
|
* @param dataset HDF5 dataset object
|
|
* @param data PNXdata object to add attributes to
|
|
*/
|
|
template <typename T>
|
|
void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet& dataset, PNXdata<T>& data)
|
|
{
|
|
int numAttrs = dataset.getNumAttrs();
|
|
|
|
for (int i = 0; i < numAttrs; i++) {
|
|
H5::Attribute attr = dataset.openAttribute(i);
|
|
std::string attrName = attr.getName();
|
|
H5::DataType attrType = attr.getDataType();
|
|
H5T_class_t typeClass = attrType.getClass();
|
|
|
|
try {
|
|
if (typeClass == H5T_INTEGER) {
|
|
int32_t value;
|
|
attr.read(H5::PredType::NATIVE_INT32, &value);
|
|
data.AddAttribute(attrName, value);
|
|
} else if (typeClass == H5T_FLOAT) {
|
|
float value;
|
|
attr.read(H5::PredType::NATIVE_FLOAT, &value);
|
|
data.AddAttribute(attrName, value);
|
|
} else if (typeClass == H5T_STRING) {
|
|
std::string value;
|
|
attr.read(attrType, value);
|
|
data.AddAttribute(attrName, value);
|
|
}
|
|
} catch (const H5::Exception& err) {
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Could not read attribute " << attrName
|
|
<< ": " << err.getDetailMsg() << std::endl;
|
|
}
|
|
}
|
|
|
|
attr.close();
|
|
}
|
|
}
|
|
|
|
// Explicit template instantiations
|
|
template void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet&, PNXdata<int>&);
|
|
template void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet&, PNXdata<float>&);
|
|
template void nxH5::PNeXus::ReadDatasetAttributes(H5::DataSet&, PNXdata<std::string>&);
|
|
|
|
//=============================================================================
|
|
// WRITE METHODS
|
|
//=============================================================================
|
|
|
|
//=============================================================================
|
|
// Create group hierarchy for a given path
|
|
//=============================================================================
|
|
/**
|
|
* Create nested group hierarchy for a given path. Groups are created recursively
|
|
* if they don't exist. If a group already exists, it is opened and returned.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @param path Full path (e.g., "/raw_data_1/detector_1")
|
|
* @return H5::Group handle to the deepest created/opened group
|
|
* @throws H5::GroupIException if group creation fails
|
|
*/
|
|
H5::Group nxH5::PNeXus::CreateGroupHierarchy(H5::H5File& file, const std::string& path)
|
|
{
|
|
std::vector<std::string> components = splitPath(path);
|
|
std::string currentPath = "";
|
|
H5::Group currentGroup;
|
|
|
|
for (const auto& component : components) {
|
|
if (component.empty()) continue; // Skip empty (root indicator)
|
|
|
|
currentPath += "/" + component;
|
|
|
|
// Check if group exists
|
|
bool exists = false;
|
|
try {
|
|
H5::Group testGroup = file.openGroup(currentPath);
|
|
testGroup.close();
|
|
exists = true;
|
|
} catch (const H5::Exception&) {
|
|
exists = false;
|
|
}
|
|
|
|
// Create if doesn't exist
|
|
if (!exists) {
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Creating group: " << currentPath << std::endl;
|
|
}
|
|
currentGroup = file.createGroup(currentPath);
|
|
|
|
// Write group attributes if any exist for this path
|
|
auto attrIt = fGroupAttributes.find(currentPath);
|
|
if (attrIt != fGroupAttributes.end()) {
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Writing " << attrIt->second.size()
|
|
<< " attributes to group: " << currentPath << std::endl;
|
|
}
|
|
WriteGroupAttributes(currentGroup, attrIt->second);
|
|
}
|
|
|
|
currentGroup.close();
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Created group: " << currentPath << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Return an invalid group handle (we just needed to create the hierarchy)
|
|
return currentGroup;
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write dataset attributes from PNXdata object
|
|
//=============================================================================
|
|
/**
|
|
* Writes all attributes from a PNXdata object to an HDF5 dataset.
|
|
* Supports int32_t, float, and string attribute types.
|
|
*
|
|
* @tparam T The data type of the PNXdata object
|
|
* @param dataset HDF5 dataset object to write attributes to
|
|
* @param data PNXdata object containing attributes
|
|
*/
|
|
template <typename T>
|
|
void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet& dataset, const PNXdata<T>& data)
|
|
{
|
|
const auto& attributes = data.GetAttributes();
|
|
|
|
for (const auto& [attrName, attrValue] : attributes) {
|
|
try {
|
|
// Try int32_t
|
|
if (auto* intVal = std::any_cast<int32_t>(&attrValue)) {
|
|
H5::DataSpace attrSpace(H5S_SCALAR);
|
|
H5::Attribute attr = dataset.createAttribute(
|
|
attrName, H5::PredType::NATIVE_INT32, attrSpace
|
|
);
|
|
attr.write(H5::PredType::NATIVE_INT32, intVal);
|
|
attr.close();
|
|
attrSpace.close();
|
|
}
|
|
// Try float
|
|
else if (auto* floatVal = std::any_cast<float>(&attrValue)) {
|
|
H5::DataSpace attrSpace(H5S_SCALAR);
|
|
H5::Attribute attr = dataset.createAttribute(
|
|
attrName, H5::PredType::NATIVE_FLOAT, attrSpace
|
|
);
|
|
attr.write(H5::PredType::NATIVE_FLOAT, floatVal);
|
|
attr.close();
|
|
attrSpace.close();
|
|
}
|
|
// Try string
|
|
else if (auto* strVal = std::any_cast<std::string>(&attrValue)) {
|
|
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
|
|
H5::DataSpace attrSpace(H5S_SCALAR);
|
|
H5::Attribute attr = dataset.createAttribute(
|
|
attrName, strType, attrSpace
|
|
);
|
|
attr.write(strType, *strVal);
|
|
attr.close();
|
|
attrSpace.close();
|
|
}
|
|
else {
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Unsupported attribute type for "
|
|
<< attrName << std::endl;
|
|
}
|
|
}
|
|
|
|
} catch (const H5::Exception& err) {
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Could not write attribute " << attrName
|
|
<< ": " << err.getDetailMsg() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write integer dataset with attributes
|
|
//=============================================================================
|
|
/**
|
|
* Writes an integer dataset to the HDF5 file with all its attributes.
|
|
* The parent group hierarchy is created automatically if it doesn't exist.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @param path Dataset path (groups created automatically)
|
|
* @param data PNXdata object containing data, dimensions, and attributes
|
|
* @throws H5::Exception if writing fails
|
|
*/
|
|
void nxH5::PNeXus::WriteIntDataset(H5::H5File& file, const std::string& path,
|
|
const PNXdata<int>& data)
|
|
{
|
|
try {
|
|
// Extract parent path and dataset name
|
|
size_t lastSlash = path.find_last_of('/');
|
|
std::string parentPath = (lastSlash == 0) ? "/" : path.substr(0, lastSlash);
|
|
std::string datasetName = path.substr(lastSlash + 1);
|
|
|
|
// Create parent group hierarchy
|
|
if (!parentPath.empty() && parentPath != "/") {
|
|
CreateGroupHierarchy(file, parentPath);
|
|
}
|
|
|
|
// Get dimensions and data
|
|
const auto& dims = data.GetDimensions();
|
|
const auto& buffer = data.GetData();
|
|
|
|
// Validate data consistency
|
|
if (buffer.size() != data.GetNumElements()) {
|
|
throw std::runtime_error("Data size mismatch with dimensions");
|
|
}
|
|
|
|
// Create dataspace
|
|
H5::DataSpace dataspace(dims.size(), dims.data());
|
|
|
|
// Create dataset
|
|
H5::DataSet dataset = file.createDataSet(
|
|
path,
|
|
H5::PredType::NATIVE_INT,
|
|
dataspace
|
|
);
|
|
|
|
// Write data
|
|
dataset.write(buffer.data(), H5::PredType::NATIVE_INT);
|
|
|
|
// Write attributes
|
|
WriteDatasetAttributes(dataset, data);
|
|
|
|
// Close resources
|
|
dataset.close();
|
|
dataspace.close();
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Wrote integer dataset: " << path
|
|
<< " (dims: ";
|
|
for (size_t i = 0; i < dims.size(); i++) {
|
|
std::cout << dims[i];
|
|
if (i < dims.size() - 1) std::cout << " x ";
|
|
}
|
|
std::cout << ")" << std::endl;
|
|
}
|
|
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error writing integer dataset " << path << ": "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write float dataset with attributes
|
|
//=============================================================================
|
|
/**
|
|
* Writes a float dataset to the HDF5 file with all its attributes.
|
|
* The parent group hierarchy is created automatically if it doesn't exist.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @param path Dataset path (groups created automatically)
|
|
* @param data PNXdata object containing data, dimensions, and attributes
|
|
* @throws H5::Exception if writing fails
|
|
*/
|
|
void nxH5::PNeXus::WriteFloatDataset(H5::H5File& file, const std::string& path,
|
|
const PNXdata<float>& data)
|
|
{
|
|
try {
|
|
// Extract parent path and dataset name
|
|
size_t lastSlash = path.find_last_of('/');
|
|
std::string parentPath = (lastSlash == 0) ? "/" : path.substr(0, lastSlash);
|
|
std::string datasetName = path.substr(lastSlash + 1);
|
|
|
|
// Create parent group hierarchy
|
|
if (!parentPath.empty() && parentPath != "/") {
|
|
CreateGroupHierarchy(file, parentPath);
|
|
}
|
|
|
|
// Get dimensions and data
|
|
const auto& dims = data.GetDimensions();
|
|
const auto& buffer = data.GetData();
|
|
|
|
// Validate data consistency
|
|
if (buffer.size() != data.GetNumElements()) {
|
|
throw std::runtime_error("Data size mismatch with dimensions");
|
|
}
|
|
|
|
// Create dataspace
|
|
H5::DataSpace dataspace(dims.size(), dims.data());
|
|
|
|
// Create dataset
|
|
H5::DataSet dataset = file.createDataSet(
|
|
path,
|
|
H5::PredType::NATIVE_FLOAT,
|
|
dataspace
|
|
);
|
|
|
|
// Write data
|
|
dataset.write(buffer.data(), H5::PredType::NATIVE_FLOAT);
|
|
|
|
// Write attributes
|
|
WriteDatasetAttributes(dataset, data);
|
|
|
|
// Close resources
|
|
dataset.close();
|
|
dataspace.close();
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Wrote float dataset: " << path
|
|
<< " (dims: ";
|
|
for (size_t i = 0; i < dims.size(); i++) {
|
|
std::cout << dims[i];
|
|
if (i < dims.size() - 1) std::cout << " x ";
|
|
}
|
|
std::cout << ")" << std::endl;
|
|
}
|
|
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error writing float dataset " << path << ": "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write string dataset with attributes
|
|
//=============================================================================
|
|
/**
|
|
* Writes a string dataset to the HDF5 file with all its attributes.
|
|
* The parent group hierarchy is created automatically if it doesn't exist.
|
|
* Handles both scalar strings and arrays of strings.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @param path Dataset path (groups created automatically)
|
|
* @param data PNXdata object containing data, dimensions, and attributes
|
|
* @throws H5::Exception if writing fails
|
|
*/
|
|
void nxH5::PNeXus::WriteStringDataset(H5::H5File& file, const std::string& path,
|
|
const PNXdata<std::string>& data)
|
|
{
|
|
try {
|
|
// Extract parent path and dataset name
|
|
size_t lastSlash = path.find_last_of('/');
|
|
std::string parentPath = (lastSlash == 0) ? "/" : path.substr(0, lastSlash);
|
|
std::string datasetName = path.substr(lastSlash + 1);
|
|
|
|
// Create parent group hierarchy
|
|
if (!parentPath.empty() && parentPath != "/") {
|
|
CreateGroupHierarchy(file, parentPath);
|
|
}
|
|
|
|
// Get data
|
|
const auto& buffer = data.GetData();
|
|
const auto& dims = data.GetDimensions();
|
|
|
|
/* //as35
|
|
// Handle single string vs array of strings
|
|
if (buffer.size() == 1 && dims.size() == 1 && dims[0] == 1) {
|
|
// Single string - use variable-length string type
|
|
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
|
|
H5::DataSpace dataspace(H5S_SCALAR);
|
|
|
|
H5::DataSet dataset = file.createDataSet(path, strType, dataspace);
|
|
dataset.write(buffer[0], strType);
|
|
|
|
WriteDatasetAttributes(dataset, data);
|
|
dataset.close();
|
|
dataspace.close();
|
|
|
|
} else {
|
|
*/ //as35
|
|
// Array of strings
|
|
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
|
|
H5::DataSpace dataspace(dims.size(), dims.data());
|
|
|
|
H5::DataSet dataset = file.createDataSet(path, strType, dataspace);
|
|
|
|
// For arrays, need to create array of C strings
|
|
std::vector<const char*> cStrings;
|
|
for (const auto& str : buffer) {
|
|
cStrings.push_back(str.c_str());
|
|
}
|
|
dataset.write(cStrings.data(), strType);
|
|
|
|
WriteDatasetAttributes(dataset, data);
|
|
dataset.close();
|
|
dataspace.close();
|
|
/* //as35
|
|
}
|
|
*/ //as35
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Wrote string dataset: " << path << std::endl;
|
|
}
|
|
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error writing string dataset " << path << ": "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write group attributes
|
|
//=============================================================================
|
|
/**
|
|
* Writes attributes to an HDF5 group object. Supports int32_t, float, and
|
|
* string attribute types.
|
|
*
|
|
* @param group HDF5 group object to write attributes to
|
|
* @param attributes Map of attribute names to values (stored as std::any)
|
|
*/
|
|
void nxH5::PNeXus::WriteGroupAttributes(H5::Group& group, const std::map<std::string, std::any>& attributes)
|
|
{
|
|
for (const auto& [attrName, attrValue] : attributes) {
|
|
try {
|
|
// Try int32_t
|
|
if (auto* intVal = std::any_cast<int32_t>(&attrValue)) {
|
|
H5::DataSpace attrSpace(H5S_SCALAR);
|
|
H5::Attribute attr = group.createAttribute(
|
|
attrName, H5::PredType::NATIVE_INT32, attrSpace
|
|
);
|
|
attr.write(H5::PredType::NATIVE_INT32, intVal);
|
|
attr.close();
|
|
attrSpace.close();
|
|
}
|
|
// Try float
|
|
else if (auto* floatVal = std::any_cast<float>(&attrValue)) {
|
|
H5::DataSpace attrSpace(H5S_SCALAR);
|
|
H5::Attribute attr = group.createAttribute(
|
|
attrName, H5::PredType::NATIVE_FLOAT, attrSpace
|
|
);
|
|
attr.write(H5::PredType::NATIVE_FLOAT, floatVal);
|
|
attr.close();
|
|
attrSpace.close();
|
|
}
|
|
// Try string
|
|
else if (auto* strVal = std::any_cast<std::string>(&attrValue)) {
|
|
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
|
|
H5::DataSpace attrSpace(H5S_SCALAR);
|
|
H5::Attribute attr = group.createAttribute(
|
|
attrName, strType, attrSpace
|
|
);
|
|
attr.write(strType, *strVal);
|
|
attr.close();
|
|
attrSpace.close();
|
|
}
|
|
else {
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Unsupported attribute type for "
|
|
<< attrName << std::endl;
|
|
}
|
|
}
|
|
|
|
} catch (const H5::Exception& err) {
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Failed to write group attribute "
|
|
<< attrName << ": " << err.getDetailMsg() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write root-level file attributes
|
|
//=============================================================================
|
|
/**
|
|
* Writes root-level file attributes such as NeXus_version, HDF5_version,
|
|
* file_name, and file_time to the HDF5 file.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @throws H5::Exception if writing fails
|
|
*/
|
|
void nxH5::PNeXus::WriteFileAttributes(H5::H5File& file)
|
|
{
|
|
try {
|
|
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
|
|
H5::DataSpace attrSpace(H5S_SCALAR);
|
|
|
|
// Check if custom root-level attributes exist
|
|
auto rootAttrIt = fGroupAttributes.find("/");
|
|
bool hasCustomAttrs = (rootAttrIt != fGroupAttributes.end());
|
|
|
|
// NeXus_version attribute
|
|
if (!fNeXusVersion.empty() &&
|
|
(!hasCustomAttrs || rootAttrIt->second.find("NeXus_version") == rootAttrIt->second.end())) {
|
|
H5::Attribute attr = file.createAttribute(
|
|
"NeXus_version", strType, attrSpace
|
|
);
|
|
attr.write(strType, fNeXusVersion);
|
|
attr.close();
|
|
}
|
|
|
|
// HDF5_version attribute
|
|
if (!fHdf5Version.empty() &&
|
|
(!hasCustomAttrs || rootAttrIt->second.find("HDF5_version") == rootAttrIt->second.end())) {
|
|
H5::Attribute attr = file.createAttribute(
|
|
"HDF5_version", strType, attrSpace
|
|
);
|
|
attr.write(strType, fHdf5Version);
|
|
attr.close();
|
|
}
|
|
|
|
// file_name attribute
|
|
if (!fFileNameNxs.empty() &&
|
|
(!hasCustomAttrs || rootAttrIt->second.find("file_name") == rootAttrIt->second.end())) {
|
|
H5::Attribute attr = file.createAttribute(
|
|
"file_name", strType, attrSpace
|
|
);
|
|
attr.write(strType, fFileNameNxs);
|
|
attr.close();
|
|
}
|
|
|
|
// file_time attribute
|
|
if (!fFileTimeNxs.empty() &&
|
|
(!hasCustomAttrs || rootAttrIt->second.find("file_time") == rootAttrIt->second.end())) {
|
|
H5::Attribute attr = file.createAttribute(
|
|
"file_time", strType, attrSpace
|
|
);
|
|
attr.write(strType, fFileTimeNxs);
|
|
attr.close();
|
|
}
|
|
|
|
attrSpace.close();
|
|
|
|
// Write any custom root-level attributes from fGroupAttributes["/"]
|
|
if (hasCustomAttrs) {
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Writing " << rootAttrIt->second.size()
|
|
<< " custom attributes to root level" << std::endl;
|
|
}
|
|
|
|
for (const auto& [attrName, attrValue] : rootAttrIt->second) {
|
|
try {
|
|
// Try int32_t
|
|
if (auto* intVal = std::any_cast<int32_t>(&attrValue)) {
|
|
H5::DataSpace scalarSpace(H5S_SCALAR);
|
|
H5::Attribute attr = file.createAttribute(
|
|
attrName, H5::PredType::NATIVE_INT32, scalarSpace
|
|
);
|
|
attr.write(H5::PredType::NATIVE_INT32, intVal);
|
|
attr.close();
|
|
scalarSpace.close();
|
|
}
|
|
// Try float
|
|
else if (auto* floatVal = std::any_cast<float>(&attrValue)) {
|
|
H5::DataSpace scalarSpace(H5S_SCALAR);
|
|
H5::Attribute attr = file.createAttribute(
|
|
attrName, H5::PredType::NATIVE_FLOAT, scalarSpace
|
|
);
|
|
attr.write(H5::PredType::NATIVE_FLOAT, floatVal);
|
|
attr.close();
|
|
scalarSpace.close();
|
|
}
|
|
// Try string
|
|
else if (auto* strVal = std::any_cast<std::string>(&attrValue)) {
|
|
H5::StrType varStrType(H5::PredType::C_S1, H5T_VARIABLE);
|
|
H5::DataSpace scalarSpace(H5S_SCALAR);
|
|
H5::Attribute attr = file.createAttribute(
|
|
attrName, varStrType, scalarSpace
|
|
);
|
|
attr.write(varStrType, *strVal);
|
|
attr.close();
|
|
scalarSpace.close();
|
|
}
|
|
else {
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Unsupported attribute type for root-level "
|
|
<< attrName << std::endl;
|
|
}
|
|
}
|
|
} catch (const H5::Exception& err) {
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Failed to write root-level attribute "
|
|
<< attrName << ": " << err.getDetailMsg() << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error writing file attributes: "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write IDF version 2 structure
|
|
//=============================================================================
|
|
/**
|
|
* Writes all datasets from the data map to the HDF5 file following the
|
|
* IDF version 2 structure. Iterates through fDataMap and writes each
|
|
* dataset based on its type.
|
|
*
|
|
* @param file HDF5 file object
|
|
* @throws H5::Exception if writing fails
|
|
*/
|
|
void nxH5::PNeXus::WriteIdfV2(H5::H5File& file)
|
|
{
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Writing IDF v2 structure..." << std::endl;
|
|
}
|
|
|
|
// Iterate through all datasets in fDataMap
|
|
for (const auto& [path, anyData] : fDataMap) {
|
|
try {
|
|
// Try PNXdata<int>
|
|
try {
|
|
auto intData = std::any_cast<PNXdata<int>>(anyData);
|
|
WriteIntDataset(file, path, intData);
|
|
continue;
|
|
} catch (const std::bad_any_cast&) {}
|
|
|
|
// Try PNXdata<int32_t> - convert to int
|
|
try {
|
|
auto int32Data = std::any_cast<PNXdata<int32_t>>(anyData);
|
|
|
|
// Convert PNXdata<int32_t> to PNXdata<int>
|
|
PNXdata<int> intData(int32Data.GetDataType());
|
|
intData.SetDimensions(int32Data.GetDimensions());
|
|
|
|
std::vector<int> convertedData;
|
|
for (auto val : int32Data.GetData()) {
|
|
convertedData.push_back(static_cast<int>(val));
|
|
}
|
|
intData.SetData(convertedData);
|
|
|
|
// Copy attributes
|
|
for (const auto& [name, value] : int32Data.GetAttributes()) {
|
|
intData.AddAttribute(name, value);
|
|
}
|
|
|
|
WriteIntDataset(file, path, intData);
|
|
continue;
|
|
} catch (const std::bad_any_cast&) {}
|
|
|
|
// Try PNXdata<float>
|
|
try {
|
|
auto floatData = std::any_cast<PNXdata<float>>(anyData);
|
|
WriteFloatDataset(file, path, floatData);
|
|
continue;
|
|
} catch (const std::bad_any_cast&) {}
|
|
|
|
// Try PNXdata<std::string>
|
|
try {
|
|
auto strData = std::any_cast<PNXdata<std::string>>(anyData);
|
|
WriteStringDataset(file, path, strData);
|
|
continue;
|
|
} catch (const std::bad_any_cast&) {}
|
|
|
|
// Unknown type
|
|
if (fPrintDebug) {
|
|
std::cerr << "Warning: Unknown data type for path " << path
|
|
<< std::endl;
|
|
}
|
|
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error writing dataset " << path << ": "
|
|
<< err.getDetailMsg() << std::endl;
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
//=============================================================================
|
|
// Group attribute management methods
|
|
//=============================================================================
|
|
|
|
/**
|
|
* Add or update an attribute for a group.
|
|
*
|
|
* @param groupPath HDF5 path of the group (e.g., "/raw_data_1")
|
|
* @param attrName Attribute name
|
|
* @param attrValue Attribute value (stored as std::any)
|
|
* @return true if attribute was added successfully
|
|
*/
|
|
bool nxH5::PNeXus::AddGroupAttribute(const std::string& groupPath, const std::string& attrName,
|
|
const std::any& attrValue)
|
|
{
|
|
fGroupAttributes[groupPath][attrName] = attrValue;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Remove an attribute from a group.
|
|
*
|
|
* @param groupPath HDF5 path of the group
|
|
* @param attrName Attribute name to remove
|
|
* @return true if attribute was removed, false if group or attribute not found
|
|
*/
|
|
bool nxH5::PNeXus::RemoveGroupAttribute(const std::string& groupPath, const std::string& attrName)
|
|
{
|
|
auto groupIt = fGroupAttributes.find(groupPath);
|
|
if (groupIt == fGroupAttributes.end()) {
|
|
return false;
|
|
}
|
|
|
|
auto attrIt = groupIt->second.find(attrName);
|
|
if (attrIt == groupIt->second.end()) {
|
|
return false;
|
|
}
|
|
|
|
groupIt->second.erase(attrIt);
|
|
|
|
// Clean up empty group entry
|
|
if (groupIt->second.empty()) {
|
|
fGroupAttributes.erase(groupIt);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if a group has a specific attribute.
|
|
*
|
|
* @param groupPath HDF5 path of the group
|
|
* @param attrName Attribute name to check
|
|
* @return true if attribute exists, false otherwise
|
|
*/
|
|
bool nxH5::PNeXus::HasGroupAttribute(const std::string& groupPath, const std::string& attrName) const
|
|
{
|
|
auto groupIt = fGroupAttributes.find(groupPath);
|
|
if (groupIt == fGroupAttributes.end()) {
|
|
return false;
|
|
}
|
|
|
|
return groupIt->second.find(attrName) != groupIt->second.end();
|
|
}
|
|
|
|
/**
|
|
* Get an attribute value from a group.
|
|
*
|
|
* @param groupPath HDF5 path of the group
|
|
* @param attrName Attribute name
|
|
* @return The attribute value as std::any
|
|
* @throws std::out_of_range if group or attribute doesn't exist
|
|
*/
|
|
std::any nxH5::PNeXus::GetGroupAttribute(const std::string& groupPath, const std::string& attrName) const
|
|
{
|
|
return fGroupAttributes.at(groupPath).at(attrName);
|
|
}
|
|
|
|
/**
|
|
* Get all attributes for a group.
|
|
*
|
|
* @param groupPath HDF5 path of the group
|
|
* @return Map of attribute names to values
|
|
*/
|
|
const std::map<std::string, std::any>& nxH5::PNeXus::GetGroupAttributes(const std::string& groupPath) const
|
|
{
|
|
static const std::map<std::string, std::any> emptyMap;
|
|
auto it = fGroupAttributes.find(groupPath);
|
|
if (it == fGroupAttributes.end()) {
|
|
return emptyMap;
|
|
}
|
|
return it->second;
|
|
}
|
|
|
|
/**
|
|
* Clear all attributes from a group.
|
|
*
|
|
* @param groupPath HDF5 path of the group
|
|
* @return true if group was found and attributes cleared, false otherwise
|
|
*/
|
|
bool nxH5::PNeXus::ClearGroupAttributes(const std::string& groupPath)
|
|
{
|
|
auto it = fGroupAttributes.find(groupPath);
|
|
if (it == fGroupAttributes.end()) {
|
|
return false;
|
|
}
|
|
|
|
fGroupAttributes.erase(it);
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Add or update an attribute at the root level "/".
|
|
* This is a convenience method that calls AddGroupAttribute("/", attrName, attrValue).
|
|
*
|
|
* @param attrName Attribute name
|
|
* @param attrValue Attribute value (stored as std::any)
|
|
* @return true if attribute was added successfully
|
|
*/
|
|
bool nxH5::PNeXus::AddRootAttribute(const std::string& attrName, const std::any& attrValue)
|
|
{
|
|
return AddGroupAttribute("/", attrName, attrValue);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Write NeXus file (main entry point)
|
|
//=============================================================================
|
|
/**
|
|
* Writes the data map contents to a new NeXus HDF5 file.
|
|
* This is the main public API for writing NeXus files.
|
|
*
|
|
* @param filename Path to the output NeXus HDF5 file
|
|
* @param idfVersion IDF version to write (default: 2)
|
|
* @return 0 on success, 1 on error
|
|
* @throws H5::FileIException if file cannot be created
|
|
* @throws H5::GroupIException if group creation fails
|
|
* @throws H5::DataSetIException if dataset writing fails
|
|
*/
|
|
int nxH5::PNeXus::WriteNexusFile(const std::string& filename, int idfVersion)
|
|
{
|
|
try {
|
|
if (fPrintDebug) {
|
|
std::cout << std::endl;
|
|
std::cout << "debug> Creating NeXus file: " << filename << std::endl;
|
|
}
|
|
|
|
// Validate that we have data to write
|
|
if (fDataMap.empty()) {
|
|
std::cerr << "Error: No data to write (data map is empty)" << std::endl;
|
|
return 1;
|
|
}
|
|
|
|
// Create new HDF5 file (truncate if exists)
|
|
H5::H5File file(filename, H5F_ACC_TRUNC);
|
|
|
|
// Turn off auto-printing for exceptions
|
|
H5::Exception::dontPrint();
|
|
|
|
// Write root-level file attributes
|
|
WriteFileAttributes(file);
|
|
|
|
// Write structure based on IDF version
|
|
if (idfVersion == 1) {
|
|
std::cerr << "Error: IDF version 1 writing not yet implemented"
|
|
<< std::endl;
|
|
file.close();
|
|
return 1;
|
|
} else if (idfVersion == 2) {
|
|
WriteIdfV2(file);
|
|
} else {
|
|
std::cerr << "Error: Unsupported IDF version " << idfVersion
|
|
<< std::endl;
|
|
file.close();
|
|
return 1;
|
|
}
|
|
|
|
// Close file
|
|
file.close();
|
|
|
|
if (fPrintDebug) {
|
|
std::cout << "debug> Successfully wrote NeXus file: " << filename
|
|
<< std::endl;
|
|
}
|
|
|
|
return 0;
|
|
|
|
} catch (const H5::FileIException& err) {
|
|
std::cerr << "Error: Failed to create file '" << filename << "': "
|
|
<< err.getDetailMsg() << std::endl;
|
|
return 1;
|
|
} catch (const H5::Exception& err) {
|
|
std::cerr << "Error: HDF5 exception occurred: "
|
|
<< err.getDetailMsg() << std::endl;
|
|
return 1;
|
|
} catch (const std::exception& err) {
|
|
std::cerr << "Error: Unexpected exception: " << err.what() << std::endl;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// Explicit template instantiations for write methods
|
|
template void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet&, const PNXdata<int>&);
|
|
template void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet&, const PNXdata<float>&);
|
|
template void nxH5::PNeXus::WriteDatasetAttributes(H5::DataSet&, const PNXdata<std::string>&);
|