Advanced#
Unlocks finer controls for challenging data. Use this if you need to tweak advanced data preprocessing, more advanced constraints, or output preference settings.
1# Created with PtyRAD 0.1.0b13
2# Documentation: https://ptyrad.readthedocs.io/en/latest/
3# Detailed description for each option: https://ptyrad.readthedocs.io/en/latest/_autosummary/ptyrad.params.html
4
5# Advanced params file with ALL PtyRAD options (~ 100 except hypertune) and full comments for refined reconstructions
6
7# tBL_WSe2
8
9init_params:
10 # Experimental params
11 probe_illum_type : 'electron' # type: str. Choose between 'electron' or 'xray'
12 probe_kv : 80 # type: float, unit: kV. Acceleration voltage for relativistic electron wavelength calculation
13 probe_conv_angle : 24.9 # type: float, unit: mrad. Semi-convergence angle for probe-forming aperture
14 probe_aberrations : {'C10': 0} # type: dict or null. Aberration coefficients in Angstroms, angles are in degrees. Supports Krivanek (polar and cartesian), Haider notations, or a mix of them. i.e., {'C10': 200, 'A1': 50, 'A1phi': 25, 'C21a': 300, 'C23': 400, 'phi23': 25, 'Cs': 5000}. Common aliases like `defocus` and `Cs` are also supported. Note that C10 = -df, and positive C10 refers to overfocus (stronger lens) following Kirkland/abtem convention.
15 meas_Npix : 128 # type: integer, unit: px (k-space). Detector pixel number, EMPAD is 128. Only supports square detector for simplicity
16 pos_N_scans : 16384 # type: int, unit: #. Number of probe positions (or equivalently diffraction patterns since 1 DP / position)
17 pos_N_scan_slow : 128 # type: int, unit: #. Number of scan position along slow scan direction. Usually it's the vertical direction of acquisition GUI
18 pos_N_scan_fast : 128 # type: int, unit: #. Number of scan position along fast scan direction. Usually it's the horizontal direction of acquisition GUI
19 pos_scan_step_size : 0.4290 # type: float, unit: Ang. Step size between probe positions in a rectangular raster scan pattern
20 meas_calibration : {'mode': 'fitRBF', 'value': null} # type: dict. 'mode' can be 'dx', 'dk', 'kMax', 'da', 'angleMax', 'n_alpha', 'RBF', and 'fitRBF'. All modes requires a scalar 'value' input except 'fitRBF'. The units are Ang, 1/Ang and mrad for electron.
21 # Model complexity
22 probe_pmode_max : 6 # type: int, unit: #. Maximum number of mixed probe modes. Set to pmode_max = 1 for single probe state, pmode_max > 1 for mixed-state probe during initialization. For simulated initial probe, it'll be generated with the specified number of probe modes. For loaded probe, the pmode dimension would be capped or padded to this number
23 probe_pmode_init_pows : [0.02] # type: list of 1 or a few (pmode_max) floats. Initial power for each additional probe modes. If set at [0.02], all additional probe modes would contain 2% of the total intensity. sum(pmode_init_pows) must be 1 if more than len(pmode_init_pows) > 1. See 'utils.make_mixed_probe' for more details
24 obj_omode_max : 1 # type: int, unit: #. Maximum number of mixed object modes. Set to omode_max = 1 for single object state, omode_max > 1 for mixed-state object during initialization. For simulated initial object, it'll be generated with the specified number of object modes. For loaded object, the omode dimension would be capped or padded to this number
25 obj_omode_init_occu : {'occu_type': 'uniform', 'init_occu': null} # type: dict. Occupancy type and value for mixed-object modes. Typically we do 'uniform' for frozen phonon like configurations as {'occu_type': 'uniform', 'init_occu': null}. 'occu_type' can be either 'uniform' or 'custom', if 'custom', pass in the desired occupancy as an array to 'init_occu'
26 obj_Nlayer : 6 # type: int, unit: #. Number of slices for multislice object
27 obj_slice_thickness : 2 # type: float, unit: Ang. Slice thickness (propagation distance) for multislice ptychography. Typical values are between 1 to 20 Ang
28 # Preprocessing
29 meas_permute : null # type: null or list of ints. This applies additional permutation (reorder axes) for the initialized diffraction patterns. The syntax is the same as np.transpose()
30 meas_reshape : null # type: null or list of 3 ints. This applies additional reshaping (rearrange elements) for the initialized diffraction patterns. The syntax is the same as np.reshape(). This is commonly needed to convert the 4D diffraction dataset (Ry,Rx,ky,kx) into 3D (N_scans,ky,kx)
31 meas_flipT : [1,0,0] # type: null or list of 3 binary booleans (0 or 1) as [flipup, fliplr, transpose] just like PtychoShleves. Default is null or [0,0,0] but you may need to find the correct flip and transpose to match your dataset configuration. This applies additional flip and transpose to initialized diffraction patterns. It's suggested to use 'meas_flipT' to correct the dataset orientation and this is the only orientaiton-related value attached to output reconstruction folder name
32 meas_crop : null # type: null or (4,2) nested list of ints as [[scan_slow_start, scan_slow_end], [scan_fast_start, scan_fast_end], [ky_start, ky_end], [kx_start, kx_end]]. If you want to keep some of the dimensions, for example, do [[0,64],[0,64], null, null] to crop in real space but leaves the k-space untouched. This applies additional cropping to the 4D dataset in both real and k-space. This is useful for reconstrucing a subset of real-space probe positions, or to crop the kMax of diffraction patterns. The syntax follows conventional numpy indexing so the upper bound is not included
33 meas_pad : {'mode': null, 'padding_type': 'power', 'target_Npix': 256, 'value': 0, 'threshold': 70} # type: dict. This will pad the CBED to side length = 'target_Npix' and correspondingly change the kMax, dx, Npix. 'mode' can be 'on_the_fly', 'precompute', or null (will disable padding). 'precompute' will pad the measurements during initialization, while 'on_the_fly' will only pad the measurements during optimization, so it's more efficient for GPU memory. 'on_the_fly' padding doesn't really affect the reconstruction time so it's suggested to always use 'on_the_fly' if you're padding to save the GPU memory. 'padding_type' can be 'constant', 'edge', 'linear_ramp', 'exp', or 'power'. In practice, you might only want to pad with 'constant' 0, or use 'power' law background for padding. If using 'constant' or 'linear_ramp' padding type, the provided 'value' would be used to pad the pattern. If you're using 'exp' or 'power', the mean diffraction pattern amplitude is first masked to exclude the central part, and then fitted by a functional background (i.e., exponential or power law). The mask is determined by the provided 'threshold' (percentile, 0-100). Smaller threshold value means excluding more of the high-value region, so larger mask radius and smaller fitting area. Typical value of 'threshold' is between 20 to 70. Note that padding doesn't necessarily increase the spatial resolution but will surely slow down the reconstruction. If you're pushing the lateral resolution, it's recommended to directly record those high angle scattering into your data given enough SNR.
34 meas_resample : {'mode': null, 'scale_factors': [2,2]} # type: dict. 'mode' can be 'on_the_fly', 'precompute', or null. 'scale_factor' takes a list of 2 floats as [ky_zoom, kx_zoom] that must be the same. This applies additional resampling of initialized diffraction patterns along ky and kx directions. This is useful for changing the k-space sampling of diffraction patterns. See scipy.ndimage.zoom for more details. 'precompute' will resample the measurements during initialization, while 'on_the_fly' will only resample the measurements during optimization, so it's more efficient for GPU memory if you're upsampling (i.e., scale_factor > 1). For downsampling, it's much better to do 'precompute' to save GPU memory. 'on_the_fly' resampling doesn't really affect the reconstruction time so it's suggested to always use 'on_the_fly' if you're upsampling.
35 meas_add_source_size : null # type: null or float, unit: Ang. This adds additional spatial partial coherence to diffraction patterns by applying Gaussian blur along scan directions. The provided value is used as the std (sigma) for the Gaussian blurring kernel in real space. Note that FWHM ~ 2.355 std, so a std of 0.34 Ang is equivalent to a source size (FWHM) of 0.8 Ang
36 meas_add_detector_blur : null # type: null or float, unit: px (k-space). This adds additional detector blur to diffraction patterns to emulate the PSF on detector. The provided value is used as the std (sigma) for the Gaussian blurring kernel in k-space. Note that this is applied to the "measured", or "ground truth" diffraction pattern and is different from 'model_params['detector_blur_std']' that applies to the forward simulated diffraction pattern
37 meas_remove_neg_values : {'mode': 'clip_neg', 'value': null, 'force': false} # type: dict. Choose the preprocessing method for handling negative values in the measurements. Available options are 'subtract_min', 'subtract_value', 'clip_neg', and 'clip_value'. Previously (before beta3.1) the PtyRAD default is 'subtract_min', while for low dose data is recommended to use 'clip_neg' or 'clip_value' i.e. {'mode': 'clip_neg', 'value': 20}. Current default is 'clip_neg'. 'value' is only needed for 'subtract_value' and 'clip_value'. This correction is skipped if there's no negative values in measurements. If you want to enforce some offsetting when there's no negative values, set 'force': true to enforce the correction.
38 meas_normalization : {'mode': 'max_at_one'} # type: null or dict. Choose the normalization method for measurements. Available options are 'max_at_one' (default), 'mean_at_one', 'sum_to_one', and 'divide_const'. For 'divide_const', you need to provide another dict entry 'value': <VALUE>.
39 meas_add_poisson_noise : null # type: null or dict, i.e., {'unit': 'total_e_per_pattern', 'value': 10000} or {'unit': 'e_per_Ang2', 'value': 10000}. This applies additional Poisson noise to diffraction patterns to emulate the Poisson statistics of electron dose based on the given unit, value, and scan step size. This is useful when you have a noise-free simulated dataset and want to try ptychographic reconstruciton at different dose conditions
40 meas_export : null # type: null, boolean, or dict, i.e., {'file_dir': null, 'file_name': 'ptyrad_init_meas', 'file_format': 'hdf5', 'output_shape': null, 'append_shape': true}. Set this to True or a dict to enable exporting the final initialized measurements array to disk for further processing, analysis, or visualization. The exported data layout has the same Python convention with py4DGUI so there's no need to worry about orientation mismatch. This can be used to interactively check whether the meas_flipT is correct. By default the output layout is (N_scans, Ky, Kx), and dropping it to py4DGUI then reshape it into (Ny, Nx, Ky, Kx) keeps the correct orientation. 'file_dir' sets the output directory, if None, it'll export to the same folder as 'meas_params['path']'. 'file_format' supports 'hdf5', 'tif', 'npy', and 'mat'. 'output_shape' takes a list of integers like [Ny, Nx, Ky, Kx] or [N_scans, Ky, Kx]. 'append_shape' is a boolean, if True, it'll append the shape of the array to the output file name.
41 probe_permute : null # type: null or list of int. This applies additional permutation (reorder axes) for the initialized probe. The syntax is the same as np.transpose()
42 probe_z_shift : null # type: null or float. unit: Ang for electron and m for x-ray. This shifts the initialized probe axially along the depth (z) dimension. The sign convention follows the propagation direction, so positive value means to propagate the probe further into the object (i.e., forward direction), and negative value refers to rewind the probe backward. This is used to shift the probe focus vertically along the object depth dimension in accordance with the object z-recentering (i.e., obj_z_crop, obj_z_pad). For example, if you pad the reconstructed object with additional 50 Ang of vacuum layer ON TOP via 'obj_z_pad', you'll want to apply a 'probe_z_shift: -50' Ang to the originally reconstructed probe so the relative geometry of probe and object is conserved.
43 probe_normalization : {'mode': 'mean_total_ints'} # type: null or dict. Choose the normalization method for probe intensity. i.e., {'mode': 'mean_total_ints'}, or {'mode': 'target_intensity', 'value': 1}. Available options for 'mode' are 'mean_total_ints' (default), 'max_total_ints', and 'target_intensity'. For 'target_intensity', you need to provide another dict entry 'value': <VALUE>. Note that probe intensity should be close to the measurement intensity to make object amplitude ~ 1. For thick sample with short collection angles, significant amount of electrons are scattered outside of the detector, use 'max_total_ints' to normalize the probe intensity by the strongest diffraction pattern (ideally vacuum region) for more accurate reconstruction.
44 pos_scan_flipT : null # type: null or list of 3 binary booleans (0 or 1) as [flipup, fliplr, transpose] just like PtychoShleves. Default value is null or equivalently [0,0,0]. This applies additional flip and transpose to initialized scan patterns. Note that modifing 'scan_flipT' would change the image orientation, so it's recommended to set this to null, and only use 'meas_flipT' to get the orientation correct
45 pos_scan_affine : null # type: null or list of 4 floats as [scale, asymmetry, rotation, shear] just like PtychoShleves. Default is null or equivalently [1,0,0,0], rotation and shear are in unit of degree. This applies additional affine transformation to initialized scan patterns to correct sample drift and imperfect scan coils
46 pos_scan_rand_std : 0.15 # type: null or float, unit: px (real space). Randomize the initial guess of scan positions with Gaussian distributed displacement (std in px) to reduce raster grid pathology
47 obj_z_crop : null # type: null or list of 2 ints as [z_start, z_end]. This applies additional cropping to the 4D object (omode, Nz, Ny, Nx) along depth dimension (Nz) and removes the unselected layers. This is useful for removing the unwanted vacuum layers above and below the specimen. The syntax follows conventional numpy indexing so the upper bound is not included.
48 obj_z_pad : null # type: null or dict, i.e., {'pad_layers': [2,2], 'pad_types': ['vacuum', 'vacuum']}. This applies additional padding to the 4D object (omode, Nz, Ny, Nx) along depth dimension (Nz) and expands the object thickness. 'pad_layers' is a list of 2 ints that determines how many layers are appended to the top and bottom surfaces. [2,2] means 2 layers on top and 2 layers on the bottom. Set the value to 0 or None to achieve asymmetric padding on one of the surface. For example 'pad_layers': [0,5] means to only pad 5 layers to the bottom. 'pad_types' is a list of 2 strings that specifies whether we are padding vacuum layers, mean layers, or edge layers. The available options are 'vacuum', 'mean', and 'edge'. This is useful for expanding reconstructed object to test object positioning or total thickness. Note that one should also adjust the 'probe_z_shift' (loaded probe) or 'probe_defocus' (simulated probe) accordingly, because padding object on the top surface will effectively change the entrance plane of the probe. For example, if one padded 10 nm of vacuum on the top surface, one should also add a 10 nm underfocus (for simulated probe via 'probe_defocus'), or apply a z-shift of -10 nm to the loaded probe via 'probe_z_shift' to maintain the relative position of probe and object.
49 obj_z_resample : null # type: null or dict, i.e., {'mode': 'scale_Nlayer', 'value': 2}. This applies additional resampling to the 4D object (omode, Nz, Ny, Nx) along depth dimension (Nz), and modifies the slice thickness altogether to preserve prod(amp), sum(phase), and total thickness. 'mode' can take string options including 'scale_Nlayer', 'scale_slice_thickness', 'target_Nlayer', 'target_slice_thickness', or null to disable it. 'value' takes the corresponding value with the specified mode. The values for 'scale_Nlayer', 'scale_slice_thickness', and 'target_slice_thickness' can be any positive float, while 'target_Nlayer' must take a positive integer. This is useful when we need to reslice the reconstructed object into different slice thicknesses including converting between single and multislice objects.
50 # Input source and params
51 meas_source : 'file' # type: str. Data type of the measurements (diffraction patterns). Currently supporting 'file' or 'custom'.
52 meas_params : {'path': 'data/tBL_WSe2/Panel_g-h_Themis/scan_x128_y128.raw', 'key': null} # type: dict, or numpy array. For file type of 'mat', or 'hdf5', it's preferred to provide both the 'path' and 'key' in a dict {'path': <PATH_TO_DATA>, 'key': <DATA_KEY>} to retrieve the data matrix, although PtyRAD would try to retrive the data even without a key. 'tif' would only need the {'path':<PATH_TO_DATA>}. For 'raw' you can optionally pass in 'shape':(N,height,width), 'offset':int, 'gap':int to load the .raw files from EMPAD1 and pre-processed EMPAD datasets. For example, {'path': <PATH_TO_DATA>, 'offset':0, 'gap':0} can be used to load pre-processed EMPAD2 raw dataset with no gap between binary diffraction patterns. The 'shape' will be automatically filled in from 'init_params', while 'offset':0, and 'gap':1024 are default values for EMPAD1 datasets. For py4dstem processed diffraction patterns (hdf5), use '/datacube_root/datacube/data' for your 'key'. For 'custom' source, pass the numpy array to the 'measurements_params' entry after you load this .yml as a dict
53 probe_source : 'simu' # type: str. Data source of the probe. Currently supporting 'simu', 'PtyRAD', 'PtyShv', 'py4DSTEM', and 'custom'
54 probe_params : null # type: null, str, or numpy array. Parameters of the probe loading/initialization. For 'simu' (simulating probe), use null to simulate based on experimental parameters specified in 'init_params'. For loading probe from 'PtyRAD' or 'PtyShv', provide a str of <PATH_TO_RECONSTRUCTION_FILE>. For 'custom' probe source, pass the 3D numpy array to the 'probe_params' entry after you load this .yml as a dict
55 pos_source : 'simu' # type: str. Data source of the probe positions. Currently supporting 'simu', 'PtyRAD', 'PtyShv', 'py4DSTEM', 'foldslice_hdf5', and 'custom'
56 pos_params : null # type: null, str, or numpy array. Parameters of the probe positions loading/initialization. For 'simu' (simulating probe positions), provide null and check whether you need 'scan_flipT'. The positions would be simulated based on 'N_scan_slow', 'N_scan_fast', 'scan_step_size', and 'scan_affine'. For loading probe positions from 'PtyRAD' or 'PtyShv', provide a str of <PATH_TO_RECONSTRUCTION_FILE>. For loading probe positions from 'foldslice_hdf5', provide a str of <PATH_TO_POSITION_FILE>. These hdf5 files are generated from many APS instruments that were previously handled in `fold_slice` using 'p.src_positions='hdf5_pos'. For 'custom' probe position source, pass the (N_scans,2) numpy array to the 'pos_params' entry after you load this .yml as a dict
57 obj_source : 'simu' # type: str. Data source of the object. Currently supporting 'simu', 'PtyRAD', 'PtyShv', 'py4DSTEM', and 'custom'
58 obj_params : null # type: null, list of 4 ints, str, or numpy array. Parameters of the object loading/initialization. For 'simu' (simulating object), provide a list of 4 ints (omode, Nz, Ny, Nx) to specify the object shape or null to let PtyRAD determine it (null is suggested and is consistent with how PtyShv creates their initial object). For loading object from 'PtyRAD' or 'PtyShv', provide a str of <PATH_TO_RECONSTRUCTION_FILE>. For 'custom' object source, pass the 4D numpy array to the 'obj_params' entry after you load this .yml as a dict
59 tilt_source : 'simu' # type: str. Data source of the object tilts. Currently supporting 'simu', 'PtyRAD', 'file', and 'custom'
60 tilt_params : {'tilt_type': 'all', 'init_tilts': [[0,0]]} # type: dict, str, or numpy array. Parameters of the object tilt loading/initialization. The object tilt is implemeted by tilted Fresnel propagator, which should be fairly accurate within 1 degree (17 mrad). For 'simu' (simulating object tilts), provide a dict as {'tilt_type':'all', 'init_tilts':[[tilt_y, tilt_x]]}. tilt_y and tilt_x are floats in unit of mrad, defaults are [[0,0]]. 'tilt_type' can be either 'all' or 'each'. 'tilt_type': 'all' will create a (1,2) tilt array specifying all positions has the same tilt as 'initial_tilts', this is a globally uniform object tilt that can be either fixed, hypertune optimized, or AD-optimized (if learning rate of 'obj_tilts' != 0). 'tilt_type': 'each' will create a (N_scans,2) tilt array that all position starts at the same 'init_tilts', but it can be later individually optimized through AD by setting learning rate of 'obj_tilts' != 0, which allows pos-dependent local object tilt correction. For loading object tilts from 'PtyRAD', provide a str of <PATH_TO_RECONSTRUCTION_FILE>. If loading object tilt from 'file', provide a dict as {'path': <PATH_TO_DATA>, 'key': <DATA_KEY>} similar to 'meas_params'. The supported file types are 'tif', 'mat', 'hdf5', and 'npy'. Note that the object tilt array must be 2D with shape equals to (1,2) or (N,2). For 'custom' object tilts source, pass the (N_scans,2) or (1,2) numpy array to the 'tilt_params'. You should always provide an initial tilt guess for tilt correction either through hypertune or estimate with '/scripts/get_local_obj_tilts.ipynb' because optimizing tilts with AD from scratch would be too slow and most likely arrive at barely corrected, slice-shifted object
61
62model_params:
63 obj_preblur_std : null # type: null or float, unit: px (real space). This applies Gaussian blur to the object before simulating diffraction patterns. Since the gradient would flow to the original "object" before blurring, it's essentially deconvolving the object with a Gaussian kernel of specified std. This sort of deconvolution can generate sharp features, but the usage is not easily justifiable so treat it carefully as a visualization exploration
64 detector_blur_std : null # type: null or float, unit: px (k-space). This applies Gaussian blur to the forward model simulated diffraction patterns to emulate the PSF of high-energy electrons on detector for experimental data. Typical value is 0-1 px (std) based on the acceleration voltage
65 preload_data : true # type: bool. This flag determines how the measurements data is stored and transferred to device during reconstruciton. If true, measurement data will be fully loaded into device memory during model initialization for best performance. However, dataset larger than device memory (i.e., GPU VRAM) would throw Out-Of-Memory error. If 'preload_data': false, measurement data is kept on host memory (CPU RAM) and only the mini-batch is transferred to device memory in a streaming way. This would enable reconstruction of large dataset that doesn't fit into GPU VRAM with a little data transfer overhead. The default is 'true' for performance although the difference is negligible on demo datasets.
66 optimizer_params : {'name': 'Adam', 'configs': {}, 'load_state': null} # Support all PyTorch optimizer including LBFGS because LBFGS can't set separate learning rates for different tensors. The suggested optimizer is 'Adam' with default configs (null). You can load the previous optimizer state by passing the path of `model.hdf5` to `load_state`, this way you can continue previous reconstruciton smoothly without abrupt gradients. (Because lots of the optimizers are adaptive and have history-dependent learning rate manipulation, so loading the optimizer state is necessary if you want to continue the previous optimization trajectory). However, the optimizer state must be coming from previous reconstructions with the same set of optimization variables with identical size of the dimensions otherwise it won't run.
67 update_params:
68 obja : {'start_iter': 1, 'lr': 5.0e-4, 'end_iter': null} # object amplitude
69 objp : {'start_iter': 1, 'lr': 5.0e-4, 'end_iter': null} # object phase
70 obj_tilts : {'start_iter': null, 'lr': 0, 'end_iter': null} # object tilts
71 slice_thickness : {'start_iter': null, 'lr': 0, 'end_iter': null} # object dz slice thickness
72 probe : {'start_iter': 1, 'lr': 1.0e-4, 'end_iter': null} # probe
73 probe_pos_shifts: {'start_iter': 1, 'lr': 5.0e-4, 'end_iter': null} # sub-px probe positions
74
75loss_params:
76 loss_single: {'state': true, 'weight': 1.0, 'dp_pow': 0.5} # NRMSE error between single simulated and experimental diffraction pattern. 'dp_pow' is commonly set at 0.5 so NRMSE(DP_sim^0.5 - DP_exp^0.5) is equivalent to the Gaussian noise model for typical dataset (dose-sufficient) under the maximum-likelihood formalism
77 loss_poissn: {'state': false, 'weight': 1.0, 'dp_pow': 1.0, 'eps': 1.0e-6} # negative log likelihood between simulated and experimental diffraction pattern. 'dp_pow' is commonly set at 1 so - (DP_sim * (DP_exp) - DP_exp) is equivalent to the Poisson noise model for low dose dataset under maximum likelihood formalism. See Odstrˇcil2018 https://doi.org/10.1364/OE.26.003108 for more details
78 loss_pacbed: {'state': false, 'weight': 0.5, 'dp_pow': 0.2} # NRMSE error between simulated and experimental position-averaved CBED (PACBED). Similar to 'loss_single', except that it's comparing PACBED with PACBED and mostly focusing on the diffuse background when 'dp_pow' is set at 0.2
79 loss_sparse: {'state': false, 'weight': 0.1, 'ln_order': 1} # L_n norm regularization calculated for object phase. 'ln_order' means the L_n norm (|a_i^n|^(1/n)) used to regularize object phase ('objp'). Usually 'ln_order' is set at 1 for L1 norm (|a|), this promotes 0 in the objp and enhance the sparsity (i.e. discrete atoms). 'ln_order' = 2 would be equivalent to L2 norm that promotes near-0 values
80 loss_simlar: {'state': false, 'weight': 0.1, 'obj_type': 'both', 'scale_factor': [1,1,1], 'blur_std': 1} # std across omode dimension for obj. This promotes similarity between object modes. 'obj_type' can be either 'amplitude', 'phase', or 'both'. 'scale_factor' as (zoom_z, zoom_y, zoom_x) is used to scale the object before calculating the std, setting 'scale_factor' to [1,0.5,0.5] is equivalent to downsampling the obj 2x along y and x directions before calculating the std, which should encourage the obj modes to keep lateral atom shifts. Similarly, 'blur_std' applies a 2D (lateral) Gaussian blur kernel with specified std to blur the obj before calculating std along omode dimension
81
82constraint_params:
83 probe_mask_k : {'start_iter': null, 'step': 1, 'end_iter': null, 'radius': 0.22, 'width': 0.05, 'power_thresh': 0.95} # Apply a k-space sigmoid (similar to a top-hat) probe mask that cut off spatial frequencies beyond the probe-forming aperture. This prevents the probe from absorbing the object structure in k-space. It might cause high-frequency oscillations in the real-space object if you have strong diffuse background in the diffraction pattern and did not provide mixed-object to properly reproduce it. Recommend setting it to 'null' unless you're pursuing mixed-object with more physical probes. k-radius should be larger than 2*rbf/Npix to avoid cutting out the BF disk. 'radius' and 'width' are used to define the sigmoid funciton in relative unit with kMax. See 'utils/make_sigmoid_mask' for more details. 'power_thresh' is used to specify how far into the pmode should be masked. If 'power_thresh': 0.95, the k-space mask would be applied from strongest probe modes to the one that adds uo to 95% total intensity. This promotes a more physical mixed-probe while keeping a small fraction of probe modes to absorb unexpected errors.
84 probe_mask_r : {'start_iter': null, 'step': 1, 'end_iter': null, 'radius': 0.95, 'order': 6, 'power_thresh': 0.95, 'z_range': [-500,500], 'z_steps': 101} # Apply a real-space super-Gaussian (similar to a top-hat) probe mask that apodize (i.e., windowing) the real space probe. This is useful for mitigating the real space corner / edge artifact, or the checkerboard artifact in k-space probe amplitude that comes from resampling model DP when simu_Npix > meas_Npix. Use 'radius' and 'order' to adjust the shape of the super-Gaussian mask. To prevent cutting off probe when it's defocused, we roll the probe back to focal plane and apply the mask there, and then propagate the probe back to its original defocus. 'z_range' and 'z_steps' are used to assign the search space for focal plane, typically a +-50 nm with 1 nm step (i.e., 101 steps) is enough. 'power_thresh' is used to specify how far into the pmode should be masked. If 'power_thresh': 0.95, the k-space mask would be applied from strongest probe modes to the one that adds uo to 95% total intensity. This promotes a more physical mixed-probe while keeping a small fraction of probe modes to absorb unexpected errors. Because windowing in real-space is equivalent to blurring in the k-space, this mitigates the corner and edge artifact intensities in probe window, while also simultaneously blur the k-space probe slightly. A signal can not be perfectly bounded in both real and Fourier domains, since probe is physically bounded by probe-forming-aperture in k-space, the real-space probe is mathematically infinitely wide and probe tails never decays to 0. Therefore, this constraint will ALWAYS cut off some probe tails and may inadvertently cause more artifacts, use it with caution.
85 ortho_pmode : {'start_iter': 1, 'step': 1, 'end_iter': null} # Apply a SVD decomposition and orthogonalization of the mixed-state probe similar to the PtychoShelves implementation (except it's vectorized in PtyRAD). Turn this on when optimizing with mixed-state probe
86 fix_probe_int : {'start_iter': 1, 'step': 1, 'end_iter': null} # Rescale the probe intensity to make it consistent with the total diffraction pattern intensity (so every probe electron hits on the detector). This is needed to stabilize the object amplitude update because the probe update could potentially change the total intensity. This removes the scaling constant ambiguity between probe and object, and should be applied if you're simultaneously optimizing the probe and object amplitude
87 obj_rblur : {'start_iter': null, 'step': 1, 'end_iter': null, 'obj_type': 'both', 'kernel_size': 5, 'std': 0.4} # Apply a "lateral" 2D Gaussian blur to the object. This removes some high frequency noise in the reconstructed object and make the apperance smoother. 'obj_type' can be either 'amplitude', 'phase', or 'both' with a specified 'std' and 'kernel_size' in unit of real-space px. Ideally kernel size is odd (like 5) and larger than 6std+1 so it decays to 0. This is usually not needed if your dataset contains sufficient dose and the kMax is not insanely high (extremely high kMax would gives very fine dx which makes feature appear sharper and probably more seemingly noisy)
88 obj_zblur : {'start_iter': 1, 'step': 1, 'end_iter': null, 'obj_type': 'both', 'kernel_size': 5, 'std': 1} # Apply a "z-direction" 1D Gaussian blur to the object. This is a real-space alternative to the typical kz_filter (or so called missing-wedge regularization that applies Fourier filtering to the object) designed for multislice ptychography. Similar to 'obj_rblur', 'obj_type' can be either 'amplitude', 'phase', or 'both' with a specified 'std' and 'kernel_size' in unit of real-space px. Note that the 'ptycho/engines/GPU_MS/private/regulation_multilayers.m' from PtychoShelves (fold_slice) is a combination of 'obja_thresh', 'kr_filter', and 'kz_filter', so you may want to activate all these constraints altogether in PtyRAD to get the most similar effect
89 kr_filter : {'start_iter': null, 'step': 1, 'end_iter': null, 'obj_type': 'both', 'radius': 0.15, 'width': 0.05} # Apply a "lateral" Fourier low-pass filtering to the object. This is similar to the band-pass filter in Digital Micrograph that the k-space filter has a sigmoid-like profile, essentially a cut-off spatial frequency. Typically we're reconstucting object all the way to kMax (Nyquist frequency) so there's not much room for us to filter out hence it's recommended to keep this off unless you want to exclude certain spatial frequencies. 'radius' and 'width' are used to define the sigmoid funciton in relative unit with kMax. See 'utils/make_sigmoid_mask' for more details
90 kz_filter : {'start_iter': null, 'step': 1, 'end_iter': null, 'obj_type': 'both', 'beta': 1, 'alpha': 1} # Apply the arctan kz filter just like the 'regulation_multilayers.m' in PtychoShelves. 'beta' controls the strength of the arctan function and is the same as 'eng. regularize_layers' in PtychoShelves. Typical value of 'beta' ranges from 0 to 1. 'alpha' is the implicit constant controls the lateral Fourier filtering that has similar effect as 'kr_filter', usually set as 1 to be consistent with PtychoShelves. Note that 'kz_filter' is designed to be a PtyRAD equivalence of the 'regulation_multilayers.m' so it also includes the arctan Fourier filer, lateral Fourier filter, and the soft object amplitude thresholding if you set 'obj_type' to 'both'. See 'optimization/kz_filter' for more details. While this 'kz_filter' works very well for most multislice reconstructions of crystals, you might prefer 'obj_zblur' if you have an object that has distinct top and bottom layer like twisted bilayer or tilted systems because 'kz_filter' would introduce intermixing between the top and bottom layer due to the periodic boundary condition of Fourier transform. Another solution to the intermixing is to pad vacuum layers to your object and remove them later, although padding extensive vacuum layers tend to make object phase bleed into the vacuum layers and it's very hard to set the interface
91 kr_thresh : {'start_iter': null, 'step': 1, 'end_iter': null, 'obj_type': 'both', 'thresh': 0.05} # Apply a thresholding to supress weak spatial frequency coefficients of the object along "lateral" (i.e., x, y) directions. 'thresh' is the ratio of spatial frequencies to keep and thresh is within (0,1]. Typical 'thresh' value is 0.05, meaning keeping 5% of the strongest spatial frequencies and set the others to 0. Smaller 'thresh' value will promote sparsity in frequency domain (implemented with DCT) and produce a smoother object with reduced noise. Note that this constraint would also reduce / remove the disorder in periodic objects like strain and atomic defects as it only keep the strong spatial frequencies, so it's advised to only use it for purely amorphous specimen, low dose reconstructions, or as an initial stabilizing constraint. The DCT is applied to the last 2 dimensions for object array shaped (...,H,W), hence the regularization effect is only within 2D and is sequentially repeated for each depth slice. See https://doi.org/10.1364/OE.551986 for example on low dose biological samples.
92 complex_ratio : {'start_iter': null, 'step': 1, 'end_iter': null, 'obj_type': 'both', 'alpha1': 1, 'alpha2': 0} # Apply a complex constraint between object amplitude and phase based on https://doi.org/10.1016/j.ultramic.2024.114068 and https://doi.org/10.1364/OE.18.001981. This will constrain the amplitude to as A' = exp(-C*phase), C is an estimated (adaptive) positive constant, so the amplitude would look similar to phase and strongest phase shift would correspond to less than 1 amplitude. Default value of phase object is alpha1 = 1, alpha2 = 0. Note that the implementation considers the negative correlation between amplitude and phase for electctron ptychography, hence it's not the exact same formula as in the papers.
93 mirrored_amp : {'start_iter': null, 'step': 1, 'end_iter': null, 'relax': 0.1, 'scale': 0.03, 'power': 4} # Apply a more flexible, ad hoc constraint for constraining amplitude using 1-scale*phase**power, which provide more arbitrary parameters to tune the constrained amplitude based on the phase.
94 obj_z_recenter: {'start_iter': null, 'step': 1, 'end_iter': null, 'thresh': 95, 'scale': 1, 'max_shift': 10} # Apply the z-recentering of object based on CoM to keep the object centered within the depth dimension. The probe defocus is adjusted accordingly. thresh is the threshold value in percentile to exclude weaker signals while calculating center-of-mass along depth. The value of thresh should be around 90-95 to target values that are 2-3 sigma away from mean. scale is a multplication scaling factor applied on the measured shift, keep it <= 1 to slowly approach the ideal center. max_shift sets a hard limit on the applied shift, which should be less than half the number of z-slices. The shift is in unit of slices, so shift=1 would re-center the object by 1 slice.
95 obja_thresh : {'start_iter': 1, 'step': 1, 'end_iter': null, 'relax': 0, 'thresh': [0.96, 1.04]} # Apply a thresholding of object amplitude around 1. The threshold is defined by 'thresh' and the value is determined by the min and max. Note that wave amplitude is multiplicative when propagating through multislice object, so even an objective amplitude of 0.95 can become 0.6 after 10 layers. The thresholding can be relaxed by the `relax` param that is a weighted sum betwen the pre-threshold and post-threshold values.
96 objp_postiv : {'start_iter': null, 'step': 1, 'end_iter': null, 'relax': 0} # Apply a positivity constraint of the object phase, make it non-negative. This clips the negative values (anything below 0) so the object is visually darker but with stronger constrast, it's suggested to keep it on so that you can interpret, compare, and process your object phase with a simple baseline. Besides, the positivity constraint makes it easier to compare with atomic potential ground truth after correct scaling for the scattering factor. The clipping can be relaxed by the `relax` param that is a weighted sum betwen the pre-threshold and post-threshold values.
97 pos_recenter : {'start_iter': null, 'step': 1, 'end_iter': null, 'relax': 0} # Recenter the probe position shifts by making mean(probe_pos_shifts) = 0 so there's no global offset from the cropping position. This would keep the probe, probe position, and object relatively fixed in place even with large position learning rates.
98 tilt_smooth : {'start_iter': null, 'step': 1, 'end_iter': null, 'std': 2} # Apply a lateral Gaussian blur of the local object tilts in unit of "scan positions". This smoothens the local tilts so that you don't have drastic changes of object tilts between scan positions.
99
100recon_params:
101 NITER: 200 # type: int. Total number of reconstruction iterations. 1 iteration means a full pass of all selected diffraction patterns. Usually 20-50 iterations can get 90% of the work done with a proper learning rate between 1e-3 to 1e-4. For faster trials in hypertune mode, set 'NITER' to a smaller number than your typical reconstruction to save time. Usually 10-20 iterations are enough for the hypertune parameters to show their relative performance.
102 INDICES_MODE: {'mode': 'full', 'subscan_slow': null, 'subscan_fast': null} # type: dict. Indices mode determines multiple ways to use diffraction patterns at each probe positions for reconstructions. Each probe position (or each diffraction pattern) has a unique index, by selecting a subset of the indices, we can conveniently change the effective reconstruction area ('center') or effective scan step size or real space overlap ('sub'). You may choose 'full' for reconstructing with all probe positions, 'sub' for subsampling by selecting only every few probe positions with the full FOV (i.e., this will increase the effective scan step size and is a good way to test whether we can reduce the real space overlap), or 'center' for only using the center rectangular region in real space with a effectively reduced FOV but full sized object canvas. 'subscan_slow' and 'subscan_fast' determine the number of scan positions chosen for 'sub' and 'center', and have no effect to 'full'. If 'subscan_slow' and 'subscan_fast' are not provided (or null), they'll be set to half of the 'N_scan_slow' and half of the 'N_scan_fast' by default. Typically we can start from 'INDICES_MODE': {'mode': 'sub', 'subscan_slow: null, 'subscan_fast': null} to get an quick idea of the entire object by reconstructing the entire object FOV but using only every other diffraction pattern along fast and slow scan directions (so only 1/4 diffraction patterns are used, hence 4x speedup in the iteration time). Similarly we can use 'center' to keep the effective scan step size, but reconstruct a smaller FOV. Once the 'sub' or 'center' show reasonable results, we can then switch to 'full' to further refine it without starting from scratch because there's no object dimension mismatch between the 'INDICES_MODEs'.
103 BATCH_SIZE: {'size': 32, 'grad_accumulation': 1} # type: dict. Batch size is the number of diffraction patterns processed simultaneously to get the gradient update. 'size' is the number of diffraction pattern in a sub-batch, and 'grad_accumulation' is how many sub-batches' gradients are accumulated before applying the update. Effective batch size (for 1 update) is batch_size * grad_accumulation. Gradient accumulation is a ML technique that allows people to use large effective batch size by trading the iteration time with memory requirement, so if you can fit the entire batch inside your memory, you should always set 'grad_accumulation': 1 for performance. "Batch size" is commonly used in machine learning community, while it's called "grouping" in PtychoShelves. Batch size has an effect on both convergence speed and final quality, usually smaller batch size leads to better final quality for iterative gradient descent, but smaller batch size would also lead to longer computation time per iteration because the GPU isn't as utilized as large batch sizes (due to less GPU parallelism). On the other hand, large batch size is known to be more robust (noise-resilient) but converges slower. Generally batch size of 32 to 128 is used, although certain algorithms (like ePIE) would prefer a large batch size that is equal to the dataset size for robustness. For extremely large object (or with a lot of object modes), you'll need to reduce batch size to save GPU memory, or use `grad_accumulation` to split a batch into multiple sub-batches for 1 update.
104 GROUP_MODE: 'random' # type: str. Group mode determines the spatial distribution of the selected probe positions within a batch (group), this is similar to the 'MLs' for 'sparse' and 'MLc' for 'compact' in PtychoShelves. Available options are 'random', 'sparse', 'compact', 'hilbert', and 'fps'. Usually 'random' is good enough with small batch sizes and is the suggested option for most cases. 'compact' is believed to provide best final quality, although it's converging much slower. 'sparse' gives the most uniform coverage on the object so converges the fastest, and is also preferred for reconstructions with few scan positions to prevent any locally biased update. Since generating sparse grouping can be computationally expensive (a few minutes with 256x256 scan), PtyRAD provides 2 methods for sparse grouping, (1) 'fps' (Farthest Point Sampling) gives highest quality groups but is slower, or (2) 'hilbert' (Hilbert Curve Sorting) produces good quality groups within 0.5 sec and is fastest with O(N). These methods can be explictly chosen, or use 'sparse' to automatically select them based on the number of probe positions. 'fps' is chosen for datasets with less than 256x256 scan pattern. Note that PtychoShelves automatically switches from 'sparse' to 'random' for Nscans > 1e3. The grouping in PtyRAD is fixed during optimization, but the order between each group is shuffled for every iteration.
105 SAVE_ITERS: 10 # type: null or int. Number of completed iterations before saving the current reconstruction results (model, probe, object) and summary figures. If 'SAVE_ITERS' is 50, it'll create an output reconstruction folder and save the results and figures into it every 50 iterations. If null, the output reconstruction folder would not be created and no reconstruction results or summary figures would be saved. If 'SAVE_ITERS' > 'NITER', it'll create the output reconstruction folder but no results / figs would be saved. Typically we set 'SAVE_ITERS' to 50 for reconstruction mode with 'NITER' around 200 to 500. For hypertune mode, it's suggested to set 'SAVE_ITERS' to null and set 'collate_results' to true to save the disk space, while also provide an convenient way to check the hypertune performance by the collated results.
106 output_dir: 'output/tBL_WSe2/' # type str. Path and name of the main output directory. Ideally the 'output_dir' keeps a series of reconstruction of the same materials system or project. The PtyRAD results and figs will be saved into a reconstruction-specific folder under 'output_dir'. The 'output_dir' folder will be automatically created if it doesn't exist.
107 recon_dir_affixes: ['default'] # type: list of strings. This list specifies the optional affixes to the reconstruction folder name for file management. The order of strings has NO effect to the output folder name. PtyRAD provides high-level presets including 'minimal', 'default', and 'all', while each of them corresponds to a subset of all available options. There are currently 19 available options, including 'indices', 'meas', 'batch', 'pmode', 'omode', 'nlayer', 'lr', 'optimizer', 'start_iter', 'model', 'constraint', 'loss', 'conv_angle', 'aberrations', 'Ls', 'z_shift', 'dx', 'affine', and 'tilt'. Each option corresponds to specific fields in the params file. These individual tags can be combined with the presets, e.g. ['minimal', 'tilt']. A typical output folder name of 'default' looks like: 'ptyrad\demo\output\tBL_WSe2\20250607_full_N16384_dp128_flipT100_random32_p6_1obj_6slice_dz2_plr1e-4_oalr5e-4_oplr5e-4_slr1e-4_orblur0.5_ozblur1_mamp0.03_4_oathr0.98_oposc_sng1.0_spr0.1'. Note that certain trivial values might not be shown even it's specified, e.g. tilt of [0,0] mrad, slice thickness for single-slice ptychography, or start_iter = 1, etc. It's recommended to use 'default' for 'reconstruction' mode and adjust if needed. For 'hypertune' mode, you can set to [] (empty list) or ['minimal'] if you're considering saving intermediate results, because a unique identifier (trial number) would be appended and detail information are fully stored in the sqlite, and the hypertuned params are appended to the collated result anyway if you have 'collate_results': true.
108 prefix_time: 'date' # type: boolean, preset strings, and time format strings. Set to true to prepend a date str like '20240903_' in front of the reconstruction folder name, so that reconstruction with the same 'recon_dir_affixes' setting won't get incorrectly saved to the same output folder. Available options are None, True, False, 'date', 'time', 'datetime', and time format string like '%Y%m%d_%H%M%S'. Suggested value is 'date' for both 'reconstruction' and 'hypertune' modes. In hypertune mode, the date string would be applied on the hypertune folder instead of the reconsstruction folder. Also note that if you're using hypertune mode on multiple GPUs, you should set prefix_time to 'date' or False, and handle any additional identifier using 'prefix', otherwise different workers launched at different times would each generate their own output folder with different time strings despite using the same sqlite database file.
109 prefix: '' # type: str. Prefix this string to the reconstruction folder name. Note that "_" will be automatically generated, and the attached str would be after the time str if 'prefix_time' is true. In hypertune mode, the prefix string would be applied on the hypertune folder instead of the reconsstruction folder.
110 postfix: '' # type: str. Postfix this string to the reconstruction folder name. Note that "_" will be automatically generated. In hypertune mode, the postfix string would be applied on the hypertune folder instead of the reconsstruction folder.
111 save_result: ['model', 'objp'] # type: list of strings. This list specifies the available results to save every SAVE_ITERS, so it keeps the intermediate progress. Available options are 'model', 'obja', 'objp', 'probe', 'probe_prop', and 'optim_state'. 'model' is a nested dict that later got stored as an hdf5 file. 'model' contains optimizable tensors and metadata so that you can always refine from it and load whatever optimizable tensors (object, probe, positions, tilts) if you want to continue the reconstruction. It's similar to the NiterXXX.mat from PtychoShelves. 'object' and 'probe' output the reconstructed object and probe as '.tif'. If you don't want to save anything, set 'SAVE_ITERS' to null. Suggested setting is to save everything (i.e., ['model', 'obja', 'objp', 'probe']). For hypertune mode, you can set 'collate_results' to true and set 'SAVE_ITERS' to null to disable result saving.
112 result_modes: {'obj_dim': [2, 3, 4], 'FOV': ['crop'], 'bit': ['8']} # type: dict. This dict specifies which object output is saved by their final dimension ('obj_dim'), whether to save the full or cropped FOV ('FOV') of object, and whether to save the raw or normalized bit depth version of object and probe. A comprehensive (but probably redundant) saving option looks like {'obj_dim': [2,3,4], 'FOV': ['full', 'crop'], 'bit': ['raw', '32', '16', '8']}. 'obj_dim' takes a list of int, the int ranges between 2 to 4, corresponding to 2D to 4D object output. Set 'obj_dim': [2] if you only want the zsum from multislice ptychography. Suggested value is [2,3,4] to save all possible output. 'FOV' takes a list of strings, the available strings are either 'full' or 'crop'. Suggested value is 'crop' so the lateral padded region of object is not saved. 'bit' takes a list of strings, the available strings are 'raw', '32', '16', and '8'. 'raw' is the original value range, while '32' normalizes the value from 0 to 1. '16' and '8' will normalize the value from 0 to 65535 and 255 correspondingly. Defualt is '8' to save only the normalized 8bit result for quick visualization. You can set it to ['raw', '8'] if you want to keep the original float32 bit results with normalized 8bit results. These postprocessing would postfix corresponding labels to the result files.
113 selected_figs: ['convergence', 'forward', 'probe_r_amp', 'pos'] # type: list of strings. This list specified the selected figures that will be plotted/saved. The suggested value is ['convergence', 'forward', 'probe_r_amp', 'pos']. The available strings are 'convergence', 'convergence_dynamic', 'loss', 'forward', 'probe_r_amp', 'probe_k_amp', 'probe_k_phase', 'pos', 'tilt', 'tilt_avg', 'slice_thickness', and 'all'. 'convergence' (alias 'convergence_full') shows the full iteration history from iter 0 → summary_convergence.png; 'convergence_dynamic' uses per-panel Kneedle zoom to the most informative x-range for inspecting the long tail → summary_convergence_dynamic.png.
114 copy_params: true # type: boolean. Set to true if you want to copy the .yml params file to the hypertune folder (hypertune mode) or individual reconstruction folders (reconsturction mode). Suggested value is true for better record keeping, although most information is saved in model.pt and can be loaded by ckpt = torch.load('model.pt'), params = ckpt['params'].
115 compiler_configs: {'enable': false} # type: null or dict. This dict specifies the PyTorch JIT compiler configurations. Set to {'enable': true} to enable PyTorch JIT compilation for a 1.3-1.9x speedup on supported hardware. See https://docs.pytorch.org/docs/stable/generated/torch.compile.html for more details.