Working with Data from NanoMAX

Working with Data from NanoMAX#

Learning how to reconstruct data from the NanoMAX beamline

The data#

Data file

Type

Download

Courtesy of

Reference

000077.h5

Raw

10_keV.zip

Maik Kahnt

DOI

scan_000077_eiger4m.h5

Raw

10_keV.zip

Maik Kahnt

DOI

The following notebook has been adapted from the original PtyPy run script as provided by the data deposition referenced above.

Reconstruction#

After 1000 iterations of DM + 1000 iterations of ML, the result looks like this


import ptypy, os
import ptypy.utils as u

ptypy.load_ptyscan_module("nanomax")
ptypy.load_gpu_engines(arch="cupy")

# Root directory of tutorial data
tutorial_data_home = "../../data/"
tutorial_data_home = "/dls/science/groups/imaging/ptypy_tutorials/"

# Dataset for this tutorial
dataset = "nanomax_10_keV"

# Absolute path to HDF5 file with raw data
beamtime_basedir = os.path.join(tutorial_data_home, dataset)

# Detector / Sample name
detector         = 'eiger4m'
sample           = '0001_setup'

# Parameters
scannr           = 77
distance_m       = 7.180     # distance between the sample and the detector in meters
defocus_um       = 1500      # distance between the focus and the sample plane in micro meters -> used for inital probe
cropping         = 512       # Cropped to 512 for the purpose of this tutorial, original value is 1536
binning          = 1
probe_modes      = 2

# General parameters
p = u.Param()
p.verbose_level = "interactive"
p.run = 'scan%d' % scannr
p.frames_per_block = 1000                  # reduce this number if you run out of memory on the GPU.

# Set io settings (no files saved)
p.io = u.Param()
p.io.rfile = None
p.io.autosave = u.Param(active=False)
p.io.interaction = u.Param(active=False)

# Live-plotting during the reconstruction
p.io.autoplot = u.Param()
p.io.autoplot.active=True
p.io.autoplot.threaded = False
p.io.autoplot.layout = "jupyter"
p.io.autoplot.interval = 10

# Scan parameters
p.scans = u.Param()
p.scans.scan00 = u.Param()
p.scans.scan00.name = 'BlockFull'
p.scans.scan00.coherence = u.Param()
p.scans.scan00.coherence.num_probe_modes = probe_modes   # number of probe modes
p.scans.scan00.coherence.num_object_modes = 1            # number of object modes


p.scans.scan00.data = u.Param()
p.scans.scan00.data.name = 'NanomaxContrast'
p.scans.scan00.data.path = beamtime_basedir+'/raw/'+sample+'/'
p.scans.scan00.data.detector = detector
p.scans.scan00.data.maskfile = {'merlin': '/data/visitors/nanomax/common/masks/merlin/latest.h5',
                                'pilatus': None,
                                'eiger': '/data/visitors/nanomax/common/masks/eiger/eiger_4M_blinking_pixels.h5', # legacy
                                'eiger1m': None,
								'eiger4m': None}[detector]
p.scans.scan00.data.scanNumber = scannr
p.scans.scan00.data.xMotor = 'pseudo/x'
p.scans.scan00.data.yMotor = 'pseudo/y'
p.scans.scan00.data.zDetectorAngle = 0.0    # rotation of the detector around the beam axis in [deg]
p.scans.scan00.data.xyAxisSkewOffset = 0.0
p.scans.scan00.data.shape = cropping        # size of the window of the diffraction patterns to be used in pixel
# p.scans.scan00.data.save = 'append'
# p.scans.scan00.data.dfile = path_data       # once all data is collected, save it as .ptyd file
p.scans.scan00.data.center = (1340,685)     # center of the diffraction pattern (y,x) in pixel or None -> auto
p.scans.scan00.data.cropOnLoad = True       # only load used part of detector frames -> save memory
                                            # requires center to be set explicitly
p.scans.scan00.data.xMotorFlipped = True
p.scans.scan00.data.yMotorFlipped = False
p.scans.scan00.data.orientation = {'merlin': (False, False, True),              # (do_transpose, do_flipud, do_fliplr)
                                   'pilatus': (False, True, False), 
                                   'eiger': (False, True, False),               # legacy
                                   'eiger1m': (False, True, False),
                                   'eiger4m': (False, True, False)}[detector]   # when mounted 180 degrees rotated
                                   #'eiger4m': (False, True, False)}[detector]  # old version when mounted the right way around
p.scans.scan00.data.distance = distance_m   # distance between sample and detector in [m] 
p.scans.scan00.data.psize = {'pilatus': 172e-6, 
                             'merlin': 55e-6, 
                             'eiger': 75e-6,   # legacy
                             'eiger1m': 75e-6,
                             'eiger4m': 75e-6}[detector]
p.scans.scan00.data.rebin = binning 
p.scans.scan00.data.I0 = None               # can be like 'alba2/1'
p.scans.scan00.data.min_frames = 10
p.scans.scan00.data.load_parallel = 'all'

# Init probe from KB parameters
p.scans.scan00.illumination = u.Param()
p.scans.scan00.illumination.model = None
p.scans.scan00.illumination.aperture = u.Param()
p.scans.scan00.illumination.aperture.form = 'rect'
p.scans.scan00.illumination.aperture.size = 525e-6        # EH1-M1 aperture diameter 
p.scans.scan00.illumination.propagation = u.Param()
p.scans.scan00.illumination.propagation.focussed = 0.200  # EH1-M1 focal length
p.scans.scan00.illumination.propagation.parallel = 1.*defocus_um*1e-6 # propagate the inital guess -> gives phase curvature
p.scans.scan00.illumination.propagation.antialiasing = 1

### details on how to init multiple probe modes differently
p.scans.scan00.illumination.diversity = u.Param()
p.scans.scan00.illumination.diversity.power = 0.1         # Power of modes relative to main mode (zero-layer)
p.scans.scan00.illumination.diversity.noise = (0.5, 1.0)  # (rms, mfs, rms_mod, mfs_mod)

p.engines = u.Param()

# 1st use the difference map algorithm
p.engines.engine00 = u.Param()
p.engines.engine00.name = 'DM_cupy'
p.engines.engine00.numiter = 1000                     # number of iterations
p.engines.engine00.numiter_contiguous = 10           # Number of iterations without interruption
p.engines.engine00.probe_support = 3                  # non-zero probe area as fraction of the probe frame
#p.engines.engine00.probe_update_start = 50           # number of iterations before probe update starts
p.engines.engine00.obj_smooth_std = 10.               # gaussian smoothing (pixel) of the current object prior to update
p.engines.engine00.clip_object = (0,1)                # clip object amplitude into this interval

# 2nd use the maximum likelyhood algorithm
p.engines.engine01 = u.Param()
p.engines.engine01.name = 'ML_cupy'
p.engines.engine01.numiter = 1000                     # number of iterations
p.engines.engine01.numiter_contiguous = 10           # Number of iterations without interruption
p.engines.engine01.probe_support = 3                  # non-zero probe area as fraction of the probe frame
#p.engines.engine01.probe_update_start = 50           # number of iterations before probe update starts

# start the reconstruction
P = ptypy.core.Ptycho(p,level=5)