An Application Program Interface (API) has been developed to facilitate the reading and writing of NeXus files. Those writing utilities to produce NeXus files are encouraged to use the API, rather than lower-level HDF routines, in order to ensure compliance with the NeXus standard. The latest version supports the reading and writing of both HDF4 and HDF5 files. The routines have been written in C although we also provide Fortran 77 and 90 wrappers. The API was defined in the SoftNeSS'96 workshop.
The NeXus Application Program Interface is a suite of subroutines, written in C but with wrappers in Fortran 77 and 90. The subroutines call HDF routines to read and write the NeXus files with the correct structure. An API serves a number of useful purposes:
For these reasons, we request that all NeXus files are written using the supplied API. We cannot be sure that anything written using the underlying HDF API will be recognized by NeXus-aware utilities.
The core API provides the basic routines for reading, writing and navigating NeXus files. It is designed to be modal; there is a hidden state that determines which groups and data sets are open at any given moment, and subsequent operations are implicitly performed on these entities. This cuts down the number of parameters to pass around in API calls, at the cost of forcing a certain pre-approved mode d'emploi. This mode d'emploi will be familiar to most: it is very similar to navigating a directory hierarchy; in our case, NeXus groups are the directories, which contain data sets and/or other directories.
The core API comprises the following functional groups:
General Initialization and Shutdown | |
---|---|
NXopen | Opens NeXus file and returns file id |
NXclose | Closes NeXus file |
NXmakegroup | Creates NeXus group |
NXopengroup | Opens existing NeXus group for input/output |
NXclosegroup | Closes NeXus group |
NXmakedata | Creates NeXus data set |
NXcompmakedata | Creates a compressed NeXus data set |
NXopendata | Opens existing NeXus data set for input/output |
NXcompress | Marks the NeXus data set for compression |
NXclosedata | Closes NeXus data set |
Reading and Writing | |
NXgetdata | Reads data from currently open data set |
NXgetslab | Reads a subset of the currently open data set |
NXgetattr | Reads sdata attribute from open data set |
NXputdata | Writes data into the currently open data set |
NXputslab | Writes a subset of data into the currently open data set |
NXputattr | Writes an attribute of the currently open data set |
NXflush | Flushes output to a data file |
Meta-Data Routines | |
NXgetinfo | Gets rank, dimensions and type of currently open data set |
NXgetgroupinfo | Returns the number of items, and the name and class of the current group |
NXinitgroupdir | Initializes a group's directory search |
NXgetnextentry | Implements a directory search of the currently open group |
NXgetattrinfo | Returns the number of attributes of the current data set |
NXinitattrdir | Initializes a data set's attribute search |
NXgetnextattr | Implements a search of all the attributes of the currently open data set |
Linking and Group Hierarchy | |
NXgetgroupID | Returns the identifier of the currently open group as an NXlink structure |
NXgetdataID | Returns the identifier of the currently open data set |
NXmakelink | Links a data item (group or set) to another group |
Although the NeXus API is considerably simpler than the underlying HDF code, writing NeXus files still involves much repetitive code. Mark Koennecke has therefore proposed a Data Dictionary Language to reduce the amount of programming.
Wrapper routines to interface the Fortran and C code have been developed by Freddie Akeroyd. The routines have the same names and argument lists as the corresponding C routines, although we have added extra routines for the input/output of character data and attributes. Care must be taken to ensure enough space is allocated for the input/output operations being performed.
It is necessary to reverse the order of indices in multidimensional arrays, compared to an equivalent C program, so that data are stored in the same order in the NeXus file.
Any program using the F77 API needs to include the following line near the top in order to define the required constants (NXHANDLESIZE, NXLINKSIZE, etc.) :
include 'NAPIF.INC'
General Initialization and Shutdown | |
---|---|
NXopen | Opens NeXus file and returns file id |
NXclose | Closes NeXus file |
NXmakegroup | Creates NeXus group |
NXopengroup | Opens existing NeXus group for input/output |
NXclosegroup | Closes NeXus group |
NXmakedata | Creates NeXus data set |
NXcompmakedata | Creates a compressed NeXus data set |
NXopendata | Opens existing NeXus data set for input/output |
NXcompress | Marks the NeXus data set for compression |
NXclosedata | Closes NeXus data set |
Reading and Writing | |
NXgetdata | Reads numeric data from currently open data set |
NXgetchardata | Reads character data from currently open data set |
NXgetslab | Reads a subset of the currently open data set |
NXgetattr | Reads a numeric attribute from the open data set |
NXgetcharattr | Reads a character attribute from the open data set |
NXputdata | Writes numeric data into the currently open data set |
NXputchardata | Writes character data into the currently open data set |
NXputslab | Writes a subset of data into the currently open data set |
NXputattr | Writes a numeric attribute of the currently open data set |
NXputcharattr | Writes a character attribute of the currently open data set |
NXflush | Flushes output to a data file |
Meta-Data Routines | |
NXgetinfo | Gets rank, dimensions and type of currently open data set |
NXgetgroupinfo | Returns the number of items, and the name and class of the current group |
NXinitgroupdir | Initializes a group's directory search |
NXgetnextentry | Implements a directory search of the currently open group |
NXgetattrinfo | Returns the number of attributes of the current data set |
NXinitattrdir | Initializes a data set's attribute search |
NXgetnextattr | Implements a search of all the attributes of the currently open data set |
Linking and Group Hierarchy | |
NXgetgroupID | Returns the identifier of the currently open group as an NXlink structure |
NXgetdataID | Returns the identifier of the currently open data set |
NXmakelink | Links a data item (group or set) to another group |
Here is a comparison of the types used in the C and F77 interface.
C | Fortran |
---|---|
int a, int* a | INTEGER A |
char* a | CHARACTER*(*) A |
NXhandle a, NXhandle* a | INTEGER A(NXHANDLESIZE) |
NXstatus | INTEGER |
int[] a | INTEGER A(*) |
void* a | REAL A(*) or DOUBLE A(*) or INTEGER A(*) |
NXlink a, NXlink* a | INTEGER A(NXLINKSIZE) |
The Fortran 90 interface is a wrapper to the C interface with nearly identical routine definitions. As with the Fortran 77 interface, it is necessary to reverse the order of indices in multidimensional arrays, compared to an equivalent C program, so that data are stored in the same order in the NeXus file.
Furthermore, we have developed a set of utility functions which are designed to make the reading and writing of NeXus data files even easier to code. When the manpower is available, equivalent functions will be ported back to C.
Any program using the F90 API needs to put the following line at the top (after the PROGRAM statement) :
use NXmodule
General Initialization and Shutdown | |
---|---|
NXopen | Opens NeXus file and returns file id |
NXclose | Closes NeXus file |
NXmakegroup | Creates NeXus group |
NXopengroup | Opens existing NeXus group for input/output |
NXclosegroup | Closes NeXus group |
NXmakedata | Creates NeXus data set |
NXopendata | Opens existing NeXus data set for input/output |
NXcompress | Marks the NeXus data set for compression |
NXclosedata | Closes NeXus data set |
Reading and Writing | |
NXgetdata | Reads data from currently open data set |
NXgetslab | Reads a subset of the currently open data set |
NXgetattr | Reads sdata attribute from open data set |
NXputdata | Writes data into the currently open data set |
NXputslab | Writes a subset of data into the currently open data set |
NXputattr | Writes an attribute of the currently open data set |
NXflush | Flushes output to a data file |
Meta-Data Routines | |
NXgetinfo | Gets rank, dimensions and type of currently open data set |
NXgetgroupinfo | Returns the number of items, and the name and class of the current group |
NXinitgroupdir | Initializes directory searches of the current group |
NXgroupdir | Returns a list of items in the current group |
NXgetnextentry | Implements a directory search of the currently open group |
NXgetattrinfo | Returns the number of attributes of the current data set |
NXinitattrdir | Initializes attribute searches of the current data set |
NXattrdir | Returns a list of attributes of the current data set |
NXgetnextattr | Implements a search of all the attributes of the currently open data set |
Linking and Group Hierarchy | |
NXgetgroupID | Returns the identifier of the currently open group as an NXlink structure |
NXgetdataID | Returns the identifier of the currently open data set |
NXmakelink | Links a data item (group or set) to another group |
The current version has been tested with Digital Fortran 90 on Alpha/VMS, Digital Visual Fortran on Windows NT and Absoft Pro Fortran on Linux. Please check the README.FORTRAN90 file for a discussion of compatibility issues and send details of any problems you encounter to <ROsborn@anl.gov>.
As an example of using the F90 API, NXdump.f90 prints a list of all the groups and data items stored in a NeXus file. A more powerful NeXus browser is described in the next section.
The NeXus F90 Utility API provides a number of routines that combine the operations of various core API routines in order to simplify the reading and writing of NeXus files. At present, they are only available (in alpha version) as a Fortran 90 module.
The utility API comprises the following functional groups:
Any program using the F90 Utility API needs to put the following line near the top of the program (N.B. do not put USE statements for both NXmodule and NXUmodule; the former is included in the latter) :
use NXUmodule
Reading and Writing | |
---|---|
NXUwriteglobals | Writes all the valid global attributes of a file. |
NXUwritegroup | Opens a group (creating it if necessary). |
NXUwritedata | Opens a data item (creating it if necessary) and writes data and its units. |
NXUreaddata | Opens and reads a data item and its units. |
NXUwritehistogram | Opens one dimensional data item (creating it if necessary) and writes histogram centers and their units. |
NXUreadhistogram | Opens and reads a one dimensional data item and converts it to histogram bin boundaries. |
NXUsetcompress | Defines the compression algorithm and minimum dataset size for subsequent write operations. |
Finding Groups, Data, and Attributes | |
NXUfindgroup | Checks whether a group of the specified name is contained within the currently open group. |
NXUfindclass | Returns the name of a group of the specified class if it is contained within the currently open group. |
NXUfinddata | Checks whether a data item of the specified name is contained within the currently open group. |
NXUfindattr | Checks whether the currently open data item has the specified attribute. |
NXUfindsignal | Searches the currently open group for a data item with the specified SIGNAL attribute. |
NXUfindaxis | Searches the currently open group for a data item with the specified AXIS attribute. |
Finding Linked Groups | |
NXUfindlink | Finds another link to the specified NeXus data item and opens the group it is in. |
NXUresumelink | Reopens the original group from which NXUfindlink was used. |
Currently, the F90 utility API will only write character strings, 4-byte integers and reals, and 8-byte reals. It can read other integer sizes into four-byte integers, but does not differentiate between signed and unsigned integers.
Here are two example programs which make heavy use of the Utility API.
The following code reads a two-dimensional set 'counts' with dimension scales of 't' and 'phi' using local routines, and then writes a NeXus file containing a single NXentry group and a single NXdata group. This is the simplest data file that conforms to the NeXus standard.
#include "napi.h" int main() { int counts[50][1000], n_t, n_p, dims[2], i; float t[1000], phi[50]; NXhandle file_id; /* Read in data using local routines */ getdata (n_t, t, n_p, phi, counts); /* Open output file and output global attributes */ NXopen ('NXfile.nxs', NXACC_CREATE, &file_id); NXputattr (file_id, "user_name", "Joe Bloggs", 10, NX_CHAR); /* Open top-level NXentry group */ NXmakegroup (file_id, "Entry1", "NXentry"); NXopengroup (file_id, "Entry1", "NXentry"); /* Open NXdata group within NXentry group */ NXmakegroup (file_id, "Data1", "NXdata"); NXopengroup (file_id, "Data1", "NXdata"); /* Output time channels */ NXmakedata (file_id, "time_of_flight", NX_FLOAT32, 1, &n_t); NXopendata (file_id, "time_of_flight") NXputdata (file_id, t); NXputattr (file_id, "units", "microseconds", 12, NX_CHAR); i = 1; NXputattr (file_id, "axis", &i, 1, NX_INT32); NXclosedata (file_id); /* Output detector angles */ NXmakedata (file_id, "phi", NX_FLOAT32, 1, &n_p); NXopendata (file_id, "phi") NXputdata (file_id, phi); NXputattr (file_id, "units", "degrees", 7, NX_CHAR); i = 2; NXputattr (file_id, "axis", &i, 1, NX_INT32); NXclosedata (file_id); /* Output data */ dims[0] = n_t; dims[1] = n_p; NXmakedata (file_id, "counts", NX_INT32, 2, dims); NXopendata (file_id, "counts") NXputdata (file_id, counts); i = 1; NXputattr (file_id, "signal", &i, 1, NX_INT32); NXclosedata (file_id); /* Close NXentry and NXdata groups and close file */ NXclosegroup (file_id); NXclosegroup (file_id); NXclose (&file_id); return; }
program WRITEDATA include 'NAPIF.INC' integer*4 status, file_id(NXHANDLESIZE), counts(1000,50), n_p, n_t, dims(2) real*4 t(1000), phi(50) !Read in data using local routines call getdata (n_t, t, n_p, phi, counts) !Open output file status = NXopen ('NXFILE.NXS', NXACC_CREATE, file_id) status = NXputcharattr + (file_id, 'user', 'Joe Bloggs', 10, NX_CHAR) !Open top-level NXentry group status = NXmakegroup (file_id, 'Entry1', 'NXentry') status = NXopengroup (file_id, 'Entry1', 'NXentry') !Open NXdata group within NXentry group status = NXmakegroup (file_id, 'Data1', 'NXdata') status = NXopengroup (file_id, 'Data1', 'NXdata') !Output time channels status = NXmakedata + (file_id, 'time_of_flight', NX_FLOAT32, 1, n_t) status = NXopendata (file_id, 'time_of_flight') status = NXputdata (file_id, t) status = NXputcharattr + (file_id, 'units', 'microseconds', 12, NX_CHAR) status = NXputattr (file_id, 'axis', 1, 1, NX_INT32) status = NXclosedata (file_id) !Output detector angles status = NXmakedata (file_id, 'phi', NX_FLOAT32, 1, n_p) status = NXopendata (file_id, 'phi') status = NXputdata (file_id, phi) status = NXputcharattr (file_id, 'units', 'degrees', 7, NX_CHAR) status = NXputattr (file_id, 'axis', 2, 1, NX_INT32) status = NXclosedata (file_id) !Output data dims(1) = n_t dims(2) = n_p status = NXmakedata (file_id, 'counts', NX_INT32, 2, dims) status = NXopendata (file_id, 'counts') status = NXputdata (file_id, counts) status = NXputattr (file_id, 'signal', 1, 1, NX_INT32) status = NXclosedata (file_id) !Close NXdata and NXentry groups and close file status = NXclosegroup (file_id) status = NXclosegroup (file_id) status = NXclose (file_id) stop end
program WRITEDATA use NXUmodule type(NXhandle) :: file_id integer, pointer :: counts(:,:) real, pointer :: t(:), phi(:) !Use local routines to allocate pointers and fill in data call getlocaldata (t, phi, counts) !Open output file if (NXopen ("NXfile.nxs", NXACC_CREATE, file_id) /= NX_OK) stop if (NXUwriteglobals (file_id, user="Joe Bloggs") /= NX_OK) stop !Set compression parameters if (NXUsetcompress (file_id, NX_COMP_LZW, 1000) /= NX_OK) stop !Open top-level NXentry group if (NXUwritegroup (file_id, "Entry1", "NXentry") /= NX_OK) stop !Open NXdata group within NXentry group if (NXUwritegroup (file_id, "Data1", "NXdata") /= NX_OK) stop !Output time channels if (NXUwritedata (file_id, "time_of_flight", t, "microseconds") /= NX_OK) stop if (NXputattr (file_id, "axis", 1) /= NX_OK) stop !Output detector angles if (NXUwritedata (file_id, "phi", phi, "degrees") /= NX_OK) stop if (NXputattr (file_id, "axis", 2) /= NX_OK) stop !Output data if (NXUwritedata (file_id, "counts", counts, "counts") /= NX_OK) stop if (NXputattr (file_id, "signal", 1) /= NX_OK) stop !Close NXdata group if (NXclosegroup (file_id) /= NX_OK) stop !Close NXentry group if (NXclosegroup (file_id) /= NX_OK) stop !Close NeXus file if (NXclose (file_id) /= NX_OK) stop end program WRITEDATA
NeXus for Java provides access to NeXus data files for programs written in Java. This API was implemented by Java code calling the original C language NeXus API through the Java Native Methods Interface.
IDL is an interactive data evaluation environment developed by Research Systems. It is an interpreted language for data manipulation and visualization. Part of IDL is an HDF-interface. In order to facilitate the import of NeXus files into this popular data manipulation package, the NeXus-API was reimplemented in the IDL language by Mark Koennecke. The package may be downloaded as a tar file from <ftp://ftp.neutron.anl.gov/nexus/nidl.tar>.
Since NeXus uses HDF as the underlying binary format, it is necessary first to install the HDF subroutine libraries and include files before installing the NeXus API. It is not usually necessary to download the HDF source code since precompiled object libraries exist for a variety of operating systems including VMS, Macintosh, Windows and various flavors of Unix. Check the following URL for more information:
The current version of the NeXus API is 2.0.0, which is the first version to read and write both HDF4 and HDF5 files, provided both sets of libraries are installed. It was written by Mark Koennecke, Przemek Klosowski, Freddie Akeroyd and Ray Osborn, and is available for downloading under the terms of the GNU Lesser General Public License.
Comments to: Ray Osborn <ROsborn@anl.gov>
Revised: Saturday, September 14, 2002
Copyright © 1996-2002 NeXus Design Team. All rights reserved.