NAPI: The NeXus Application Programming Interface

The NeXus API consists of routines to read and write NeXus data files. It was written to provide a simple to use and consistent common interface for all supported backends (XML, HDF4 and HDF5) to scientific programmers and other users of the NeXus Data Standard.

Note

It is not necessary to use the NAPI to write or read NeXus data files. The intent of the NAPI is to simplify the programming effort to use the HDF programming interface. There are Examples of writing and reading NeXus data files to help you understand.

This section will provide a brief overview of the available functionality. Further documentation of the NeXus Application Programming Interface (NAPI) for bindings to specific programming language can be found in the NAPI chapter and may be downloaded from the NeXus development site. [1]

For an even more detailed description of the internal workings of NAPI see the NeXus Internals manual, copied from the NeXus code repository. That document is written for programmers who want to work on the NAPI itself. If you are new to NeXus and just want to implement basic file reading or writing you should not start by reading that.

How do I write a NeXus file?

The NeXus Application Program Interface (NAPI) provides a set of subroutines that make it easy to read and write NeXus files. These subroutines are available in C, Fortran 77, Fortran 90, Java, Python, C++, and IDL.

The API uses a very simple state model to navigate through a NeXus file. When you open a file, the API provides a file handle, which stores the current location, i.e. which group and/or field is currently open. Read and write operations then act on the currently open entity. Following the simple example titled Example structure of a simple data file, we walk through a schematic of NeXus program written in C (without any error checking or real data).

Writing a simple NeXus file using NAPI

Note

We assume the program can define the arrays tth and counts, each length n. This part has been omitted from the example code.

 1 #include "napi.h"
 2 
 3  int main()
 4  {
 5     /* we start with known arrays tth and counts, each length n */
 6     NXhandle fileID;
 7     NXopen ("NXfile.nxs", NXACC_CREATE, &fileID);
 8       NXmakegroup (fileID, "Scan", "NXentry");
 9       NXopengroup (fileID, "Scan", "NXentry");
10         NXmakegroup (fileID, "data", "NXdata");
11         NXopengroup (fileID, "data", "NXdata");
12           NXmakedata (fileID, "two_theta", NX_FLOAT32, 1, &n);
13           NXopendata (fileID, "two_theta");
14             NXputdata (fileID, tth);
15             NXputattr (fileID, "units", "degrees", 7, NX_CHAR);
16           NXclosedata (fileID);  /* two_theta */
17           NXmakedata (fileID, "counts", NX_FLOAT32, 1, &n);
18           NXopendata (fileID, "counts");
19             NXputdata (fileID, counts);
20           NXclosedata (fileID);  /* counts */
21         NXclosegroup (fileID);  /* data */
22       NXclosegroup (fileID);  /* Scan */
23     NXclose (&fileID);
24     return;
25 }

program analysis

  1. line 7:

    Open the file NXfile.nxs with create access (implying write access). NAPI [2] returns a file identifier of type NXhandle.

  2. line 7:

    Next, we create the NXentry group to contain the scan using NXmakegroup() and then open it for access using NXopengroup(). [3]

  3. line 10:

    The plottable data is contained within an NXdata group, which must also be created and opened.

  4. line 12:

    To create a field, call NXmakedata(), specifying the data name, type (NX_FLOAT32), rank (in this case, 1), and length of the array (n). Then, it can be opened for writing. [4]

  5. line 14:

    Write the data using NXputdata().

  6. line 15:

    With the field still open, we can also add some field attributes, such as the data units, [5] [6] which are specified as a character string (type="NX_CHAR" [7]) that is 7 bytes long.

  7. line 16:

    Then we close the field before opening another. In fact, the API will do this automatically if you attempt to open another field, but it is better style to close it yourself.

  8. line 17:

    The remaining fields in this group are added in a similar fashion. Note that the indentation whenever a new field or group are opened is just intended to make the structure of the NeXus file more transparent.

  9. line 20:

    Finally, close the groups (NXdata and NXentry) before closing the file itself.

How do I read a NeXus file?

Reading a NeXus file works in the same way by traversing the tree with the handle.

This schematic C code will read the two-theta array created in the example above. (Again, compare this example with Reading a simple NeXus file using native HDF5 commands in C.)

Reading a simple NeXus file using NAPI

 1  NXopen ('NXfile.nxs', NXACC_READ, &fileID);
 2    NXopengroup (fileID, "Scan", "NXentry");
 3      NXopengroup (fileID, "data", "NXdata");
 4        NXopendata (fileID, "two_theta");
 5          NXgetinfo (fileID, &rank, dims, &datatype);
 6          NXmalloc ((void **) &tth, rank, dims, datatype);
 7          NXgetdata (fileID, tth);
 8        NXclosedata (fileID);
 9      NXclosegroup (fileID);
10    NXclosegroup (fileID);
11  NXclose (fileID);

How do I browse a NeXus file?

NeXus files can also be viewed by a command-line browser, nxbrowse, which is included as a helper tool in the NeXus API distribution. The following is an example session of nxbrowse nxbrowse to view a data file.

Using nxbrowse

 1 %> nxbrowse lrcs3701.nxs
 2 
 3 NXBrowse 3.0.0. Copyright (C) 2000 R. Osborn, M. Koennecke, P. Klosowski
 4     NeXus_version = 1.3.3
 5     file_name = lrcs3701.nxs
 6     file_time = 2001-02-11 00:02:35-0600
 7     user = EAG/RO
 8 NX> dir
 9   NX Group : Histogram1 (NXentry)
10   NX Group : Histogram2 (NXentry)
11 NX> open Histogram1
12 NX/Histogram1> dir
13   NX Data  : title[44] (NX_CHAR)
14   NX Data  : analysis[7] (NX_CHAR)
15   NX Data  : start_time[24] (NX_CHAR)
16   NX Data  : end_time[24] (NX_CHAR)
17   NX Data  : run_number (NX_INT32)
18   NX Group : sample (NXsample)
19   NX Group : LRMECS (NXinstrument)
20   NX Group : monitor1 (NXmonitor)
21   NX Group : monitor2 (NXmonitor)
22   NX Group : data (NXdata)
23 NX/Histogram1> read title
24   title[44] (NX_CHAR) = MgB2 PDOS 43.37g 8K 120meV E0@240Hz T0@120Hz
25 NX/Histogram1> open data
26 NX/Histogram1/data> dir
27   NX Data  : title[44] (NX_CHAR)
28   NX Data  : data[148,750] (NX_INT32)
29   NX Data  : time_of_flight[751] (NX_FLOAT32)
30   NX Data  : polar_angle[148] (NX_FLOAT32)
31 NX/Histogram1/data> read time_of_flight
32   time_of_flight[751] (NX_FLOAT32) = [ 1900.000000 1902.000000 1904.000000 ...]
33     units = microseconds
34     long_name = Time-of-Flight [microseconds]
35 NX/Histogram1/data> read data
36   data[148,750] (NX_INT32) = [ 1 1 0 ...]
37     units = counts
38     signal = 1 
39     long_name = Neutron Counts
40     axes = polar_angle:time_of_flight
41 NX/Histogram1/data> close
42 NX/Histogram1> close
43 NX> quit

program analysis

  1. line 1:

    Start nxbrowse from the UNIX command line and open file lrcs3701.nxs from IPNS/LRMECS.

  2. line 8:

    List the contents of the current group.

  3. line 11:

    Open the NeXus group Histogram1.

  4. line 23:

    Print the contents of the NeXus data labeled title.

  5. line 41:

    Close the current group.

  6. line 43:

    Quits nxbrowse.

The source code of nxbrowse [8] provides an example of how to write a NeXus reader. The test programs included in the NeXus API may also be useful to study.