h5py
example writing a simple NeXus data file with links¶
Building on the previous example, we wish to identify our measured data with
the detector on the instrument where it was generated.
In this hypothetical case, since the detector was positioned at some
angle two_theta, we choose to store both datasets,
two_theta
and counts
, in a NeXus group.
One appropriate NeXus group is NXdetector.
This group is placed in a NXinstrument group
which is placed in a NXentry group.
Still, NeXus requires a NXdata group.
Rather than duplicate the same data already placed in the detector group,
we choose to link to those datasets from the NXdata
group.
(Compare the next figure with Linking in a NeXus file in the
NeXus Design chapter of the NeXus User Manual.)
The NeXus Design chapter provides a figure
(Linking in a NeXus file) with a small variation from our
previous example, placing the measured data
within the /entry/instrument/detector
group.
Links are made from that data to the /entry/data
group.
The Python code to build an HDF5 data file with that structure (using numerical data from the previous example) is shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #!/usr/bin/env python
'''
Writes a simple NeXus HDF5 file using h5py with links
according to the example from Figure 2.1 in the Design chapter
'''
import h5py
import numpy
buffer = numpy.loadtxt('input.dat').T
tthData = buffer[0] # float[]
countsData = numpy.asarray(buffer[1],'int32') # int[]
f = h5py.File('writer_2_1.hdf5', "w") # create the HDF5 NeXus file
f.attrs['default'] = 'entry'
nxentry = f.create_group('entry')
nxentry.attrs['NX_class'] = 'NXentry'
nxentry.attrs['default'] = 'data'
nxinstrument = nxentry.create_group('instrument')
nxinstrument.attrs['NX_class'] = 'NXinstrument'
nxdetector = nxinstrument.create_group('detector')
nxdetector.attrs['NX_class'] = 'NXdetector'
# store the data in the NXdetector group
ds_tth = nxdetector.create_dataset('two_theta', data=tthData)
ds_tth.attrs['units'] = 'degrees'
ds_counts = nxdetector.create_dataset('counts', data=countsData)
ds_counts.attrs['units'] = 'counts'
# create the NXdata group to define the default plot
nxdata = nxentry.create_group('data')
nxdata.attrs['NX_class'] = 'NXdata'
nxdata.attrs['signal'] = 'counts'
nxdata.attrs['axes'] = 'two_theta'
nxdata.attrs['two_theta_indices'] = [0,]
source_addr = '/entry/instrument/detector/two_theta' # existing data
target_addr = 'two_theta' # new location
ds_tth.attrs['target'] = source_addr # a NeXus API convention for links
nxdata._id.link(source_addr, target_addr, h5py.h5g.LINK_HARD)
source_addr = '/entry/instrument/detector/counts' # existing data
target_addr = 'counts' # new location
ds_counts.attrs['target'] = source_addr # a NeXus API convention for links
nxdata._id.link(source_addr, target_addr, h5py.h5g.LINK_HARD)
f.close() # be CERTAIN to close the file
|
It is interesting to compare the output of the h5dump
of the data file writer_2_1.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 h5toText.py
tool knows about the additional NeXus
target
attribute and shows the data to appear in its original
location, in the NXdetector
group.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | writer_2_1.hdf5
@default = entry
entry:NXentry
@NX_class = NXentry
@default = data
data:NXdata
@NX_class = NXdata
@signal = counts
@axes = two_theta
@two_theta_indices = [0]
counts --> /entry/instrument/detector/counts
two_theta --> /entry/instrument/detector/two_theta
instrument:NXinstrument
@NX_class = NXinstrument
detector:NXdetector
@NX_class = NXdetector
counts:int32[31] = [1037, 1318, 1704, '...', 1321]
@units = counts
@target = /entry/instrument/detector/counts
two_theta:float64[31] = [17.926079999999999, 17.925909999999998, 17.925750000000001, '...', 17.92108]
@units = degrees
@target = /entry/instrument/detector/two_theta
|
downloads¶
The Python code and files related to this section may be downloaded from the following table.
file | description |
---|---|
writer_2_1.py |
python code to write example writer_2_1 |
writer_2_1.hdf5 |
NeXus file written by this code |
writer_2_1_h5dump.txt |
h5dump analysis of the NeXus file |
writer_2_1_structure.txt |
h5toText analysis of the NeXus file |