1#!/usr/bin/env python
2"""
3Writes a simple NeXus HDF5 file using h5py with links
4according to the example from Figure 2.1 in the Design chapter
5"""
7from pathlib import Path
8import h5py
9import numpy
11filename = str(Path(__file__).absolute().parent.parent / "simple_example.dat")
12buffer = numpy.loadtxt(filename).T
13tthData = buffer[0] # float[]
14countsData = numpy.asarray(buffer[1], "int32") # int[]
16with h5py.File("simple_example_write2.hdf5", "w") as f: # create the HDF5 NeXus file
17 f.attrs["default"] = "entry"
19 nxentry = f.create_group("entry")
20 nxentry.attrs["NX_class"] = "NXentry"
21 nxentry.attrs["default"] = "data"
23 nxinstrument = nxentry.create_group("instrument")
24 nxinstrument.attrs["NX_class"] = "NXinstrument"
26 nxdetector = nxinstrument.create_group("detector")
27 nxdetector.attrs["NX_class"] = "NXdetector"
29 # store the data in the NXdetector group
30 ds_tth = nxdetector.create_dataset("two_theta", data=tthData)
31 ds_tth.attrs["units"] = "degrees"
32 ds_counts = nxdetector.create_dataset("counts", data=countsData)
33 ds_counts.attrs["units"] = "counts"
35 # create the NXdata group to define the default plot
36 nxdata = nxentry.create_group("data")
37 nxdata.attrs["NX_class"] = "NXdata"
38 nxdata.attrs["signal"] = "counts"
39 nxdata.attrs["axes"] = "two_theta"
40 nxdata.attrs["two_theta_indices"] = [
41 0,
42 ]
44 source_addr = "/entry/instrument/detector/two_theta" # existing data
45 target_addr = "two_theta" # new location
46 ds_tth.attrs["target"] = source_addr # a NeXus API convention for links
47 nxdata[target_addr] = f[source_addr] # hard link
48 # nxdata._id.link(source_addr, target_addr, h5py.h5g.LINK_HARD)
50 source_addr = "/entry/instrument/detector/counts" # existing data
51 target_addr = "counts" # new location
52 ds_counts.attrs["target"] = source_addr # a NeXus API convention for links
53 nxdata[target_addr] = f[source_addr] # hard link
54 # nxdata._id.link(source_addr, target_addr, h5py.h5g.LINK_HARD)
It is interesting to compare the output of the h5dump
of the data file simple_example_write2.hdf5 with our Python instructions.
See the downloads section below.
Look carefully! It appears in the output of
h5dump that the actual data for two_theta
and counts has moved into
the NXdata group at HDF5 path /entry/data! But we stored
that data in the NXdetector group at /entry/instrument/detector.
This is normal for h5dump output.
A bit of explanation is necessary at this point.
The data is not stored in either HDF5 group directly. Instead, HDF5
creates a DATA storage element in the file and posts a reference
to that DATA storage element as needed.
An HDF5 hard link
requests another reference to that same DATA storage element.
The h5dump tool describes in full that DATA storage element
the first time (alphabetically) it is called. In our case, that is within the
NXdata group. The next time it is called, within the
NXdetector group, h5dump reports that a hard link
has been made and shows the HDF5 path to the description.
NeXus recognizes this behavior of the HDF5 library and adds an additional structure
when building hard links, the target attribute,
to preserve the original location of the data. Not that it actually matters.
the punx tree tool knows about the additional NeXus
target attribute and shows the data to appear in its original
location, in the NXdetector group.