Search for NXtomo(s), Modify, and Resume Processing#

H. Payno

Jan 09, 2024

15 min read

In this example, we will see how to:

  • Search for NXtomo(s)

  • Modify the NXtomo

  • Trigger downstream widgets

The script to search, modify, and trigger downstream widgets can be the first step of a workflow like this:

../../_images/start_workflow.png

Hint

To edit the script, double-click on the widget.

Search for NXtomo(s)#

It can be the first step of a workflow.

Python comes with the glob module, which allows us to find pathnames matching a specified pattern.

You can use it, for example, to search for all files ending with .nx, which is the expected file extension for NXtomos.

Hint

For convenience, in this example, we will search everywhere at a given location using the ‘**’ pattern. But you can also search at a specific level/depth instead.

from glob import glob
import os

files = glob(
    os.path.join("/path/to/data/**", "*.nx"),  # Search for all .nx files under /path/to/data/
    recursive=True,
)
print(files)  # Print found files to ensure this part works

Now, these files can contain zero, one, or several NXtomo objects that we want to use. There are several ways to handle this. The simplest is probably to use the Tomwer ScanFactory. It can browse a file and deduce if an entry is a NXtomo or not.

We can add the following lines to the script:

from glob import glob
from tomwer.core.scan.scanfactory import ScanFactory

# Retrieve all scans
scans = []
for file_ in files:
    scans.extend(ScanFactory.create_scan_objects(file_))
# Print found scans
print("Found scans", [scan.get_identifier().short_description() for scan in scans])

Here is a concrete example of such a script and the expected output:

../../_images/find_nxtomo_example.png

Modify NXtomo(s)#

Now that we have a set of scans, we can modify them as needed (metadata only for now). The simplest way to do this is to use the NXtomo library.

We cannot use the scans directly from Tomwer because the API doesn’t allow “direct persistent” modification of the data.

For example, to modify the energy value of each NXtomo, we need to:

  • Load the NXtomo

  • Modify it

  • Overwrite the NXtomo

You can append the following lines to the script:

from nxtomo.application.nxtomo import NXtomo

for scan in scans:
    nx_tomo = NXtomo().load(scan.master_file, scan.entry)
    # Modify the field as needed
    nx_tomo.energy = new_value
    nx_tomo.save(
        file_path=scan.entry,
        data_path="entry",
        overwrite=True,
    )
    # Clear caches to ensure coherence
    scan.clear_caches()

See also

A tutorial explaining how to edit NXtomo can be found here: https://tomotools.gitlab-pages.esrf.fr/nxtomo/tutorials/nx_tomo_tutorial.html#edit-an-NXtomo

Resume Processing / Trigger Downstream Widgets#

Now that the NXtomo objects are modified, we can pass them to the next widgets. For this, we define an output variable at the end of the script. In this example, we want to provide a list of scans, so we define out_tomo_objs.

out_tomo_objs = scans

Since many widgets expect a single scan object, we also need to add a ‘tomo objs hub’ instance to the workflow to “split” the list into a series of data/scan objects, ensuring smooth processing.

../../_images/adding_hub.png

Warning

out_tomo_obj vs out_tomo_objs

You cannot set out_tomo_obj multiple times in a loop. Output variable resolution happens only at the end, so it will trigger downstream widgets only once, using the last value set.

Caution

Tomwer Scan Object Caches

In this example, we are not loading parameters from the scan object. But if, for example, we added a call to scan.energy before modifying the value, it would be cached. When you modify a scan and reuse an existing scan object, it is usually safer to clear caches using the clear_caches() function.

Example with a Workflow After#

Here is an example of a workflow after NXtomo search and modification. In this case, it will build a volume for each scan.

../../_images/final_workflow.png

Warning

GUI Freeze

The Python script will be executed in the main Qt thread. As a consequence, during its execution, the GUI will not be responsive. If your processing is heavy, you might experience some GUI freezing.