The Parameter Tree

The Parameter Tree#

Getting familiar with the parameter tree using the MoonFlower simulation/reconstruction.

A ptychographic reconstruction requires a lot of input, from details about the data to the specifics of the reconstruction algorithm among others. All this information is typically summarised in a parameter tree. In PtyPy, the parameter tree is a Python object - more specifically a dictionary-like ptypy.utils.Param object. This means that we can put together a Python script (or Jupyter notebook), define all relevant parameters and execute the script (notebook) to run a ptychographic reconstruction! Let’s have a look at this parameter tree and its building blocks…

Tip

A full list of available parameters can be found here: https://ptycho.github.io/ptypy/rst/parameters.html

First, we need to import ptypy.

import ptypy
import ptypy.utils as u

We start by creating an empty root parameter tree p.

p = u.Param()

We set the verbose_level to "interactive" which is preferred for Jupyter notebooks since it gives a more condensed user output. Other options to use are "info" or "debug" for a more detailed and verbose output.

p.verbose_level = "interactive"

Next we create a subtree for input/output related parameters. For this particular example, we don’t want to save any intermediary files and turn off automatic plotting.

p.io = u.Param()
p.io.rfile = None
p.io.autosave = u.Param(active=False)
p.io.autoplot = u.Param(active=False)
p.io.interaction = u.Param(active=False)

Note

Parameters can be specified as arguments to the u.Param class when creating a tree/subtree, like

p.io.interaction = u.Param(active=False)

instead of

p.io.interaction = u.Param()
p.io.interaction.active = False

PtyPy allows us to define multiple scans in a single reconstruction, but in most cases we just define a single scan instance. In this case, we create a scan of type (scan model) "Full" and call it MF (named after the MoonFlower simulation example used here) but it could be named differently.

p.scans = u.Param()
p.scans.MF = u.Param()
p.scans.MF.name = "Full"

The scan model creates the Views, Containers and Storages for the object, probe and other data structures including the PODs which links them all together.

Tip

To learn more about these classes and their underlying concepts, visit this tutorial from the main PtyPy documentation.

As part of the scan we also need to define a data subtree, holding all relevant information related to the data (or simulated data) we are trying to reconstruct. For a real experiment, users can create their own data loader by subclassing ptypy.core.data.PtyScan or using the generic ptypy.experiment.hdf5_loader.Hdf5Loader. In this example, we are using the MoonFlowerScan which provides simulated diffraction data from a synthetic probe (the moon) and a synthetic object (the flower).

p.scans.MF.data= u.Param()
p.scans.MF.data.name = "MoonFlowerScan"
p.scans.MF.data.shape = 128
p.scans.MF.data.photons = 1e8

# The following parameters are only used for the simulation
p.scans.MF.data.num_frames = 200
p.scans.MF.data.density = 0.2
p.scans.MF.data.psf = 0.

Tip

To learn more about data management and how to subclass PtyScan, visit this tutorial from the main PtyPy documentation.

Finally, we need to define one (or more) reconstruction engines which specify which ptychographic algorithm(s) should be used in the reconstruction. In this example, we define engine00 to use the difference map (DM) algorithm with 80 iterations.

p.engines = u.Param()
p.engines.engine00 = u.Param()
p.engines.engine00.name = "DM"
p.engines.engine00.numiter = 80
p.engines.engine00.numiter_contiguous = 1

At the end of our script (notebook) we can create a ptypy.core.Ptycho object by passing the parameter tree p and level=5 which initialises everything and starts the reconstruction.

P = ptypy.core.Ptycho(p,level=5)

After loading/simulating the diffraction data, initialising the model/engine and running 80 iterations of difference map the output should look something like this

Full: loading data for scan MF (161 diffraction frames, 161 PODs, 1 probe(s) and 1 object(s))
Full: loading data for scan MF (reformatting probe/obj/exit)
Full: loading data for scan MF (initializing probe/obj/exit)
DM: initializing engine
DM: preparing engine
DM: Iteration # 80/80 :: Fourier 5.37e+01, Photons 1.55e+01, Exit 4.78e+00
==== This reconstruction relied on the following work ==========================
The Ptypy framework:
    Enders B. and Thibault P., "A computational framework for ptychographic reconstructions" Proc. Royal Soc. A 472 (2016) 20160640, doi: 10.1098/rspa.2016.0640.
The difference map reconstruction algorithm:
    Thibault et al., "Probe retrieval in ptychographic coherent diffractive imaging" Ultramicroscopy 109 (2009) 338, doi: 10.1016/j.ultramic.2008.12.011.
================================================================================

To visualise the results of the reconstruction, we can use ptypy.utils.plot_client.figure_from_ptycho(P) to produce an image like this

Challenge

Run the MoonFlower example below and inspect some of the attributes of the Ptycho instance P, like P.obj, P.probe and P.diff. These are all containers which are connected to .storages and have a list of .views. From a single object view, we can find out information about for example the reconstructed pixelsize: P.obj.views[“V0000”].psize.

Challenge

Change the verbose_level to info or debug and observe the change in output. In the section just above the summary of the engine parameters, you should find a list of storages which also contains information about the reconstructed pixel size in the object.

Challenge

Where the Ptycho instance is being created, change the level from 5 to 4 and observe the output. At level 4, PtyPy does all the data loading but it stops before invoking the engine. This can be useful if you would like to modify the P instance just before running the engine with P.run()


import ptypy
import ptypy.utils as u

# Create parameter tree
p = u.Param()

# Set verbose level, can be "interactive", "info" or "debug"
p.verbose_level = "interactive"

# Basic I/O settings (no files saved in this case)
p.io = u.Param()
p.io.rfile = None
p.io.autosave = u.Param(active=False)
p.io.autoplot = u.Param(active=False)
p.io.interaction = u.Param(active=False)

# Define the scan model
p.scans = u.Param()
p.scans.MF = u.Param()
p.scans.MF.name = "Full"

# Data loader / simulator 
# Generate 200 frames (128x128px) of diffraction data
p.scans.MF.data= u.Param()
p.scans.MF.data.name = "MoonFlowerScan"
p.scans.MF.data.shape = 128
p.scans.MF.data.photons = 1e8
# the following parameters
# are only used for the simulation
p.scans.MF.data.num_frames = 200
p.scans.MF.data.density = 0.2
p.scans.MF.data.psf = 0.

# Define reconstruction engine
p.engines = u.Param()
p.engines.engine00 = u.Param()
p.engines.engine00.name = "DM"
p.engines.engine00.numiter = 80
p.engines.engine00.numiter_contiguous = 1

# Prepare and run
P = ptypy.core.Ptycho(p,level=5)

# Display the results
fig = ptypy.utils.plot_client.figure_from_ptycho(P)