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.
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.
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.