add HDF4/HDF5 example programs.

This commit is contained in:
2026-01-25 14:07:36 +01:00
parent 6aaed94adf
commit b3e4d247f0
14 changed files with 3768 additions and 69 deletions

View File

@@ -1,69 +0,0 @@
2011/04/13 -- BMW
Under Cygwin of all the required libraries for NeXus only HDF5 is available.
The packages <hdf5> and <libhdf5-devel> can be installed through the Cygwin setup.
One should also make sure that <bison>, <flex> and a package containing "/usr/lib/librpc.a" (e.g. <sunrpc> = 4.0-3) are installed.
All other libraries have to be built from the sources:
* JPEG-6b
URL: http://www.hdfgroup.org/ftp/lib-external/jpeg/src/jpegsrc.v6b.tar.gz
Configure options: --prefix=/usr/local --enable-static
* MXML 2.5
URL: http://ftp.easysw.com/pub/mxml/2.5/mxml-2.5.tar.gz
Configure options: --prefix=/usr/local --enable-static
* HDF 4.2.5
URL: http://www.hdfgroup.org/ftp/HDF/HDF_Current/src/hdf-4.2.5.tar.gz
Configure options: --prefix=/usr/local --enable-static --disable-fortran --with-jpeg=/usr/local
* NeXus 4.2.1
URL: http://download.nexusformat.org/kits/nexus-4.2.1.tar.gz
Configure options: --prefix=/usr/local --with-hdf4=/usr/local --with-hdf5=/usr --with-xml=/usr/local
The version numbers and source-code locations might of course change with time but should be easily adjustable.
If one is confident enough that all requirements to build the above packages are fullfilled, one could also try to run the following lines as a script.
However, there is absolutely no warranty that it works.
---
#!/bin/sh
cd
mkdir nexus
cd nexus
curl http://www.hdfgroup.org/ftp/lib-external/jpeg/src/jpegsrc.v6b.tar.gz -G | tar xz
cd jpeg-6b
./configure --prefix=/usr/local --enable-static
make
make install
cd ..
curl http://ftp.easysw.com/pub/mxml/2.5/mxml-2.5.tar.gz -G | tar xz
cd mxml-2.5
./configure --prefix=/usr/local --enable-static
make
make install
cd ..
curl http://www.hdfgroup.org/ftp/HDF/HDF_Current/src/hdf-4.2.5.tar.gz -G | tar xz
cd hdf-4.2.5
./configure --prefix=/usr/local --enable-static --disable-fortran --with-jpeg=/usr/local
make
make install
cd ..
curl http://download.nexusformat.org/kits/nexus-4.2.1.tar.gz -G | tar xz
./configure --prefix=/usr/local --with-hdf4=/usr/local --with-hdf5=/usr --with-xml=/usr/local
make
make install
---
In order to obtain NeXus support in musrfit after installing the above libraries, musrfit has to be configured with the options
"--enable-static --enable-NeXus"
Further information on how to set up musrfit under Cygwin can be found here:
https://intranet.psi.ch/MUSR/MusrFitSetup#A_4_MS_Windows
http://lmu.web.psi.ch/facilities/software/musrfit/user/intranet.psi.ch/MUSR/MusrFitSetup.html#A_4_MS_Windows
EOF

View File

@@ -0,0 +1,149 @@
# - h4nexus
cmake_minimum_required(VERSION 3.26)
project(h4nexus VERSION 0.1.0 LANGUAGES CXX)
#--- set C++ standard ---------------------------------------------------------
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
#--- set a default build type if none was specified ---------------------------
set(default_build_type "Release")
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif ()
#--- check for pkg-config -----------------------------------------------------
find_package(PkgConfig REQUIRED)
#--- check for git ------------------------------------------------------------
find_package(Git REQUIRED)
#--- check for HDF4 -----------------------------------------------------------
# Find HDF4 manually (pkg-config often doesn't have hdf4)
find_path(HDF4_INCLUDE_DIR
NAMES mfhdf.h
PATHS /usr/include /usr/local/include
PATH_SUFFIXES hdf
)
find_library(HDF4_DF_LIBRARY
NAMES df libdf
PATHS /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib
)
find_library(HDF4_MFHDF_LIBRARY
NAMES mfhdf libmfhdf
PATHS /usr/lib64 /usr/lib /usr/local/lib64 /usr/local/lib
)
if (HDF4_INCLUDE_DIR AND HDF4_DF_LIBRARY AND HDF4_MFHDF_LIBRARY)
set(HDF4_FOUND TRUE)
set(HDF4_INCLUDE_DIRS ${HDF4_INCLUDE_DIR})
set(HDF4_LIBRARIES ${HDF4_MFHDF_LIBRARY} ${HDF4_DF_LIBRARY})
message(STATUS "Found HDF4: ${HDF4_INCLUDE_DIR}")
message(STATUS " HDF4 libraries: ${HDF4_LIBRARIES}")
else ()
message(FATAL_ERROR "HDF4 library not found. Please install libhdf4-dev or hdf-devel")
endif ()
include_directories(${HDF4_INCLUDE_DIRS})
#--- check for HDF5 -----------------------------------------------------------
find_package(HDF5 REQUIRED COMPONENTS CXX)
if(NOT HDF5_FOUND)
message(FATAL_ERROR "HDF5 C++ library not found")
endif()
include_directories(${HDF5_INCLUDE_DIRS})
#--- check for ROOT -----------------------------------------------------------
find_package(ROOT 6.36 REQUIRED COMPONENTS Minuit2)
if (ROOT_miniut2_FOUND)
execute_process(COMMAND root-config --bindir OUTPUT_VARIABLE ROOT_BINDIR)
string(STRIP ${ROOT_BINDIR} ROOT_BINDIR)
execute_process(COMMAND root-config --version OUTPUT_VARIABLE ROOT_VERSION)
string(STRIP ${ROOT_VERSION} ROOT_VERSION)
message("-- Found ROOT: ${ROOT_BINDIR} (found version: ${ROOT_VERSION})")
#---Define useful ROOT functions and macros (e.g. ROOT_GENERATE_DICTIONARY)
include(${ROOT_USE_FILE})
endif (ROOT_miniut2_FOUND)
#--- all checks done -> feed config.h -----------------------------------------
set(HAVE_CONFIG_H 1 CACHE INTERNAL "config.h is available")
configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
#--- check if project source is a git repo ------------------------------------
if (EXISTS "${CMAKE_SOURCE_DIR}/.git/HEAD")
message(STATUS "is a git repo")
set(IS_GIT_REPO 1)
else ()
message(STATUS "is NOT a git repo")
set(IS_GIT_REPO 0)
endif ()
#--- start create git-revision.h ----------------------------------------------
if (IS_GIT_REPO)
execute_process(COMMAND sh ${CMAKE_SOURCE_DIR}/git_revision.sh ${CMAKE_BINARY_DIR})
set(HAVE_GIT_REV_H "-DHAVE_GIT_REV_H")
set(GIT_REV_H "git-revision.h")
else (IS_GIT_REPO)
set(HAVE_GIT_REV_H "")
set(GIT_REV_H "")
endif (IS_GIT_REPO)
#--- end create git-revision.h ------------------------------------------------
#--- write summary of the installation
cmake_host_system_information(RESULT PROCESSOR QUERY PROCESSOR_DESCRIPTION)
message("")
message("|-----------------------------------------------------------------------|")
message("| |")
message("| Summary |")
message("| |")
message("|-----------------------------------------------------------------------|")
message("")
message(" System: ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR} - ${CMAKE_HOST_SYSTEM_VERSION}")
message(" Processor: ${PROCESSOR} (${CMAKE_SYSTEM_PROCESSOR})")
message(" ----------")
message("")
message(" h4nexus Version: ${h4nexus_VERSION}")
message(" ----------------")
message("")
message(" Build Type: ${CMAKE_BUILD_TYPE}")
message(" -----------")
message("")
message(" Requirements:")
message(" -------------")
message("")
message(" HDF4 found in ${HDF4_INCLUDE_DIRS}")
message(" ROOT found in ${ROOT_INCLUDE_DIRS}, Version: ${ROOT_VERSION}")
message("")
message(" Installation directories:")
message(" -------------------------")
message("")
message(" Programs : ${CMAKE_INSTALL_PREFIX}/bin")
message("")
message("-------------------------------------------------------------------------")
message("")
#--- h4nexus executable -------------------------------------------------------
add_executable(h4nexus
../../PNeXus.cpp
main.cpp)
target_compile_options(h4nexus BEFORE PRIVATE "-DHAVE_HDF4 -DHAVE_CONFIG_H" ${HAVE_GIT_REV_H})
target_include_directories(h4nexus
BEFORE PRIVATE
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/build>
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/../..>
$<BUILD_INTERFACE:${ROOT_INCLUDE_DIRS}>
)
target_link_libraries(h4nexus ${HDF4_LIBRARIES} ${HDF5_LIBRARIES} ${ROOT_LIBRARIES})

View File

@@ -0,0 +1,7 @@
/* config.h.in. Generated from CMakeLists.txt */
/* Define to 1 if you have the <config.h> file. */
#cmakedefine HAVE_CONFIG_H @HAVE_CONFIG_H@
/* h4nexus version */
#define H4NEXUS_VERSION "@h4nexus_VERSION@"

View File

@@ -0,0 +1,165 @@
# h4nexus - handle muSR-NeXus files via HDF4 only
## Contents
Tests and classes to handle muSR-NeXus files directly via the HDF4 C API.
This project provides the same API as h5nexus but uses HDF4 instead of HDF5 for handling NeXus files.
## Features
- **Read and write NeXus HDF4 files** with a clean C++ API
- **Case-insensitive path lookup** for datasets and groups
- **Type-safe data handling** using template classes
- **Dead time correction calculation** for muon detector data using ROOT Minuit2
- **Compatible API with h5nexus** for easy migration
## Key Classes
- `nxH4::PNeXus` - Main class for reading/writing NeXus HDF4 files
- `nxH4::PNXdata<T>` - Template class for storing dataset content with attributes
- `nxH4::PNeXusDeadTime` - Dead time correction calculator for muon detector data
## Requirements
- CMake >= 3.26
- C++17 compatible compiler
- HDF4 library (libhdf4-dev or hdf-devel)
- ROOT >= 6.36 with Minuit2 component
- pkg-config
## Building
```bash
mkdir build
cd build
cmake ..
make
```
## Installation
```bash
make install
```
This will install:
- Library: `libh4nexus.so` in `${CMAKE_INSTALL_PREFIX}/lib`
- Executable: `h4nexus` in `${CMAKE_INSTALL_PREFIX}/bin`
- Header: `PNeXus.h` in `${CMAKE_INSTALL_PREFIX}/include/h4nexus`
## Usage
### Command Line
```bash
# Display help
h4nexus --help
# Read and display a NeXus HDF4 file
h4nexus --fn input.nxs
# Read with debug output
h4nexus --fn input.nxs --debug
# Calculate dead time corrections
h4nexus --fn input.nxs --dead_time_estimate
# Write output file
h4nexus --fn input.nxs --out output.nxs
```
### Programmatic Usage
```cpp
#include <h4nexus/PNeXus.h>
// Read a NeXus file
nxH4::PNeXus nexus("data.nxs");
// Access datasets
auto counts_data = nexus.GetDataset<int>("/raw_data_1/detector_1/counts");
const auto& counts = counts_data.GetData();
const auto& dims = counts_data.GetDimensions();
// Dump file contents
nexus.Dump();
// Write to new file
nexus.WriteNexusFile("output.nxs", 2); // IDF version 2
```
### Creating Files from Scratch
```cpp
nxH4::PNeXus nxs_out;
// Add datasets
std::vector<int> counts(16*66000, 0);
nxs_out.AddDataset<int>("/raw_data_1/detector_1/counts",
counts, {1, 16, 66000},
nxH4::H4DataType::INT32);
// Add attributes
nxs_out.AddDatasetAttribute<int>("/raw_data_1/detector_1/counts",
"units", std::string("counts"));
// Add group attributes
nxs_out.AddGroupAttribute("/raw_data_1", "NX_class", std::string("NXentry"));
// Write file
nxs_out.WriteNexusFile("output.nxs");
```
## API Compatibility with h5nexus
The h4nexus API is designed to be compatible with h5nexus. The main differences are:
- Namespace: `nxH4::` instead of `nxH5::`
- Data types: `H4DataType` enum instead of `H5::DataType`
- Dimensions: Uses `uint32_t` instead of `hsize_t`
Code migration typically requires only:
1. Changing namespace from `nxH5` to `nxH4`
2. Changing `H5::PredType::NATIVE_INT` to `nxH4::H4DataType::INT32` (etc.)
3. Changing dimension types from `hsize_t` to `uint32_t`
## Supported Data Types
- `H4DataType::INT32` - 32-bit signed integer
- `H4DataType::FLOAT32` - 32-bit floating point
- `H4DataType::FLOAT64` - 64-bit floating point
- `H4DataType::CHAR8` - 8-bit character/string
- `H4DataType::UINT32` - 32-bit unsigned integer
- `H4DataType::INT16` - 16-bit signed integer
- `H4DataType::UINT16` - 16-bit unsigned integer
- `H4DataType::INT8` - 8-bit signed integer
- `H4DataType::UINT8` - 8-bit unsigned integer
## Documentation
Generate Doxygen documentation (if Doxygen is installed):
```bash
make doc
```
Documentation will be generated in the `doc/html` directory.
## Differences from HDF5
HDF4 has some limitations compared to HDF5:
- No true hierarchical groups (simulated using naming conventions)
- Less flexible attribute handling
- Different maximum name lengths
- C API instead of C++ API
The h4nexus library abstracts these differences to provide a similar interface to h5nexus.
## License
GNU General Public License v2 (GPLv2)
## Contacts
Andreas Suter <andreas.suter@psi.ch>

View File

@@ -0,0 +1,35 @@
#!/bin/bash
# Script to create git-revision.h with current git information
# Usage: git_revision.sh [output_directory]
output_dir="${1:-.}"
output_file="${output_dir}/git-revision.h"
# Check if we're in a git repository
if ! git rev-parse --git-dir > /dev/null 2>&1; then
echo "Not in a git repository, skipping git-revision.h generation"
exit 0
fi
# Get git information
git_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
git_hash=$(git rev-parse --short HEAD 2>/dev/null)
git_date=$(git log -1 --format=%cd --date=short 2>/dev/null)
# Create header file
cat > "$output_file" << EOF
// This file is auto-generated by git_revision.sh
// Do not edit manually
#ifndef GIT_REVISION_H
#define GIT_REVISION_H
#define GIT_BRANCH "$git_branch"
#define GIT_HASH "$git_hash"
#define GIT_DATE "$git_date"
#endif // GIT_REVISION_H
EOF
echo "Generated $output_file"

View File

@@ -0,0 +1,447 @@
/***************************************************************************
main.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 main.cpp
* @brief Command-line interface for the h4nexus NeXus HDF4 file reader/writer
*
* This file contains the main() function and command-line interface for the
* h4nexus program. It provides functionality to:
* - Read and display NeXus HDF4 files
* - Write NeXus HDF4 files using the PNXdata approach
* - Calculate dead time corrections for muon detector data
*
* **Command-Line Options:**
* - --fn <file>: Input NeXus HDF4 file (required)
* - --out <file>: Output NeXus HDF4 file (optional)
* - --debug, -d: Enable debug output
* - --dead_time_estimate, -dt: Calculate dead time corrections
* - --help, -h: Display help message
* - --version, -v: Display version information
*
* @author Andreas Suter
* @date 2007-2026
* @copyright GNU General Public License v2
* @version 1.0
*
* @see nxH4::PNeXus
* @see nxH4::PNeXusDeadTime
*/
#include <cstring>
#include <iostream>
#include <string>
#include <memory>
#include <ctime>
#include <mfhdf.h>
#include "PNeXus.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_GIT_REV_H
#include "git-revision.h"
#endif
//-----------------------------------------------------------------------------
/**
* @brief Display command-line syntax and help information
*
* Prints the usage syntax and available command-line options for the h4nexus
* program to stdout and exits the program.
*/
void h4nexus_syntax() {
std::cout << std::endl;
std::cout << "usage: h4nexus [--help | -h] |" << std::endl;
std::cout << " [--version | -v] |" << std::endl;
std::cout << " --fn <fln> [--debug | -d]" << std::endl;
std::cout << " [--dead_time_estimate | -dt]]" << std::endl;
std::cout << " [--out <fout>]" << std::endl;
std::cout << std::endl;
std::cout << "options:" << std::endl;
std::cout << " --help, -h: this help." << std::endl;
std::cout << " --version, -v: version of h4nexus." << std::endl;
std::cout << " --fn <fln>: nexus hdf4 input file name <fn>." << std::endl;
std::cout << " --dead_time_estimate, -dt: dead time estimate for the read hdf4 nexus file." << std::endl;
std::cout << " --debug, -d: print additional debug information." << std::endl;
std::cout << " --out <fout>: write the required datasets of a nexus hdf4 file to file " << std::endl;
std::cout << " with name <fout>. Only makes sense together with the option --fn <fln>." << std::endl;
std::cout << std::endl;
exit(0);
}
//-----------------------------------------------------------------------------
/**
* @brief Calculate dead time corrections for muon detector data
*
* Estimates dead time corrections for each detector in the NeXus file using
* the PNeXusDeadTime class and ROOT Minuit2 minimization.
*
* @param nxs Pointer to the PNeXus object containing the data
* @param debug If true, print additional debug information
*
* @see nxH4::PNeXusDeadTime
*/
void h4nexus_deadTimeEstimate(const nxH4::PNeXus *nxs, bool debug)
{
if (debug) {
std::cout << std::endl;
std::cout << std::endl << "+++++++++++++++++++";
std::cout << std::endl << "in deadTimeEstimate";
std::cout << std::endl << "+++++++++++++++++++";
std::cout << std::endl;
}
nxH4::PNeXusDeadTime ndt(nxs, debug);
auto dims = ndt.GetDimensions();
for (unsigned int i=0; i<dims[1]; i++) {
ndt.minimize(i);
}
}
//-----------------------------------------------------------------------------
/**
* @brief Write NeXus HDF4 file using the PNXdata approach
*
* Writes all datasets from the PNeXus data map to a new NeXus HDF4 file.
* This function uses the WriteNexusFile() method to create a complete
* NeXus file with all groups, datasets, and attributes.
*
* @param nxs Pointer to the PNeXus object containing the data to write
* @param outFileName Output filename for the NeXus HDF4 file
* @param debug If true, print additional debug information
*
* @note Currently only supports IDF version 2 files
*
* @see nxH4::PNeXus::WriteNexusFile()
*/
void h4nexus_writeTest(const nxH4::PNeXus *nxs, const std::string& outFileName, bool debug)
{
if (debug) {
std::cout << std::endl;
std::cout << "++++++++++++++++++++" << std::endl;
std::cout << "Writing NeXus file" << std::endl;
std::cout << "++++++++++++++++++++" << std::endl;
std::cout << std::endl;
}
if (nxs->GetIdfVersion() == 1) {
std::cerr << "Error: IDF v1 write not yet implemented" << std::endl;
return;
}
// Write using the read object's data
int result = const_cast<nxH4::PNeXus*>(nxs)->WriteNexusFile(outFileName, nxs->GetIdfVersion());
if (result == 0) {
std::cout << "Successfully wrote: " << outFileName << std::endl;
} else {
std::cerr << "Failed to write file: " << outFileName << std::endl;
}
// write data from scratch
std::unique_ptr<nxH4::PNeXus> nxs_out = std::make_unique<nxH4::PNeXus>();
std::vector<int> ival;
std::vector<float> fval;
std::vector<std::string> sval;
// ----------
// raw_data_1
// ----------
// IDF version
ival.push_back(2);
nxs_out->AddDataset<int>("/raw_data_1/IDF_version", ival, {1}, nxH4::H4DataType::INT32);
ival.clear();
// add group attribute to '/raw_data_1'
nxs_out->AddGroupAttribute("/raw_data_1", "NX_class", std::string("NXentry"));
// beamline
sval.push_back("piE3");
nxs_out->AddDataset<std::string>("/raw_data_1/beamline", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// definition
sval.push_back("muonTD");
nxs_out->AddDataset<std::string>("/raw_data_1/definition", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// run_number
ival.push_back(1234);
nxs_out->AddDataset<int>("/raw_data_1/run_number", ival, {1}, nxH4::H4DataType::INT32);
ival.clear();
// title
sval.push_back("this is the run title.");
nxs_out->AddDataset<std::string>("/raw_data_1/title", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// start time
sval.push_back("2026-01-01T01:02:03");
nxs_out->AddDataset<std::string>("/raw_data_1/start_time", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// end time
sval.push_back("2026-01-01T02:03:42");
nxs_out->AddDataset<std::string>("/raw_data_1/end_time", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// experiment_identifier - pgroup for PSI
sval.push_back("p18324");
nxs_out->AddDataset<std::string>("/raw_data_1/experiment_identifier", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// -------------------
// detector_1 (NXdata)
// -------------------
// add group attribute to /raw_data_1/instrument
nxs_out->AddGroupAttribute("/raw_data_1/detector_1", "NX_class", std::string("NXdata"));
// counts
std::vector<int> counts(16*66000, 42); // data 16 histos with length 66000
nxs_out->AddDataset<int>("/raw_data_1/detector_1/counts", counts, {1, 16, 66000}, nxH4::H4DataType::INT32);
// attributes for counts
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "signal", 1);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "axes", std::string("period_index,spectrum_index,raw_time"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "long_name", std::string("positron_counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "t0_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "first_good_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "last_good_bin", 66000);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "units", std::string("counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "target", std::string("/raw_data_1/instrument/detector_1/counts"));
// raw_time
std::vector<float> raw_time(66000, 0.0);
for (unsigned int i=0; i<raw_time.size(); i++)
raw_time[i] = 0.1953125f*1.0e-3*((float)i-2741.0f+0.5f);
nxs_out->AddDataset<float>("/raw_data_1/detector_1/raw_time", raw_time, {66000}, nxH4::H4DataType::FLOAT32);
// attributes raw_time
nxs_out->AddDatasetAttribute<float>("/raw_data_1/detector_1/raw_time", "units", std::string("microseconds"));
nxs_out->AddDatasetAttribute<float>("/raw_data_1/detector_1/raw_time", "target", std::string("/raw_data_1/instrument/detector_1/raw_time"));
// ----------
// instrument
// ----------
// add group attribute to /raw_data_1/instrument
nxs_out->AddGroupAttribute("/raw_data_1/instrument", "NX_class", std::string("NXinstrument"));
// name
sval.push_back("LEM");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/name", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// ------
// source
// ------
// add group attribute to /raw_data_1/instrument/source
nxs_out->AddGroupAttribute("/raw_data_1/instrument/source", "NX_class", std::string("NXsource"));
// name
sval.push_back("PSI");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/source/name", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// type
sval.push_back("continuous muon source");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/source/types", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// probe
sval.push_back("postive muons");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/source/probe", sval, {1}, nxH4::H4DataType::CHAR8);
sval.clear();
// -----------------------
// detector_1 (NXdetector)
// -----------------------
// add group attribute to /raw_data_1/instrument/detector_1
nxs_out->AddGroupAttribute("/raw_data_1/instrument/detector_1", "NX_class", std::string("NXdetector"));
// counts
nxs_out->AddDataset<int>("/raw_data_1/instrument/detector_1/counts", counts, {1, 16, 66000}, nxH4::H4DataType::INT32);
// attributes for counts
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "signal", 1);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "axes", std::string("period_index,spectrum_index,raw_time"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "long_name", std::string("positron_counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "t0_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "first_good_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "last_good_bin", 66000);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "units", std::string("counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "target", std::string("/raw_data_1/instrument/detector_1/counts"));
// raw_time
nxs_out->AddDataset<float>("/raw_data_1/instrument/detector_1/raw_time", raw_time, {66000}, nxH4::H4DataType::FLOAT32);
// attributes raw_time
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/raw_time", "units", std::string("microseconds"));
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/raw_time", "target", std::string("/raw_data_1/instrument/detector_1/raw_time"));
// resolution
fval.push_back(195.3125);
nxs_out->AddDataset<float>("/raw_data_1/instrument/detector_1/resolution", fval, {1}, nxH4::H4DataType::FLOAT32);
fval.clear();
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/resolution",
"units", std::string("picoseconds"));
// spectrum_index
for (unsigned int i=0; i<66000; i++)
ival.push_back(i+1);
nxs_out->AddDataset<int>("/raw_data_1/instrument/detector_1/spectrum_index", ival, {66000}, nxH4::H4DataType::INT32);
ival.clear();
// dead_time
std::vector<float> deadTime(66000, 0.0);
nxs_out->AddDataset<float>("/raw_data_1/instrument/detector_1/dead_time", deadTime, {66000}, nxH4::H4DataType::FLOAT32);
// attributes dead_time
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/dead_time", "available", 0);
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/dead_time", "units", std::string("microseconds"));
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/dead_time", "target", std::string("/raw_data_1/instrument/detector_1/dead_time"));
// add root attributes
// file name
nxs_out->AddRootAttribute("file_name", std::string("_test.nxs"));
// date-time
std::time_t time = std::time({});
char timeString[std::size("yyyy-mm-ddThh:mm:ssZ")];
std::strftime(std::data(timeString), std::size(timeString),
"%FT%TZ", std::gmtime(&time));
nxs_out->AddRootAttribute("file_time", std::string(timeString));
// NeXus version
nxs_out->AddRootAttribute("NeXus_Version", std::string("4.3.0"));
// hdf4 version
nxs_out->AddRootAttribute("HDF4_Version", std::string(nxs->GetHdf4Version()));
// creator
nxs_out->AddRootAttribute("creator", std::string("h4nexus - PSI"));
nxs_out->WriteNexusFile("_test.nxs");
}
//-----------------------------------------------------------------------------
/**
* @brief Main entry point for the h4nexus program
*
* Parses command-line arguments and performs the requested operations:
* - Read and display NeXus HDF4 file information
* - Write NeXus HDF4 files with modified data
* - Calculate dead time corrections
*
* @param argc Number of command-line arguments
* @param argv Array of command-line argument strings
*
* @return 0 on success, non-zero on error
*
* @see h4nexus_syntax() for available command-line options
*/
int main(int argc, char *argv[])
{
std::string fileName{""};
std::string fileNameOut{""};
bool printDebug{false};
bool deadTimeEstimate{false};
if (argc == 1)
h4nexus_syntax();
for (int i=1; i<argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
h4nexus_syntax();
} else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
#ifdef HAVE_CONFIG_H
#ifdef HAVE_GIT_REV_H
std::cout << std::endl << "h4nexus version: " << H4NEXUS_VERSION << ", git-branch: " << GIT_BRANCH << ", git-hash: " << GIT_HASH << std::endl << std::endl;
#else
std::cout << std::endl << "h4nexus version: " << H4NEXUS_VERSION << std::endl << std::endl;
#endif
#else
#ifdef HAVE_GIT_REV_H
std::cout << std::endl << "h4nexus git-branch: " << GIT_BRANCH << ", git-hash: " << GIT_HASH << std::endl << std::endl;
#else
std::cout << std::endl << "h4nexus version: unknown." << std::endl << std::endl;
#endif
#endif
return 0;
} else if (!strcmp(argv[i], "--fn")) {
if (i+1 >= argc) {
std::cout << std::endl << "**ERROR** found --fn without <fln>." << std::endl;
h4nexus_syntax();
}
i++;
fileName = argv[i];
} else if (!strcmp(argv[i], "-dt") || !strcmp(argv[i], "--dead_time_estimate")) {
deadTimeEstimate = true;
} else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
printDebug = true;
} else if (!strcmp(argv[i], "--out")) {
if (i+1 >= argc) {
std::cout << std::endl << "**ERROR** found --out without <fout>." << std::endl;
h4nexus_syntax();
}
i++;
fileNameOut = argv[i];
} else {
h4nexus_syntax();
}
}
if (fileName.empty()) {
std::cerr << std::endl;
std::cerr << "**ERROR** <fln> is missing." << std::endl;
std::cerr << std::endl;
h4nexus_syntax();
}
std::unique_ptr<nxH4::PNeXus> nxs = std::make_unique<nxH4::PNeXus>(fileName, printDebug);
nxs->Dump();
if (deadTimeEstimate) {
h4nexus_deadTimeEstimate(nxs.get(), printDebug);
}
if (!fileNameOut.empty()) {
h4nexus_writeTest(nxs.get(), fileNameOut, printDebug);
}
return 0;
}

View File

@@ -0,0 +1,119 @@
# - h5nexus
cmake_minimum_required(VERSION 3.26)
project(h5nexus VERSION 0.1.0 LANGUAGES CXX)
#--- set C++ standard ---------------------------------------------------------
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
#--- set a default build type if none was specified ---------------------------
set(default_build_type "Release")
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
message(STATUS "Setting build type to '${default_build_type}' as none was specified.")
set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE
STRING "Choose the type of build." FORCE)
# Set the possible values of build type for cmake-gui
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
"Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif ()
#--- check for pkg-config -----------------------------------------------------
find_package(PkgConfig REQUIRED)
#--- check for git ------------------------------------------------------------
find_package(Git REQUIRED)
#--- check for HDF5 -----------------------------------------------------------
find_package(HDF5 REQUIRED COMPONENTS CXX)
if(NOT HDF5_FOUND)
message(FATAL_ERROR "HDF5 C++ library not found")
endif()
include_directories(${HDF5_INCLUDE_DIRS})
#--- check for ROOT -----------------------------------------------------------
find_package(ROOT 6.36 REQUIRED COMPONENTS Minuit2)
if (ROOT_miniut2_FOUND)
execute_process(COMMAND root-config --bindir OUTPUT_VARIABLE ROOT_BINDIR)
string(STRIP ${ROOT_BINDIR} ROOT_BINDIR)
execute_process(COMMAND root-config --version OUTPUT_VARIABLE ROOT_VERSION)
string(STRIP ${ROOT_VERSION} ROOT_VERSION)
message("-- Found ROOT: ${ROOT_BINDIR} (found version: ${ROOT_VERSION})")
#---Define useful ROOT functions and macros (e.g. ROOT_GENERATE_DICTIONARY)
include(${ROOT_USE_FILE})
endif (ROOT_miniut2_FOUND)
#--- all checks done -> feed config.h -----------------------------------------
set(HAVE_CONFIG_H 1 CACHE INTERNAL "config.h is available")
configure_file(${CMAKE_SOURCE_DIR}/cmake/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
#--- check if project source is a git repo ------------------------------------
if (EXISTS "${CMAKE_SOURCE_DIR}/.git/HEAD")
message(STATUS "is a git repo")
set(IS_GIT_REPO 1)
else ()
message(STATUS "is NOT a git repo")
set(IS_GIT_REPO 0)
endif ()
#--- start create git-revision.h ----------------------------------------------
if (IS_GIT_REPO)
execute_process(COMMAND sh ${CMAKE_SOURCE_DIR}/src/git_revision.sh)
set(HAVE_GIT_REV_H "-DHAVE_GIT_REV_H")
set(GIT_REV_H "git-revision.h")
else (IS_GIT_REPO)
set(HAVE_GIT_REV_H "")
set(GIT_REV_H "")
endif (IS_GIT_REPO)
#--- end create git-revision.h ------------------------------------------------
#--- write summary of the installation
cmake_host_system_information(RESULT PROCESSOR QUERY PROCESSOR_DESCRIPTION)
message("")
message("|-----------------------------------------------------------------------|")
message("| |")
message("| Summary |")
message("| |")
message("|-----------------------------------------------------------------------|")
message("")
message(" System: ${CMAKE_HOST_SYSTEM_NAME} ${CMAKE_SYSTEM_PROCESSOR} - ${CMAKE_HOST_SYSTEM_VERSION}")
message(" Processor: ${PROCESSOR} (${CMAKE_SYSTEM_PROCESSOR})")
message(" ----------")
message("")
message(" h5nexus Version: ${musrfit_VERSION}")
message(" ----------------")
message("")
message(" Build Type: ${CMAKE_BUILD_TYPE}")
message(" -----------")
message("")
message(" Requirements:")
message(" -------------")
message("")
message(" HDF5 found in ${HDF5_INCLUDE_DIRS}, Version: ${HDF5_VERSION}")
message(" ROOT found in ${ROOT_INCLUDE_DIRS}, Version: ${ROOT_VERSION}")
message("")
message(" Installation directories:")
message(" -------------------------")
message("")
message(" Programs : ${CMAKE_INSTALL_PREFIX}/bin")
message("")
message("-------------------------------------------------------------------------")
message("")
#--- add executable -----------------------------------------------------------
add_executable(h5nexus
../../PNeXus.cpp
main.cpp)
target_compile_options(h5nexus BEFORE PRIVATE "-DHAVE_CONFIG_H" "${HAVE_GIT_REV_H}")
target_include_directories(h5nexus
BEFORE PRIVATE
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/build>
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/../..>
$<BUILD_INTERFACE:${ROOT_INCLUDE_DIRS}>
)
target_link_libraries(h5nexus ${HDF5_CXX_LIBRARIES} ${ROOT_LIBRARIES})

View File

@@ -0,0 +1,5 @@
// config.h
#define PACKAGE_VERSION "@PROJECT_VERSION@"
#define BUILD_TYPE "@CMAKE_BUILD_TYPE@"

View File

@@ -0,0 +1,8 @@
#ifndef GIT_VERSION_H
#define GIT_VERSION_H
#define GIT_BRANCH "@GIT_BRANCH@"
#define GIT_CURRENT_SHA1 @GIT_CURRENT_SHA1@
#endif // GIT_VERSION_H

View File

@@ -0,0 +1,198 @@
# h5nexus Documentation
This directory contains documentation for the h5nexus project.
## Available Documentation
- **Usage.md** - Comprehensive usage guide with detailed examples and workflow patterns
- **mainpage.md** - Main page for Doxygen-generated API documentation
## Generating API Documentation
The project uses [Doxygen](https://www.doxygen.nl/) to generate comprehensive API documentation from source code comments.
### Prerequisites
- **Doxygen** (required) - Install with:
```bash
# macOS
brew install doxygen
# Ubuntu/Debian
sudo apt-get install doxygen
# Fedora/RHEL
sudo dnf install doxygen
```
- **Graphviz** (optional, recommended) - For generating diagrams:
```bash
# macOS
brew install graphviz
# Ubuntu/Debian
sudo apt-get install graphviz
# Fedora/RHEL
sudo dnf install graphviz
```
### Method 1: Using CMake (Recommended)
If you've already configured your build with CMake:
```bash
cd build
make doc
```
This will generate HTML documentation in the `doc/html/` directory.
### Method 2: Using Doxygen Directly
From the project root directory:
```bash
doxygen Doxyfile
```
This will also generate HTML documentation in the `doc/html/` directory.
## Viewing the Documentation
After generation, open the main documentation page in your web browser:
```bash
# macOS
open doc/html/index.html
# Linux
xdg-open doc/html/index.html
# Or manually navigate to: file:///path/to/h5nexus/doc/html/index.html
```
## Documentation Structure
The generated documentation includes:
- **Main Page** - Overview, quick start guide, and architecture description
- **Classes** - Detailed documentation for all classes:
- `nxH5::PNeXus` - Main NeXus file reader/writer class
- `nxH5::PNXdata<T>` - Template class for dataset storage
- `nxH5::PNeXusDeadTime` - Dead time correction calculator
- **Files** - Source file documentation
- **Namespaces** - Namespace documentation (nxH5)
- **Examples** - Code examples from the documentation
- **Class Hierarchy** - Visual class inheritance diagrams
- **Call Graphs** - Function call graphs (if Graphviz is available)
- **Include Dependency Graphs** - Header file dependency visualization
## Documentation Features
The Doxygen configuration includes:
- **Full source browsing** - Browse annotated source code
- **Search functionality** - Fast search across all documentation
- **Interactive SVG diagrams** - Zoomable class and call graphs
- **Cross-references** - Links between related classes and functions
- **Syntax highlighting** - Colored code examples
- **Responsive layout** - Works on desktop and mobile browsers
## Configuration
The Doxygen configuration is stored in `Doxyfile` at the project root. Key settings:
- **Input files**: `inc/`, `src/`, `docu/mainpage.md`
- **Output directory**: `doc/`
- **Output format**: HTML (LaTeX disabled)
- **Graphs**: Enabled if Graphviz/dot is available
- **Extract all**: Yes (documents all code, not just documented items)
- **Source browser**: Enabled
## Updating Documentation
To update the documentation after code changes:
1. Edit Doxygen comments in header/source files
2. Regenerate documentation: `make doc` or `doxygen Doxyfile`
3. Refresh your browser to see changes
### Doxygen Comment Style
The project uses Javadoc-style comments:
```cpp
/**
* @brief Brief description of the function
*
* Detailed description with more information about what
* the function does, how it works, and any important notes.
*
* @param arg1 Description of first parameter
* @param arg2 Description of second parameter
* @return Description of return value
* @throws ExceptionType Description of when this exception is thrown
*
* @note Important note about usage
* @warning Warning about potential issues
*
* @example
* @code
* // Example usage
* MyClass obj;
* obj.myFunction(42, "test");
* @endcode
*/
void myFunction(int arg1, const std::string& arg2);
```
## Cleaning Generated Documentation
To remove generated documentation:
```bash
rm -rf doc/html/
```
## Troubleshooting
### "Doxygen not found" during CMake configuration
Install Doxygen (see Prerequisites above), then reconfigure:
```bash
cd build
cmake ..
```
### Graphs/diagrams not generating
Install Graphviz (see Prerequisites above), then regenerate:
```bash
make doc
# or
doxygen Doxyfile
```
### Broken links in documentation
Ensure all referenced files exist and paths in Doxyfile are correct. The documentation system expects:
- Source files in `inc/` and `src/`
- Main page at `docu/mainpage.md`
### Documentation looks outdated
Clear the output directory and regenerate:
```bash
rm -rf doc/html/
make doc
```
## Additional Resources
- [Doxygen Manual](https://www.doxygen.nl/manual/)
- [Doxygen Special Commands](https://www.doxygen.nl/manual/commands.html)
- [Markdown Support in Doxygen](https://www.doxygen.nl/manual/markdown.html)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
#!/bin/bash
echo "-- Generating header for git hash"
GIT_HEADER="git-revision.h"
[ -d src ] || mkdir src
GIT_BRANCH=`git rev-parse --abbrev-ref HEAD`
GIT_VERSION=`git log -n 1 --pretty=format:"%ad - %h"`
if [ "$(grep -ics "$GIT_VERSION" $GIT_HEADER)" = 1 ]
then
echo "-- No need to generate new $GIT_HEADER - git hash is unchanged"
exit 0;
fi
echo "-- git branch is : " $GIT_BRANCH
echo "-- git version is : " $GIT_VERSION
echo "#ifndef GIT_VERSION_H" > $GIT_HEADER
echo "#define GIT_VERSION_H" >> $GIT_HEADER
echo "" >> $GIT_HEADER
echo "#define GIT_BRANCH \"$GIT_BRANCH\"" >> $GIT_HEADER
echo "#define GIT_CURRENT_SHA1 \"$GIT_VERSION\"" >> $GIT_HEADER
echo "" >> $GIT_HEADER
echo "#endif //GIT_VERSION_H" >> $GIT_HEADER
echo "-- file is generated into" $GIT_HEADER

View File

@@ -0,0 +1,572 @@
/***************************************************************************
main.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 main.cpp
* @brief Command-line interface for the h5nexus NeXus HDF5 file reader/writer
*
* This file contains the main() function and command-line interface for the
* h5nexus program. It provides functionality to:
* - Read and display NeXus HDF5 files
* - Write NeXus HDF5 files using the PNXdata approach
* - Calculate dead time corrections for muon detector data
*
* **Command-Line Options:**
* - --fn <file>: Input NeXus HDF5 file (required)
* - --out <file>: Output NeXus HDF5 file (optional)
* - --debug, -d: Enable debug output
* - --dead_time_estimate, -dt: Calculate dead time corrections
* - --help, -h: Display help message
* - --version, -v: Display version information
*
* @author Andreas Suter
* @date 2007-2026
* @copyright GNU General Public License v2
* @version 1.0
*
* @see nxH5::PNeXus
* @see nxH5::PNeXusDeadTime
*/
#include <cstring>
#include <iostream>
#include <string>
#include <memory>
#include <ctime>
#include <fstream>
#include "hdf5.h"
#include "PNeXus.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_GIT_REV_H
#include "git-revision.h"
#endif
//-----------------------------------------------------------------------------
/**
* @brief Display command-line syntax and help information
*
* Prints the usage syntax and available command-line options for the h5nexus
* program to stdout and exits the program.
*/
void h5nexus_syntax() {
std::cout << std::endl;
std::cout << "usage: h5nexus [--help | -h] |" << std::endl;
std::cout << " [--version | -v] |" << std::endl;
std::cout << " --fn <fln> [--debug | -d]" << std::endl;
std::cout << " [--dead_time_estimate | -dt]]" << std::endl;
std::cout << " [--out <fout>]" << std::endl;
std::cout << " [--data idx <dout>]" << std::endl;
std::cout << std::endl;
std::cout << "options:" << std::endl;
std::cout << " --help, -h: this help." << std::endl;
std::cout << " --version, -v: version of h5nexus." << std::endl;
std::cout << " --fn <fln>: nexus hdf5 input file name <fn>." << std::endl;
std::cout << " --dead_time_estimate, -dt: dead time estimate for the read hdf5 nexus file." << std::endl;
std::cout << " --debug, -d: print additional debug information." << std::endl;
std::cout << " --out <fout>: write the required datasets of a nexus hdf5 file to file " << std::endl;
std::cout << " with name <fout>. Only makes sense together with the option --fn <fln>." << std::endl;
std::cout << " --data idx <dout>: write a single ascii data set with idx to <dout>." << std::endl;
std::cout << " Only makes sense together with the option --fn <fln>." << std::endl;
std::cout << std::endl;
exit(0);
}
//-----------------------------------------------------------------------------
/**
* @brief Calculate dead time corrections for muon detector data
*
* Estimates dead time corrections for each detector in the NeXus file using
* the PNeXusDeadTime class and ROOT Minuit2 minimization.
*
* @param nxs Pointer to the PNeXus object containing the data
* @param debug If true, print additional debug information
*
* @see nxH5::PNeXusDeadTime
*/
std::vector<float> h5nexus_deadTimeEstimate(const nxH5::PNeXus *nxs, bool debug)
{
if (debug) {
std::cout << std::endl;
std::cout << std::endl << "+++++++++++++++++++";
std::cout << std::endl << "in deadTimeEstimate";
std::cout << std::endl << "+++++++++++++++++++";
std::cout << std::endl;
}
nxH5::PNeXusDeadTime ndt(nxs, debug);
auto dims = ndt.GetDimensions();
for (unsigned int i=0; i<dims[1]; i++) {
ndt.minimize(i);
}
return ndt.GetDeadTimeEstimated();
}
//-----------------------------------------------------------------------------
void h5nexus_writeData(const nxH5::PNeXus *nxs, const std::string& fln, const std::string& dataOutFln, const int idx, const std::vector<float> dte)
{
std::cout << std::endl << "as35> in h5nexus_writeData: idf version: " << nxs->GetIdfVersion();
std::vector<int> counts;
std::vector<float> dt;
std::vector<long long unsigned int> dims;
float resolution{0.0};
int good_frames{0};
if (nxs->GetIdfVersion() == 1) {
if (nxs->HasDataset("/run/histogram_data_1/counts")) {
std::cout << std::endl << "as35> found counts in idf version 1";
}
} else {
auto dataMap = nxs->GetDataMap();
if (nxs->HasDataset("/raw_data_1/detector_1/counts")) {
std::cout << std::endl << "as35> found counts in idf version 2";
auto counts_data = std::any_cast<nxH5::PNXdata<int>>(dataMap["/raw_data_1/detector_1/counts"]);
counts = counts_data.GetData();
auto dd = counts_data.GetDimensions();
for (auto i=0; i<dd.size(); i++)
dims.push_back(dd[i]);
std::cout << std::endl << "as35> dims: " << dims.size() << ": ";
for (auto i=0; i<dims.size(); i++)
std::cout << dims[i] << ", ";
}
if (idx >= dims[1]) {
std::cerr << std::endl << "**ERROR** idx=" << idx << " is >= number of dataset=" << dims[1] << std::endl;
return;
}
if (nxs->HasDataset("/raw_data_1/detector_1/dead_time")) {
std::cout << std::endl << "as35> found dead_time in idf version 2";
auto dt_data = std::any_cast<nxH5::PNXdata<float>>(dataMap["/raw_data_1/detector_1/dead_time"]);
dt = dt_data.GetData();
}
if (nxs->HasDataset("/raw_data_1/instrument/detector_1/resolution")) {
std::cout << std::endl << "as35> found resolution in idf version 2";
auto r_data = std::any_cast<nxH5::PNXdata<int>>(dataMap["/raw_data_1/instrument/detector_1/resolution"]);
auto rr = r_data.GetData();
resolution = (float)rr[0];
if (r_data.HasAttribute("units")) {
std::string units = std::any_cast<std::string>(r_data.GetAttribute("units"));
if (units == "picoseconds")
resolution *= 1.0e-6;
else if (units == "nanoseconds")
resolution *= 1.0e-3;
}
}
if (nxs->HasDataset("/raw_data_1/good_frames")) {
std::cout << std::endl << "as35> found good_frames in idf version 2";
auto gf_data = std::any_cast<nxH5::PNXdata<int>>(dataMap["/raw_data_1/good_frames"]);
good_frames = gf_data.GetData()[0];
}
}
std::cout << std::endl;
float dtei;
if (dte.size() > idx)
dtei = dte[idx];
// write dataset
std::ofstream fout(dataOutFln);
fout << "# NeXus fln: " << fln << std::endl;
fout << "# idx=" << idx << std::endl;
fout << "# resolution : " << resolution << " (us)" << std::endl;
fout << "# good_frames : " << good_frames << std::endl;
fout << "# dead_time : " << dt[idx] << " (us) : from file" << std::endl;
fout << "# dead_time : " << dtei << " (us) : from estimater" << std::endl;
fout << "# ------" << std::endl;
fout << "# raw counts, dead time corrected counts (file), dead time corrected counts (estimated)" << std::endl;
int cc{0}, dtcc{0}, dtecc{0};
// see https://docs.mantidproject.org/v3.9.0/algorithms/ApplyDeadTimeCorr-v1.html#algm-applydeadtimecorr
for (auto i=0; i<dims[2]; i++) {
cc = counts[i + idx*dims[2]];
dtcc = (int)((float)cc / (1.0 - (float)cc * (dt[idx]/(resolution*(float)good_frames))));
dtecc = (int)((float)cc / (1.0 - (float)cc * (dtei/(resolution*(float)good_frames))));
fout << cc << ", " << dtcc << ", " << dtecc << std::endl;
}
fout.close();
}
//-----------------------------------------------------------------------------
/**
* @brief Write NeXus HDF5 file using the PNXdata approach
*
* Writes all datasets from the PNeXus data map to a new NeXus HDF5 file.
* This function uses the WriteNexusFile() method to create a complete
* NeXus file with all groups, datasets, and attributes.
*
* @param nxs Pointer to the PNeXus object containing the data to write
* @param outFileName Output filename for the NeXus HDF5 file
* @param debug If true, print additional debug information
*
* @note Currently only supports IDF version 2 files
*
* @see nxH5::PNeXus::WriteNexusFile()
*/
void h5nexus_writeTest(const nxH5::PNeXus *nxs, const std::string& outFileName, bool debug)
{
if (debug) {
std::cout << std::endl;
std::cout << "++++++++++++++++++++" << std::endl;
std::cout << "Writing NeXus file" << std::endl;
std::cout << "++++++++++++++++++++" << std::endl;
std::cout << std::endl;
}
if (nxs->GetIdfVersion() == 1) {
std::cerr << "Error: IDF v1 write not yet implemented" << std::endl;
return;
}
// Write using the read object's data
int result = const_cast<nxH5::PNeXus*>(nxs)->WriteNexusFile(outFileName, nxs->GetIdfVersion());
if (result == 0) {
std::cout << "Successfully wrote: " << outFileName << std::endl;
} else {
std::cerr << "Failed to write file: " << outFileName << std::endl;
}
// write data from scratch
std::unique_ptr<nxH5::PNeXus> nxs_out = std::make_unique<nxH5::PNeXus>();
std::vector<int> ival;
std::vector<float> fval;
std::vector<std::string> sval;
H5::StrType strType(H5::PredType::C_S1, H5T_VARIABLE);
// ----------
// raw_data_1
// ----------
// IDF version
ival.push_back(2);
nxs_out->AddDataset<int>("/raw_data_1/IDF_version", ival, {1}, H5::PredType::NATIVE_INT);
ival.clear();
// add group attribute to '/raw_data_1'
nxs_out->AddGroupAttribute("/raw_data_1", "NX_class", std::string("NXentry"));
// beamline
sval.push_back("piE3");
nxs_out->AddDataset<std::string>("/raw_data_1/beamline", sval, {1}, strType);
sval.clear();
// definition
sval.push_back("muonTD");
nxs_out->AddDataset<std::string>("/raw_data_1/definition", sval, {1}, strType);
sval.clear();
// run_number
ival.push_back(1234);
nxs_out->AddDataset<int>("/raw_data_1/run_number", ival, {1}, H5::PredType::NATIVE_INT);
ival.clear();
// title
sval.push_back("this is the run title.");
nxs_out->AddDataset<std::string>("/raw_data_1/title", sval, {1}, strType);
sval.clear();
// start time
sval.push_back("2026-01-01T01:02:03");
nxs_out->AddDataset<std::string>("/raw_data_1/start_time", sval, {1}, strType);
sval.clear();
// end time
sval.push_back("2026-01-01T02:03:42");
nxs_out->AddDataset<std::string>("/raw_data_1/end_time", sval, {1}, strType);
sval.clear();
// experiment_identifier - pgroup for PSI
sval.push_back("p18324");
nxs_out->AddDataset<std::string>("/raw_data_1/experiment_identifier", sval, {1}, strType);
sval.clear();
// -------------------
// detector_1 (NXdata)
// -------------------
// add group attribute to /raw_data_1/instrument
nxs_out->AddGroupAttribute("/raw_data_1/detector_1", "NX_class", std::string("NXdata"));
// counts
std::vector<int> counts(16*66000, 42); // data 16 histos with length 66000
nxs_out->AddDataset<int>("/raw_data_1/detector_1/counts", counts, {1, 16, 66000}, H5::PredType::NATIVE_INT);
// attributes for counts
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "signal", 1);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "axes", std::string("period_index,spectrum_index,raw_time"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "long_name", std::string("positron_counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "t0_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "first_good_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "last_good_bin", 66000);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "units", std::string("counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/detector_1/counts", "target", std::string("/raw_data_1/instrument/detector_1/counts"));
// raw_time
std::vector<float> raw_time(66000, 0.0);
for (unsigned int i=0; i<raw_time.size(); i++)
raw_time[i] = 0.1953125f*1.0e-3*((float)i-2741.0f+0.5f);
nxs_out->AddDataset<float>("/raw_data_1/detector_1/raw_time", raw_time, {66000}, H5::PredType::NATIVE_FLOAT);
// attributes raw_time
nxs_out->AddDatasetAttribute<float>("/raw_data_1/detector_1/raw_time", "units", std::string("microseconds"));
nxs_out->AddDatasetAttribute<float>("/raw_data_1/detector_1/raw_time", "target", std::string("/raw_data_1/instrument/detector_1/raw_time"));
// ----------
// instrument
// ----------
// add group attribute to /raw_data_1/instrument
nxs_out->AddGroupAttribute("/raw_data_1/instrument", "NX_class", std::string("NXinstrument"));
// name
sval.push_back("LEM");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/name", sval, {1}, strType);
sval.clear();
// ------
// source
// ------
// add group attribute to /raw_data_1/instrument/source
nxs_out->AddGroupAttribute("/raw_data_1/instrument/source", "NX_class", std::string("NXsource"));
// name
sval.push_back("PSI");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/source/name", sval, {1}, strType);
sval.clear();
// type
sval.push_back("continuous muon source");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/source/types", sval, {1}, strType);
sval.clear();
// probe
sval.push_back("postive muons");
nxs_out->AddDataset<std::string>("/raw_data_1/instrument/source/probe", sval, {1}, strType);
sval.clear();
// -----------------------
// detector_1 (NXdetector)
// -----------------------
// add group attribute to /raw_data_1/instrument/detector_1
nxs_out->AddGroupAttribute("/raw_data_1/instrument/detector_1", "NX_class", std::string("NXdetector"));
// counts
nxs_out->AddDataset<int>("/raw_data_1/instrument/detector_1/counts", counts, {1, 16, 66000}, H5::PredType::NATIVE_INT);
// attributes for counts
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "signal", 1);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "axes", std::string("period_index,spectrum_index,raw_time"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "long_name", std::string("positron_counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "t0_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "first_good_bin", 2741);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "last_good_bin", 66000);
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "units", std::string("counts"));
nxs_out->AddDatasetAttribute<int>("/raw_data_1/instrument/detector_1/counts", "target", std::string("/raw_data_1/instrument/detector_1/counts"));
// raw_time
nxs_out->AddDataset<float>("/raw_data_1/instrument/detector_1/raw_time", raw_time, {66000}, H5::PredType::NATIVE_FLOAT);
// attributes raw_time
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/raw_time", "units", std::string("microseconds"));
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/raw_time", "target", std::string("/raw_data_1/instrument/detector_1/raw_time"));
// resolution
fval.push_back(195.3125);
nxs_out->AddDataset<float>("/raw_data_1/instrument/detector_1/resolution", fval, {1}, H5::PredType::NATIVE_FLOAT);
fval.clear();
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/resolution",
"units", std::string("picoseconds"));
// spectrum_index
for (unsigned int i=0; i<66000; i++)
ival.push_back(i+1);
nxs_out->AddDataset<int>("/raw_data_1/instrument/detector_1/spectrum_index", ival, {66000}, H5::PredType::NATIVE_FLOAT);
ival.clear();
// dead_time
std::vector<float> deadTime(66000, 0.0);
nxs_out->AddDataset<float>("/raw_data_1/instrument/detector_1/dead_time", deadTime, {66000}, H5::PredType::NATIVE_FLOAT);
// attributes dead_time
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/dead_time", "available", 0);
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/dead_time", "units", std::string("microseconds"));
nxs_out->AddDatasetAttribute<float>("/raw_data_1/instrument/detector_1/dead_time", "target", std::string("/raw_data_1/instrument/detector_1/dead_time"));
// add root attributes
// file name
nxs_out->AddRootAttribute("file_name", std::string("_test.nxs"));
// date-time
std::time_t time = std::time({});
char timeString[std::size("yyyy-mm-ddThh:mm:ssZ")];
std::strftime(std::data(timeString), std::size(timeString),
"%FT%TZ", std::gmtime(&time));
nxs_out->AddRootAttribute("file_time", std::string(timeString));
// NeXus version
nxs_out->AddRootAttribute("NeXus_Version", std::string("4.3.0"));
// hdf5 version
nxs_out->AddRootAttribute("HDF5_Version", std::string(nxs->GetHdf5Version()));
// creator
nxs_out->AddRootAttribute("creator", std::string("h5nexus - PSI"));
nxs_out->WriteNexusFile("_test.nxs");
}
//-----------------------------------------------------------------------------
/**
* @brief Main entry point for the h5nexus program
*
* Parses command-line arguments and performs the requested operations:
* - Read and display NeXus HDF5 file information
* - Write NeXus HDF5 files with modified data
* - Calculate dead time corrections
*
* @param argc Number of command-line arguments
* @param argv Array of command-line argument strings
*
* @return 0 on success, non-zero on error
*
* @see h5nexus_syntax() for available command-line options
*/
int main(int argc, char *argv[])
{
std::string fileName{""};
std::string fileNameOut{""};
std::string dataNameOut{""};
int idx{-1};
bool printDebug{false};
bool deadTimeEstimate{false};
if (argc == 1)
h5nexus_syntax();
for (int i=1; i<argc; i++) {
if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) {
h5nexus_syntax();
} else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--version")) {
#ifdef HAVE_CONFIG_H
#ifdef HAVE_GIT_REV_H
std::cout << std::endl << "h5nexus version: " << PACKAGE_VERSION << ", git-branch: " << GIT_BRANCH << ", git-rev: " << GIT_CURRENT_SHA1 << " (" << BUILD_TYPE << ")" << std::endl << std::endl;
#else
std::cout << std::endl << "h5nexus version: " << PACKAGE_VERSION << " (" << BUILD_TYPE << ")" << std::endl << std::endl;
#endif
#else
#ifdef HAVE_GIT_REV_H
std::cout << std::endl << "h5nexus git-branch: " << GIT_BRANCH << ", git-rev: " << GIT_CURRENT_SHA1 << std::endl << std::endl;
#else
std::cout << std::endl << "h5nexus version: unkown." << std::endl << std::endl;
#endif
#endif
return 0;
} else if (!strcmp(argv[i], "--fn")) {
if (i+1 >= argc) {
std::cout << std::endl << "**ERROR** found --fn without <fln>." << std::endl;
h5nexus_syntax();
}
i++;
fileName = argv[i];
} else if (!strcmp(argv[i], "-dt") || !strcmp(argv[i], "--dead_time_estimate")) {
deadTimeEstimate = true;
} else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) {
printDebug = true;
} else if (!strcmp(argv[i], "--data")) {
if (i+2 >= argc) {
std::cout << std::endl << "**ERROR** in --data idx <dout>." << std::endl;
h5nexus_syntax();
}
int ii;
try {
ii = std::stoi(argv[i+1]);
} catch (const std::invalid_argument& ia) {
std::cout << std::endl << "**ERROR** couldn't convert idx from arguments given." << std::endl;
h5nexus_syntax();
}
if (ii < 0) {
std::cout << std::endl << "**ERROR** found idx < 0, namely " << ii << std::endl;
h5nexus_syntax();
}
idx = ii;
dataNameOut = argv[i+2];
i += 2;
} else if (!strcmp(argv[i], "--out")) {
if (i+1 >= argc) {
std::cout << std::endl << "**ERROR** found --out without <fout>." << std::endl;
h5nexus_syntax();
}
i++;
fileNameOut = argv[i];
} else {
h5nexus_syntax();
}
}
if (fileName.empty()) {
std::cerr << std::endl;
std::cerr << "**ERROR** <fln> is missing." << std::endl;
std::cerr << std::endl;
h5nexus_syntax();
}
if (printDebug) {
std::cout << std::endl;
std::cout << ">> fln = '" << fileName << "'" << std::endl;
std::cout << ">> fout = '" << fileNameOut << "'" << std::endl;
std::cout << ">> dout = '" << dataNameOut << "', idx=" << idx << std::endl;
std::cout << std::endl;
}
std::unique_ptr<nxH5::PNeXus> nxs = std::make_unique<nxH5::PNeXus>(fileName, printDebug);
nxs->Dump();
std::vector<float> dte;
if (deadTimeEstimate) {
dte = h5nexus_deadTimeEstimate(nxs.get(), printDebug);
}
if (!fileNameOut.empty()) {
h5nexus_writeTest(nxs.get(), fileNameOut, printDebug);
}
if (!dataNameOut.empty()) {
h5nexus_writeData(nxs.get(), fileName, dataNameOut, idx, dte);
}
return 0;
}

Binary file not shown.