Format

HDF5 is a binary container of named datasets; NeXus files (.nxs) are HDF5 with a standardized internal layout.

The examples below read the same two detector signals and instrument metadata from a D22 NeXus file. The file name is a placeholder, while the internal group and field names are taken from the file. Other instruments can use different paths. The unit comments reproduce the units attributes stored with the metadata fields.

The detector datasets have shapes (128, 256, 1) and (96, 256, 1). The examples select their only frames and preserve the remaining dimension order. The file does not associate those dimensions with physical detector axes. Match their order and orientation to the BornAgain detector as described for detector images.

HDF5 with h5py

h5py exposes the HDF5 groups, datasets, and attributes directly. It is not installed with BornAgain; install it once with pip install h5py.

import h5py
import numpy as np

filename = "path/to/data.nxs"

with h5py.File(filename, "r") as file:
    instrument = file["entry0/D22"]
    image1 = np.asarray(file["entry0/data1/MultiDetector1_data"][:, :, 0])  # shape (128, 256)
    image2 = np.asarray(file["entry0/data2/MultiDetector2_data"][:, :, 0])  # shape (96, 256)

    wavelength = float(instrument["selector/wavelength"][0])  # angstrom
    detector1_distance = float(instrument["Detector 1/det1_actual"][0])  # m
    detector2_distance = float(instrument["Detector 2/det2_actual"][0])  # m
    pixel_size1_x = float(instrument["Detector 1/det1_pixel_size_x"][0])  # mm
    pixel_size1_y = float(instrument["Detector 1/det1_pixel_size_y"][0])  # mm
    pixel_size2_x = float(instrument["Detector 2/det2_pixel_size_x"][0])  # mm
    pixel_size2_y = float(instrument["Detector 2/det2_pixel_size_y"][0])  # mm

The entry0/data1 and entry0/data2 groups have the NX_class="NXdata" attribute. Their signal fields link to datasets in the corresponding NX_class="NXdetector" groups and have signal=1, which marks the default plottable signal. The datasets have no unit or long_name attribute, so the file does not identify them more specifically as counts or calibrated intensity.

Use the h5py group and dataset keys to inspect the paths in another HDF5 file. Files compressed with external filters such as Bitshuffle or Blosc also require pip install hdf5plugin and import hdf5plugin before opening the file.

If the file comes from a detector whose HDF5 flavor Fabio knows (e.g. Eiger), fabio.open works as well, as for any detector image — no dataset path needed.

NeXus with nexusformat

nexusformat represents NeXus classes and fields directly. Install it with pip install nexusformat. Use h5py instead when a file has no NeXus metadata or uses a general HDF5 virtual-dataset layout.

import nexusformat.nexus as nx
import numpy as np

filename = "path/to/data.nxs"
root = nx.nxload(filename)

entry = root.NXentry[0]
instrument = entry.NXinstrument[0]
image1 = np.asarray(entry["data1"].nxsignal[:, :, 0])  # shape (128, 256)
image2 = np.asarray(entry["data2"].nxsignal[:, :, 0])  # shape (96, 256)

wavelength = float(instrument["selector/wavelength"])  # angstrom
detector1_distance = float(instrument["Detector 1/det1_actual"])  # m
detector2_distance = float(instrument["Detector 2/det2_actual"])  # m
pixel_size1_x = float(instrument["Detector 1/det1_pixel_size_x"])  # mm
pixel_size1_y = float(instrument["Detector 1/det1_pixel_size_y"])  # mm
pixel_size2_x = float(instrument["Detector 2/det2_pixel_size_x"])  # mm
pixel_size2_y = float(instrument["Detector 2/det2_pixel_size_y"])  # mm

Here, NXentry, NXinstrument, NXdetector, and NXvelocity_selector come from the file’s NX_class attributes. nxsignal follows the signal declared by the NXdata group. The root.tree property shows the structure of another NeXus file in an interactive Python session.