16.2. NeXus Template Engine¶
The idea is to define what will be written by a GDA scan (new and old) in a standard way. This is to then allow a template to be used to create links back into this standard structure which can work for old and new scanning. Another prime motivation for the template engine is to provide a mechanism to add NeXus application definitions to an existing NeXus file (or tree), however the potential uses are even more general.
The NeXus Template Engine is a framework for applying templates to either a
NeXus file or, an in-memory NeXus tree. The framework lives in the Eclipse
plug-in org.eclipse.dawnsci.nexus.templates. There is also a product
org.eclipse.dawnsci.nexus.templates.product which can be exported to create
standalone template application (see below).
Important note: The template format can add static data, but it cannot refer directly to devices so any dynamic data (i.e. the value of a scannable) must already be present in the NeXus file. The template provides a way to add links from new NeXus groups to the existing data.
16.2.1. Template format¶
YAML is used as the format for the template. An example of what you can do is below.
myGroup/:
NX_class@: NXentry
attribueOnMyGroup@: "something"
my_inline_static_data: 123.45
my_obj_static_data:
value: james
myAttribute@: hello
linkedAttr@: /link/to/attr@
my_obj_link: thing # NOTE: adding attributes to linked nodes is not yet
supported!
link: /target/help
james@: foo
my_inline_link: /must/start/with/slash
data*: # the asterisk denotes copying an NXdata group with substituted axis
names
nodePath: /path/to/data
axisSubstitutions:
stagex: x
stagey: y
The rules for the template are as follows:
Groups are defined by their name and a trailing slash. All groups must have an
NX_classattribute specifying the NeXus class;For static data you can define it inline or multiline which is required if you want to add attributes. If multiline is used, “value” must be specified which will contain the data to be written into the dataset;
For links they can be defined inline using a leading slash (also representing absolute) or multiline in which case “link” must be specified to point to the target. Multiline link - allowing the addition of attributes - will not be supported initially;
Where possible type coercion will be used to attempt ‘int’ then ‘double’ then ‘String’ for datasets and attributes;
Defining both a value and link is an error and it should fail.
A special syntax can be used to copy an
NXdatagroup with substituted axis names. This is done by added a trailing asterisk to the new group name. The mapping for the group must contain ‘nodePath’ specifying the path of theNXdatagroup to be copied, and ‘axisSubstitutions’, a map specifying the axis names to be substituted where the key is the axis name in theNXdatagroup to be copied, and the value is the new name.
16.2.2. Standalone Template Application¶
The Eclipse plug-in org.eclipse.dawnsci.nexus.templates defines an Eclipse
product, org.eclipse.dawnsci.nexus.templates.product. When exported this
produces a stand-alone application, that can apply a template to an existing
NeXus file. As of writing (2019-07-05) an early version of this is available at
/dls_sw/apps/templates. The usage is:
./apply-template <templateFile.yaml> <nexusFile.nxs>
E.g
./apply-template test-template.yaml p45-example.nxs
16.2.3. Usage in new scanning¶
During a scan the NeXus file is constructed and saved in memory, therefore the
NeXus templating system can also be applied to an ‘in-memory’ NeXus tree. The
template will be applied to the NeXus tree before it is saved to disk. For this
to happen it must be attached to the ScanModel, which is created from the
ScanRequest. A new field has been added to the ScanRequest/ScanModel
(called getTemplate()) meaning that the template will exist in the model and
can be applied to the NeXus tree. Just note that the ScanModel used within
the code is created as mentioned from the ScanRequest and is not for the
client code to modify.
Implementation
During the ScanProcess.execute() phase of new scanning (see new scanning use
case) the ScanModel is created
during ScanModel.prepareScan().
This calls
createScanModel()where theScanModelis created from theScanRequest:final ScanRequest<?> req = bean.getScanRequest();
And the template file is set:
scanModel.setTemplateFilePath(req.getTemplateFilePaths());
Following the preparation of the scan,
ScanProcess.execute()callsrunScan():This calls
createRunnableDevice()with the model as input, which subsequently callsconfigureDetectors()->device.configure().
One of the key devices in new scanning is the AcquistionDevice which
essentially runs the scan. So during the above step,
AcquisitionDevice.configure() method is called.
AcqusitionDevice.configure()sets up theNexusScanFileManagerand callscreateNexusFile()on the manager to create the NeXus file.The
createNexusFile()method callscreateEntry().createEntry()essentially begins to build the NeXus file creating the NeXus tree. It:adds the default groups to the NeXus file (see above) -
addDefaultGroups().adds the scan metadata attached in the
ScanRequest(and added from there to theScanModel)-addScanMetaData()).adds devices.
creates an
NXdataentry.
Next
createNexusFile()callsapplyTemplates()using the NeXus tree created above. This:gets the template file set in the
ScanModel.loads this as a
NexusTemplateImplobject (which implements theNexusTemplateinterface) from theNexusTemplateServiceImpl(which implements theNexusTemplateServiceinterface).applied the template to the supplied in-memory NeXus tree -
apply().
Finally, the file is created and opened ready to write to.
When apply() method is called on the NexusTemplate object, it carries out
the following:
It creates a new
InMemoryNexusContextobject which implements theNexusContextinterface.Calls
applyTemplate()with the above object:creates an
ApplyNexusTemplateTask- essentially just a task to apply a NeXus template to a NeXus tree.calls
run()on the above task. This then goes through each entry in the yaml mapping, gets each node type(GROUP,DATA,LINK,ATTRIBUTE)and adds the mapping accordingly.
Setting a template
Essentially the template file just need so be defined in the ScanRequest
which is submitted.
1. A template can be added to the ScanRequest directly if the ScanRequest
is created programmatically (for example in a Jython script).
2. Alternatively it can be added via the DefaultScanConfiguration. The
DefaultScanConfiguration object contains scan defaults that will be merged
with a ScanRequest on the server. This can be defined in the server Spring
configuration, e.g:
<bean id="default_scan" class="org.eclipse.scanning.server.servlet.DefaultScanConfiguration"> <property name="templateFilePaths"> <set> <value type="java.lang.String">/path/to/templates/template1.yaml</value> </set> </property> </bean>
After the scan is submitted, the
ScanServletwill begin withcreateProcess()using theScanBean. This subsequently callspreprocess()which checks if there are any ‘preprocessors’ and callspreprocess()on them. With the inclusion ofDefaultScanConfiguration, theDefaultScanPreprocessorwill be registered. Its job is to merge the monitors defined in itsDefaultScanConfigurationinto aScanRequest. TheDefaultScanPreprocessor.preprocess()call gets the template file path from theDefaultScanConfigurationand sets it in theScanRequest(ScanRequest.setTemplateFilePath()).
3. In the case of ‘mapping’ scans, templates can simply be defined and added in the UI. Below is the field that allows this.
Templates can be added or removed:
Note that in order for this field to be available in the Mapping UI the
MappingExperimentBeanon the client side must define thetemplateFilePathsparameter. For example in the client side xml files, define:
<bean id="mapping_experiment_bean" class="uk.ac.diamond.daq.mapping.impl.MappingExperimentBean"> <property name="templateFilePaths"> <list> </list> </property> </bean>
After submitting the scan, the UI converts the
MappingExperimentBeanto aScanRequestusing theScanRequestConverter. This uses the sameScanRequest.setTemplateFilePath()method mentioned above to set the template file path in theScanRequest.

