diff --git a/base_classes/NXdata.nxdl.xml b/base_classes/NXdata.nxdl.xml index 1de87136a..136307c00 100644 --- a/base_classes/NXdata.nxdl.xml +++ b/base_classes/NXdata.nxdl.xml @@ -41,9 +41,9 @@ --> - These symbols will be used below to coordinate datasets with the same shape. - rank of the ``data`` field - length of the ``variable`` field + These symbols will be used below to coordinate fields with the same shape. + rank of the ``DATA`` field + length of the ``VARIABLE`` field length of the ``x`` field length of the ``y`` field length of the ``z`` field @@ -64,15 +64,17 @@ + .. index:: find the default plottable data .. index:: plotting + .. index:: signal attribute value - Declares which dataset is the default. - The value is the name of the dataset to be plotted. - A field of this name *must* exist (either as dataset - or as a link to a dataset). + Declares which NeXus field is the default. + The value is the name of the NeXus field to be plotted. + (The value :ref:`names <validItemName>` an existing child + of this group. The child group must itself be a NeXus group.) It is recommended (as of NIAC2014) to use this attribute - rather than adding a signal attribute to the dataset. + rather than adding a signal attribute to the field. See https://www.nexusformat.org/2014_How_to_find_default_data.html for a summary of the discussion. @@ -124,7 +126,7 @@ Integer array that defines the indices of the *signal* field (that field will be a multidimensional array) - which need to be used in the *AXISNAME* dataset in + which need to be used in the *AXISNAME* field in order to reference the corresponding axis value. The first index of an array is ``0`` (zero). @@ -162,10 +164,11 @@ .. index:: plotting - It is mandatory that there is at least one :ref:`NXdata` group - in each :ref:`NXentry` group. - Note that the ``variable`` and ``data`` + It is strongly recommended that there is at least one :ref:`NXdata` + group in each :ref:`NXentry` group. + Note that the fields named ``VARIABLE`` and ``DATA`` can be defined with different names. + (Upper case is used to indicate that the actual name is left to the user.) The ``signal`` and ``axes`` attributes of the ``data`` group define which items are plottable data and which are *dimension scales*, respectively. @@ -174,12 +177,14 @@ to provide a default plot for the data of this :ref:`NXentry`. The actual data might be stored in another group and (hard) linked to the :ref:`NXdata` group. - * Each :ref:`NXdata` group will define only one data set - containing plottable data, dimension scales, and - possibly associated standard deviations. - Other data sets may be present in the group. + * Each :ref:`NXdata` group will define one field as the default + plottable data. The value of the ``signal`` attribute names this field. + Additional fields may be used to describe the dimension scales and + uncertainities. + The ``auxiliary_signals`` attribute is a list of the other fields + to be plotted with the ``signal`` data. * The plottable data may be of arbitrary rank up to a maximum - of ``NX_MAXRANK=32``. + of ``NX_MAXRANK=32`` (for compatibility with backend file formats). * The plottable data will be named as the value of the group ``signal`` attribute, such as:: @@ -191,7 +196,7 @@ mr: float[100] --> the default independent data The field named in the ``signal`` attribute **must** exist, either - directly as a dataset or defined through a link. + directly as a NeXus field or defined through a link. * The group ``axes`` attribute will name the *dimension scale* associated with the plottable data. @@ -308,14 +313,14 @@ This pattern of using ``VARIABLE_errors`` can be used throughout a NeXus data file to associate uncertainties - with a dataset named ``VARIABLE``. This pattern also + with a field named ``VARIABLE``. This pattern also applies to other relationships such as ``VARIABLE_resolutions`` - to connect additional data with a certain dataset. + to connect additional data with a certain field. A dimension scale must have a rank of 1 and has length ``n``, - same as ``variable``. + same as ``VARIABLE``. diff --git a/base_classes/NXentry.nxdl.xml b/base_classes/NXentry.nxdl.xml index d548e5f8e..44f172453 100644 --- a/base_classes/NXentry.nxdl.xml +++ b/base_classes/NXentry.nxdl.xml @@ -2,9 +2,9 @@ - + .. index:: find the default plottable data .. index:: plotting + .. index:: default attribute value Declares which :ref:`NXdata` group contains the data to be shown by default. - It is needed to resolve ambiguity when more than + It is used to resolve ambiguity when one :ref:`NXdata` group exists. - The value is the name of the default :ref:`NXdata` group. + The value :ref:`names <validItemName>` a child group. If that group + itself has a ``default`` attribute, continue this chain until an + :ref:`NXdata` group is reached. For more information about how NeXus identifies the default plottable data, see the @@ -46,41 +50,41 @@ section. - + - (**required**) :ref:`NXentry` describes the measurement. - + (**required**) :ref:`NXentry` describes the measurement. + The top-level NeXus group which contains all the data and associated - information that comprise a single measurement. + information that comprise a single measurement. It is mandatory that there is at least one group of this type in the NeXus file. - + The data group - + .. note:: Before the NIAC2016 meeting [#]_, at least one :ref:`NXdata` group was required in each :ref:`NXentry` group. - At the NIAC2016 meeting, it was decided to make :ref:`NXdata` - an optional group in :ref:`NXentry` groups for data files that + At the NIAC2016 meeting, it was decided to make :ref:`NXdata` + an optional group in :ref:`NXentry` groups for data files that do not use an application definition. - It is recommended strongly that all NeXus data files provide - a NXdata group. - It is permissable to omit the NXdata group only when + It is recommended strongly that all NeXus data files provide + a NXdata group. + It is permissable to omit the NXdata group only when defining the default plot is not practical or possible from the available data. - - For example, neutron event data may not have anything that + + For example, neutron event data may not have anything that makes a useful plot without extensive processing. - + Certain application definitions override this decision and require an :ref:`NXdata` group in the :ref:`NXentry` group. The ``minOccurs=0`` attribute - in the application definition will indicate the + in the application definition will indicate the :ref:`NXdata` group is optional, otherwise, it is required. - - .. [#] NIAC2016: + + .. [#] NIAC2016: https://www.nexusformat.org/NIAC2016.html, https://github.com/nexusformat/NIAC/issues/16 @@ -96,8 +100,8 @@ - Unique identifier for the experiment, - defined by the facility, + Unique identifier for the experiment, + defined by the facility, possibly linked to the proposals @@ -130,13 +134,13 @@ (alternate use: see same field in :ref:`NXsubentry` for preferred) - + Official NeXus NXDL schema to which this entry conforms. - + This field is provided so that :ref:`NXentry` can be the overlay position - in a NeXus data file for an application definition and its + in a NeXus data file for an application definition and its set of groups, fields, and attributes. - + *It is advised* to use :ref:`NXsubentry`, instead, as the overlay position. NXDL version number @@ -145,7 +149,7 @@ Local NXDL schema extended from the entry - specified in the ``definition`` field. + specified in the ``definition`` field. This contains any locally-defined, additional fields in the entry. @@ -202,7 +206,7 @@ The mime type should be an ``image/*`` - diff --git a/base_classes/NXroot.nxdl.xml b/base_classes/NXroot.nxdl.xml index 04e10823d..54c432c79 100644 --- a/base_classes/NXroot.nxdl.xml +++ b/base_classes/NXroot.nxdl.xml @@ -2,9 +2,9 @@ /entry/instrument/detector/image - instrument:NXinstrument - @NX_class = NXinstrument - detector:NXdetector - @NX_class = NXdetector - image:NX_UINT8[40,30] = __array + @H5PY_VERSION = "3.6.0" + @creator = "write_nexus_file()" + @default = "entry" + @file_time = "2022-03-07 14:34:04.418733" + @filename = "example.h5" + entry:NXentry + @NX_class = "NXentry" + @default = "data" + data:NXdata + @NX_class = "NXdata" + @signal = "data" + data --> /entry/instrument/detector/image + instrument:NXinstrument + @NX_class = "NXinstrument" + detector:NXdetector + @NX_class = "NXdetector" + image:NX_UINT8[1024,1024] = __array __array = [ - [48, 80, 112, '...', 208] - [240, 16, 48, '...', 176] - [208, 240, 16, '...', 144] - ... - [16, 48, 80, '...', 176] - ] - @units = counts - @target = /entry/instrument/detector/image - metadata:NXcollection - @NX_class = NXcollection - bitcoin_value:NX_CHAR = 15000 - detector_state:NX_INT64[] = - size_x:NX_INT64[] = - size_y:NX_INT64[] = - unique_id:NX_INT64[] = + [76, 77, 78, '...', 75] + [77, 78, 79, '...', 76] + [78, 79, 80, '...', 77] + ... + [75, 76, 77, '...', 74] + ] + @target = "/entry/instrument/detector/image" + @units = "counts" + metadata:NXcollection + @NX_class = "NXcollection" + bitcoin_value:NX_CHAR = b'15000' + detector_state:NX_INT64[] = + size_x:NX_INT64[] = + size_y:NX_INT64[] = + unique_id:NX_INT64[] = diff --git a/manual/source/examples/epics/write_nexus_file.py b/manual/source/examples/epics/write_nexus_file.py index 8f25f35db..3b28427ab 100755 --- a/manual/source/examples/epics/write_nexus_file.py +++ b/manual/source/examples/epics/write_nexus_file.py @@ -1,82 +1,82 @@ -import numpy as np -import h5py -import datetime - -def write_nexus_file(fname, image, md={}): - """ - write the image to a NeXus HDF5 data file - - Parameters - ---------- - fname : str - name of the file (relative or absolute) to be written - image : numpy array - the image data - md : dictionary - key: value where value is something that can be written by h5py - (such as str, int, float, numpy array, ...) - """ - nexus = h5py.File(fname, "w") - nexus.attrs["filename"] = fname - nexus.attrs["file_time"] = str(datetime.datetime.now()) - nexus.attrs["creator"] = "write_nexus_file()" - nexus.attrs["H5PY_VERSION"] = h5py.__version__ - - # /entry - nxentry = nexus.create_group("entry") - nxentry.attrs["NX_class"] = "NXentry" - nexus.attrs["default"] = nxentry.name - - # /entry/instrument - nxinstrument = nxentry.create_group("instrument") - nxinstrument.attrs["NX_class"] = "NXinstrument" - - # /entry/instrument/detector - nxdetector = nxinstrument.create_group("detector") - nxdetector.attrs["NX_class"] = "NXdetector" - - # /entry/instrument/detector/image - ds = nxdetector.create_dataset("image", data=image, compression="gzip") - ds.attrs["units"] = "counts" - ds.attrs["target"] = "/entry/instrument/detector/image" - - # /entry/data - nxdata = nxentry.create_group("data") - nxdata.attrs["NX_class"] = "NXdata" - nxentry.attrs["default"] = nxdata.name - - # /entry/data/data --> /entry/instrument/detector/image - nxdata["data"] = nexus["/entry/instrument/detector/image"] - nxdata.attrs["signal"] = "data" - - if len(md) > 0: - # /entry/instrument/metadata (optional, for metadata) - metadata = nxinstrument.create_group("metadata") - metadata.attrs["NX_class"] = "NXcollection" - for k, v in md.items(): - try: - metadata.create_dataset(k, data=v) - except Exception: - metadata.create_dataset(k, data=str(v)) - - nexus.close() - - -if __name__ == "__main__": - """demonstrate how to use this code""" - import epics - prefix = "13SIM1:" - img = epics.caget(prefix+"image1:ArrayData") - size_x = epics.caget(prefix+"cam1:ArraySizeX_RBV") - size_y = epics.caget(prefix+"cam1:ArraySizeY_RBV") - # edit the full image for just the binned data - img = img[:size_x*size_y].reshape((size_x, size_y)) - - extra_information = dict( - unique_id = epics.caget(prefix+"image1:UniqueId_RBV"), - size_x = size_x, - size_y = size_y, - detector_state = epics.caget(prefix+"cam1:DetectorState_RBV"), - bitcoin_value="15000", - ) - write_nexus_file("example.h5", img, md=extra_information) +import numpy as np +import h5py +import datetime + +def write_nexus_file(fname, image, md={}): + """ + write the image to a NeXus HDF5 data file + + Parameters + ---------- + fname : str + name of the file (relative or absolute) to be written + image : numpy array + the image data + md : dictionary + key: value where value is something that can be written by h5py + (such as str, int, float, numpy array, ...) + """ + nexus = h5py.File(fname, "w") + nexus.attrs["filename"] = fname + nexus.attrs["file_time"] = datetime.datetime.now().astimezone().isoformat() + nexus.attrs["creator"] = "write_nexus_file()" + nexus.attrs["H5PY_VERSION"] = h5py.__version__ + + # /entry + nxentry = nexus.create_group("entry") + nxentry.attrs["NX_class"] = "NXentry" + nexus.attrs["default"] = nxentry.name + + # /entry/instrument + nxinstrument = nxentry.create_group("instrument") + nxinstrument.attrs["NX_class"] = "NXinstrument" + + # /entry/instrument/detector + nxdetector = nxinstrument.create_group("detector") + nxdetector.attrs["NX_class"] = "NXdetector" + + # /entry/instrument/detector/image + ds = nxdetector.create_dataset("image", data=image, compression="gzip") + ds.attrs["units"] = "counts" + ds.attrs["target"] = "/entry/instrument/detector/image" + + # /entry/data + nxdata = nxentry.create_group("data") + nxdata.attrs["NX_class"] = "NXdata" + nxentry.attrs["default"] = nxdata.name + + # /entry/data/data --> /entry/instrument/detector/image + nxdata["data"] = nexus["/entry/instrument/detector/image"] + nxdata.attrs["signal"] = "data" + + if len(md) > 0: + # /entry/instrument/metadata (optional, for metadata) + metadata = nxinstrument.create_group("metadata") + metadata.attrs["NX_class"] = "NXcollection" + for k, v in md.items(): + try: + metadata.create_dataset(k, data=v) + except Exception: + metadata.create_dataset(k, data=str(v)) + + nexus.close() + + +if __name__ == "__main__": + """demonstrate how to use this code""" + import epics + prefix = "13SIM1:" + img = epics.caget(prefix+"image1:ArrayData") + size_x = epics.caget(prefix+"cam1:ArraySizeX_RBV") + size_y = epics.caget(prefix+"cam1:ArraySizeY_RBV") + # edit the full image for just the binned data + img = img[:size_x*size_y].reshape((size_x, size_y)) + + extra_information = dict( + unique_id = epics.caget(prefix+"image1:UniqueId_RBV"), + size_x = size_x, + size_y = size_y, + detector_state = epics.caget(prefix+"cam1:DetectorState_RBV"), + bitcoin_value="15000", + ) + write_nexus_file("example.h5", img, md=extra_information)