16.3. Writing Diamond Default NeXus File Structure

16.3.1. Introduction

In order to improve how we write NeXus files at Diamond, the Diamond Default NeXus File Structure has been proposed. This outlines a minimum NeXus file structure with particular NeXus groups and datasets that must be present in order to comply.

The diagram below details the minimum structure required to comply with Diamond Default Nexus Structure.

entry:NXentry
	start_time: NX_DATE_TIME
	end_time: NX_DATE_TIME
	program_name: NX_CHAR
	instrument:NXinstrument
		source:NXsource
		id:NXinsertion_device
		magnet:NXbending_magnet
		monochromator:NXmonochromator
	sample:NXsample
		beam:NXbeam
	user01:NXuser  # further users if required
	appDef1:NXsubentry  # further subentries for application definitions if required

Note that names of the NeXus group here are suggestions, the important thing is that your nexus file must include groups of the required types in order to comply with the Diamond Default NeXus File Structure

16.3.2. How to write Diamond Default NeXus File Structure

16.3.2.1. CommonBeamlineDeviceConfiguration bean

To make it easier to write Diamond Default NeXus File Structure a bean CommonBeamlineDeviceConfiguration was added. This has a field for each kind of device that should be included in the NeXus file in order to comply with this structure.

In order to use this bean, it is necessary to use new nexus writing. This will always be the case with new scanning, but with old scanning you should set the property `gda.data.scan.datawriter.dataFormat=NexusScanDataWriter’. The class NexusScanDataWriter adapts new nexus writing to work with old scanning. Note that the ‘scan’ command uses old scanning, whereas ‘mscan’ uses new scanning.

Although it is possible to write Diamond Default NeXus File Structure without using this bean, i.e. by using the location map (old scanning) or by simply writing your own INexusDevices (new scanning), using this bean and the metadata classes that have been defined to be used with it make the task much easier.

The fields start_time, end_time and program_name of the NXentry group will be written by the nexus writing framework itself - this is the case for both old and new nexus writing.

The bean CommonBeamlineDeviceConfiguration defines the following fields, for specifying the names of the appropriate INexusDevice:

Field Name

NeXus Class

Description

sourceName

NXsource

A description of the facility, e.g. Diamond

insertionDeviceName

NXinsertion_device

The insertion device (if present)

bendingMagnetName

NXbending_magnet

The bending magnet (if used instead of an insertion device)

monochromatorName

NXmonochromator

The monochromator, includes the current energy

beamName

NXbeam

A description of the beam at the sample

userDeviceName

NXuser

Information about the user(s)

additionalDeviceNames

any - multivalued

The names of any additional INexusDevices to add

The field additionalDeviceNames is a set of string, and can be used to include the names of additional metadata devices in the scan. A device name specified in this bean can be the name of a scannable, or of an INexusDevice registered with the INexusDeviceSevice. (this can be done in spring by specifying init-method="register" on the bean definition. A scannable will be adapted to an INexusDevice by the class ScannableNexusDevice by default. These nexus devices will be added to the scan as per-scan monitors. This means that their nexus objects will be added to the nexus file, but they will not be able asked to write data for each point of the scan.

16.3.2.2. Nexus Metadata Device classes

Any class that implements INexusDevice can be specified in the spring configuration for the CommonBeamlineDevicesConfiguration bean. For example, a NexusTemplateDevice could be used, which write a nexus object according to a template, or you could write your own class in either Java or Python.

However, for each type of metadata device class specified by the Default Diamond NeXus File Structure, a new class has been created
in order to make it easier to write a nexus object of the expected type. These classes create a nexus object of the expected type, e.g. NXmonochromator and set the values of specific fields of the nexus object according to either fixed scalar values or the value (i.e. position) of a scannable name that has been set in the nexus device object. Whether a particular field is set according to a scannable name or fixed scalar value depends on the field.

For example MonochromatorNexusDevice has a field energyScannableName. When this is set in spring to the name of the energy scannable for this beamline, then the energy field of the NXmonochromator object will be written with the value (position) of the energy scannable in the nexus file. An example of a field that has a scalar value is the type of an insertion device. InsertionDeviceNexusDevice has a field called type, that should be set in spring to the type (a string) of insertion device - valid values in the case are UNDULATOR or WIGGLER.

One of the these new types of nexus device, UserNexusDevice, works a little differently than the others. It creates an NXuser group containing the current user name and id. The values for these are not set via spring configuration but instead are retrieved from the baton holder. Note that the name property of the UserNexusDevice must still be set as this is used to look up the UserNexusDevice, as with other devices included in the scan. The userGroupName property controls the name the NXuser group is given within the parent group. If not set, the default value user01 is used.

The table below summarises the available NexusDevice classes (more may be added later).

Nexus Device Class

Nexus Class

Description

SourceNexusDevice

NXsource

A description of the facility, e.g. Diamond

InsertionDeviceNexusDevice

NXinsertion_device

The insertion device

BendingMagnetNexusDevice

NXbending_magnet

The bending magnet (if used instead of an insertion device)

MonochromatorNexusDevice

NXmonochromator

The monochromator, includes the current energy

BeamNexusDevice

NXbeam

A description of the beam at the sample

UserNexusDevice

NXuser

Information about the user(s)

SlitNexusDevice (TBC)

NXslit

Describes a slit

MirrorNexusDevice (TBC)

NXmirror

Describes a mirror

16.3.2.3. Metadata Fields and Node Types

As well as being able to define a scalar value or a scannable name for particular fields, it is also possible to create a field with any name. This is done by setting the customNodes property in the spring configuration or by calling setCustomNodes. This method takes a list of MetadataNode. This is an interface with number of implementations for each way a field can be written.

Each field has a name, with other properties depending on the implementation. For example the class ScalarField is used for a field with a scalar value (i.e. a value directly specified in the spring configuration), ScannableField for a field whose value is set to the position of a scannable. The class LinkedField can be used to link to another location in the nexus file, this takes a path relative to the entry. The class GroupMetadataNode can even be used to create a child group with its own metadata fields defined in the same way. For example this could be used to create an NXcrystal group within the NXmonochromator group. The class ProcesssingVariableField writes a field whose value is set to the value of a PV (this class has not merged at time of writing). The table below summarizes these type of field.

Class Name

Property to set

Description

ScalarField

value

Sets field value to a hard-coded scalar value, e.g. 2.5

ScannableField

scannableName

Sets field value to the value (position) of a scannable, e.g. energy

LinkedField

linkPath

Adds a link to another location in the nexus file, e.g. /entry/dev/data

ProcessingVariableField

pvName

Sets field value to that of a Processing Variable (PV)

GroupMetadataNode

childNodes Creates a child node which itself can contain `MetadataNode`s

The new nexus device classes all extend from AbstractNexusMetadataDevice. This is where the method setCustomNodes is defined. An instance of this class contains a single GroupMetadataNode which defines how the nexus object for this device should be written. This contains a Map from node name (a String), to MetadataNode. A MetadataNode can be a field (one of ScalarField, ScannableField, LinkedField, ProcessingVariableField), or a nested GroupMetadataNode.

Note that these custom fields on their own would be able to create the same fields that can be created by setting the properties for fields with a particular name. For example, InsertionDeviceNexusDevice has a field gapScannableName, i.e. there is a setter method setGapScannableName. Setting the customNodes property to a list which includes a ScannableField field with the name gap will achieve the same effect as setting the gapScannableName property via spring. The setter methods simply add the MetadataNode of the appropriate type to the Map of MetadataNodes in the root GroupMetadataNode. The purpose of the setter methods in the nexus device classes is to make it easier to configure the nexus devices in spring for common field names, thus encouraging the use of a common set of fields so that we write Nexus files with a similar structure across beamlines.

A class NexusMetadataDevice has been defined which simply implements AbstractNexusMetadataDevice with no additional behaviour (except that it has a setter method setChildNodes as a delegate method to setCustomNodes). This can be used as a generic INexusDevice implementation to set custom nodes only. When using this class is it important to set the nexusClass property to the Nexus class name for your desired device type, e.g. NXmirror. The property nexusCategory can also be set to the Nexus class of the desired parent group of the nexus object for this device. This should be one of NXentry, NXinstrument or NXsample, as these are nexus groups are initially added to the nexus tree before the nexus object from any devices in the scan are added.

16.3.2.4. Configuring the CommonBeamlineDeviceConfiguration bean

The snippet below is an example of how to define your CommonBeamlineDeviceConfiguration bean in your spring configuration:

We haven’t declared the devices themselves yet, so it is recommended to use the standard names as above. Note that an exception will be thrown if a nexus device cannot be found for a particular name. For that reason you may wish to only add the devices to this bean as you declare them (see below), or start with yet to be configured devices commented out. The devices above should be declared in order to write the groups required for Diamond Default Nexus File Structure.

16.3.2.5. Configuring the Nexus Metadata Devices

You can then declare the nexus devices themselves. Although you can use any nexus device, including ones you’ve written yourself, it is preferred to use the nexus metadata classes that have been created for each type of device. The example below is adapted from the spring configuration for i16:

The declaration init-method="register" causes the nexus device to be registered with the NexusDeviceService. The name property is required for all INexusDevices, as this is the property by which it is looked up from the NexusDeviceService. All other properties are optional (as shown in the Nexus Format documentation.

Any fields not present on Nexus metadata device class can be added by additional fields can be added to these predefined nexus devices as customNodes. The snippet below shows an example of this for the SourceNexusDevice, with a field for each of the types of MetadataNode:

16.3.2.6. Adding Custom Nexus Metadata Devices

The snippet below shows how to add additional devices

slit1 slit2 mirror1 mirror2

Again, these device can be of any class that implements INexusDevice, however the class NexusMetadataDevice may be particularly useful. An example of configuring this is shown below:

		   <!-- and any other fields you wish to add -->
     </list>
 </property>

Note: The class SlitNexusDevice will shortly be available as a more convenient way to contribute an NXslit group to your nexus file.