Bohrium
robot
新建

空间站广场

论文
Notebooks
比赛
课程
Apps
我的主页
我的Notebooks
我的论文库
我的足迹

我的工作空间

任务
节点
文件
数据集
镜像
项目
数据库
公开
ACOM of polycrystalline WS2 thin film - disk detection
AI4S
py4dstem
STEM
AI4Spy4dstemSTEM
Linfeng Zhang
发布于 2023-09-18
推荐镜像 :py4dstem:py4dstem
推荐机型 :c2_m4_cpu
ACOM of polycrystalline WS2 thin film - disk detection
Data
pymatgen
Acknowledgements
Load data
Virtual imaging
Probe template
Bragg disk detection
Centering and calibration
Pixel size calibration
Automated crystal orientation mapping (ACOM)
Strain maps

ACOM of polycrystalline WS2 thin film - disk detection

This notebook performs disk detection for the AuAgPd nanowire 4D-STEM bullseye experiment. The data has been downsampled for the purposes of the tutorial.

Our goal is to perform automated crystal orientation mapping (ACOM), as described in:
Automated Crystal Orientation Mapping in py4DSTEM using Sparse Correlation Matching

Data

polycrystal_2D_WS2.h5 (1.0 GB)

WS2.cif (2 kB)

This dataset is a simulation of a polycrystalline WS2 thin film, where each grain has an [0001] zone axis orientation, and a random in-plane rotation. Each grain has been randomly strained by -2%, -1%, 0%, 1%, or 2%, for e_xx, e_yy, and e_xy. This makes checking the output strain maps easier, as each grain has an exact multiple of 1% strain for each value in the strain tensor. The dataset for this tutorial has also been downsampled in both real and diffraction space to make a small example.

pymatgen

This notebook requires that pymatgen is installed.

Acknowledgements

This tutorial was created by the py4DSTEM instructor team:

代码
文本
[1]
import py4DSTEM
import numpy as np
print(py4DSTEM.__version__)
0.14.3
代码
文本
[2]
#dirpath = "data/"
# dirpath = "/Users/Ben/work/data/py4dstem_sampledata/ACOM_2D_WS2/"
dirpath = "/media/cophus/DataSSD1/2023_User_Meeting_Workshop/"

filepath_data = dirpath + 'polycrystal_2D_WS2.h5'
filepath_analysis = dirpath + 'polycrystal_2D_WS2_analysis_'
filepath_cif = dirpath + 'WS2.cif'
代码
文本

Load data

代码
文本
[3]
py4DSTEM.print_h5_tree(filepath_data)
/
|---4DSTEM
    |---datacube
        |---calibration


代码
文本
[4]
# Load the datacubes using py4DSTEM
dataset = py4DSTEM.read(
filepath_data,
root='4DSTEM/datacube',
)
代码
文本

Virtual imaging

代码
文本
[5]
dataset.get_dp_max()
dataset.get_dp_mean()
VirtualDiffraction( A 2-dimensional array of shape (128, 128) called 'dp_mean',
                    with dimensions:

                        dim0 = [0,1,2,...] pixels
                        dim1 = [0,1,2,...] pixels
)
代码
文本
[6]
py4DSTEM.visualize.show(
dataset.tree('dp_max'),
figsize = (4,4),
ticks = False,
)
代码
文本
[7]
py4DSTEM.show(
[
dataset.data[20,20],
dataset.data[90,20],
dataset.data[120,20],
dataset.data[20,70],
dataset.data[50,70],
dataset.data[90,70],
dataset.data[20,110],
dataset.data[70,110],
dataset.data[110,110],
],
combine_images=True,
figsize = (4,4),
)
代码
文本

We can see the polycrystalline grain structure of the WS2 in the maximum diffraction pattern. Many diffraction rings are present, with Bragg peaks at many different rotations along these rings.

代码
文本
[8]
# Estimate the radius of the BF disk, and the center coordinates
probe_semiangle, probe_qx0, probe_qy0 = dataset.get_probe_size(
dataset.tree('dp_mean').data,
)
center = (probe_qx0, probe_qy0)

# plot the mean diffraction pattern, with the estimated probe radius overlaid as a circle
fig, ax = py4DSTEM.show(
dataset.tree('dp_mean'),
figsize=(4,4),
circle = {
'center': center,
'R': probe_semiangle,
},
ticks = False,
returnfig = True,
vmax = 1,
);
# ax.set_xlim([57, 70]);
# ax.set_ylim([70, 57]);

# Print the estimate probe radius
print('Estimated probe radius =', '%.2f' % probe_semiangle, 'pixels')
代码
文本
[9]
# Create a virtual annular dark field (ADF) image around the first diffraction ring

radii = (13,24)

# Plot the ADF detector
dataset.position_detector(
mode = 'annular',
geometry = (
center,
radii
),
figsize = (4,4),
ticks = False,
)

# Calculate the ADF image
dataset.get_virtual_image(
mode = 'annulus',
geometry = (center,radii),
name = 'dark_field',
)

# Plot the ADF image
py4DSTEM.show(
dataset.tree('dark_field'),
figsize = (4,4),
)
代码
文本

Two kinds of contrast changes are visible in the virtual dark field image:

  • The grain boundaries show up as bright lines bordering the polycrystalline grains.
  • Some grains appear brighter or darker - this is because the applied strain fields increase or decrease the atomic density of the grains, leading to more or less diffraction into the annular dark field region.
代码
文本

Probe template

代码
文本
[10]
# Because the diffracted signal is so weak, we will just average a subset of the probe positions.
rx_min, rx_max = 10, 30
ry_min, ry_max = 10, 30

probe = dataset.get_vacuum_probe(
ROI=(rx_min, rx_max, ry_min, ry_max),
#mask,
#align=True,
)


100%|████████████████████████████████████████████████████████████████████████████████| 399/399 [00:00<00:00, 607.87it/s]
代码
文本
[11]
# Subtract a normalization function to give the probe a mean value of zero
probe.get_kernel(
mode = 'sigmoid',
radii = (probe_semiangle * 0.0, probe_semiangle * 4.0),
bilinear=True,
)

# Plot the probe kernel
py4DSTEM.visualize.show_kernel(
probe.kernel,
R=10, L=10, W=1,
figsize = (8,4),
)
代码
文本

Bragg disk detection

代码
文本
[12]
# Test parameters on a few probe positions
# Visualize the diffraction patterns and the located disk positions

rxs = 32,96,96
rys = 32,32,96
colors=['r','limegreen','c']


# parameters
detect_params = {
'corrPower': 1.0,
'sigma': 0,
'edgeBoundary': 4,
'minRelativeIntensity': 0,
'minAbsoluteIntensity': 1e-7,
'minPeakSpacing': 12,
'subpixel' : 'poly',
# 'subpixel' : 'multicorr',
'upsample_factor': 8,
'maxNumPeaks': 1000,
# 'CUDA': True,
}

disks_selected = dataset.find_Bragg_disks(
data = (rxs, rys),
template = probe.kernel,
**detect_params,
)

py4DSTEM.visualize.show_image_grid(
get_ar = lambda i:dataset.data[rxs[i],rys[i],:,:],
H=1,
W=3,
axsize=(3,3),
get_bordercolor = lambda i:colors[i],
get_x = lambda i: disks_selected[i].data['qx'],
get_y = lambda i: disks_selected[i].data['qy'],
get_pointcolors = lambda i: colors[i],
open_circles = True,
scale = 300,
)
代码
文本
[13]
# Find Bragg peaks for all probe positions
bragg_peaks = dataset.find_Bragg_disks(
template = probe.kernel,
**detect_params,
)
Finding Bragg Disks: 100%|██████████████████████████████████████████████████████████| 16.4k/16.4k [00:33<00:00, 485DP/s]
代码
文本

Centering and calibration

代码
文本
[14]
# Compute the origin position for all probe positions by finding and then fitting the center beam

# measure origins
qxy_origins = bragg_peaks.measure_origin(
# mode = 'no_beamstop',
)

# fit a plane to the origins
qx0_fit,qy0_fit,qx0_residuals,qy0_residuals = bragg_peaks.fit_origin(
# plot_range=0.1,
figsize = (4,4)
)
代码
文本
[15]
# Calculate BVM from centered data
bragg_vector_map_centered = bragg_peaks.get_bvm()

py4DSTEM.show(
bragg_vector_map_centered,
figsize = (4,4),
)
代码
文本

Pixel size calibration

代码
文本
[16]
# Calculate and plot the radial integral.
# Note that for a 2D material, the center beam is orders of magnitude higher than the diffracted beam.
# Thus we need to specify a maximum y value for the plot.
# We also scale the plotted intensity by q to better show the higher angle peaks.
ymax = 30

q, intensity_radial = py4DSTEM.process.utils.radial_integral(
bragg_vector_map_centered,
)

py4DSTEM.visualize.show_qprofile(
q = q,
intensity = intensity_radial * q,
ymax = ymax,
)
代码
文本
[17]
# Load the WS2 crystal file
crystal = py4DSTEM.process.diffraction.Crystal.from_CIF(filepath_cif)
/home/cophus/anaconda3/envs/py4dstem/lib/python3.11/site-packages/pymatgen/io/cif.py:1145: UserWarning: Issues encountered while parsing CIF: Some fractional coordinates rounded to ideal values to avoid issues with finite precision.
  warnings.warn("Issues encountered while parsing CIF: " + "\n".join(self.warnings))
代码
文本
[18]
# Calculate structure factors
k_max = 1.4

crystal.calculate_structure_factors(
k_max,
)
代码
文本
[19]
crystal.plot_structure(
zone_axis_lattice=(1,1,0.1),
figsize=(6,3),
camera_dist = 6,
)
代码
文本
[20]
# Test different pixel sizes, and overlay the structure factors onto the experimental data.
# Note that we will use our knowledge that the WS2 has an [0001] zone axis.

inv_Ang_per_pixel = 0.0192

q_SF = np.linspace(0,k_max,250)
I_SF = np.zeros_like(q_SF)
for a0 in range(crystal.g_vec_leng.shape[0]):
if np.abs(crystal.g_vec_all[2,a0]) < 0.01:
idx = np.argmin(np.abs(q_SF-crystal.g_vec_leng[a0]))
I_SF[idx] += crystal.struct_factors_int[a0]
I_SF /= np.max(I_SF)

fig,ax = py4DSTEM.visualize.show_qprofile(
q=q*inv_Ang_per_pixel,
intensity=intensity_radial*q,
xlabel='q (1/Ang)',
returnfig=True,
ymax=ymax,
)

ax.plot(q_SF,I_SF*ymax,c='r')
ax.set_xlim([0, k_max])
代码
文本
[21]
# Apply pixel size calibration
bragg_peaks.calibration.set_Q_pixel_size(inv_Ang_per_pixel)
bragg_peaks.calibration.set_Q_pixel_units('A^-1')
代码
文本
[22]
bragg_peaks.calstate
{'center': True, 'ellipse': False, 'pixel': True, 'rotate': False}
代码
文本
[23]
# Save calibrated Bragg peaks
filepath_braggdisks_cal = filepath_analysis + 'braggdisks_cal.h5'
py4DSTEM.save(
filepath_braggdisks_cal,
bragg_peaks,
mode='o',
)
100%|███████████████████████████████████████████████████████████████████████████| 16384/16384 [00:01<00:00, 8928.09it/s]
代码
文本

Automated crystal orientation mapping (ACOM)

代码
文本
[24]
# Reload Bragg peaks if needed
filepath_braggdisks_cal = filepath_analysis + 'braggdisks_cal.h5'
py4DSTEM.print_h5_tree(filepath_braggdisks_cal)
/
|---datacube_root
    |---braggvectors
        |---_v_uncal


代码
文本
[25]
# Reload bragg peaks cif file, recompute structure factors
bragg_peaks = py4DSTEM.read(
filepath_braggdisks_cal,
)
bragg_peaks

k_max = 1.4
crystal = py4DSTEM.process.diffraction.Crystal.from_CIF(filepath_cif)
crystal.calculate_structure_factors(
k_max,
)
Reading PointListArray: 100%|████████████████████████████████████████████| 16384/16384 [00:02<00:00, 6017.15PointList/s]
/home/cophus/anaconda3/envs/py4dstem/lib/python3.11/site-packages/pymatgen/io/cif.py:1145: UserWarning: Issues encountered while parsing CIF: Some fractional coordinates rounded to ideal values to avoid issues with finite precision.
  warnings.warn("Issues encountered while parsing CIF: " + "\n".join(self.warnings))
代码
文本
[26]
# Create an orientation plan for [0001] WS2
crystal.orientation_plan(
angle_step_zone_axis = 1.0,
angle_step_in_plane = 4.0,
zone_axis_range = 'fiber',
fiber_axis = [0,0,1],
fiber_angles = [0,0],
# CUDA=True,
)
Orientation plan: 100%|██████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 327.02 zone axes/s]
代码
文本
[27]
# Test matching on some probe positions
# xind, yind = 64,64
xind, yind= 32,96

orientation = crystal.match_single_pattern(
bragg_peaks.cal[xind,yind],
# plot_corr = True,
# plot_polar = False,
verbose = True,
)

sigma_compare = 0.03
range_plot = np.array([k_max+0.1,k_max+0.1])

bragg_peaks_fit = crystal.generate_diffraction_pattern(
orientation,
ind_orientation=0,
sigma_excitation_error=sigma_compare)


# plot comparisons
py4DSTEM.process.diffraction.plot_diffraction_pattern(
bragg_peaks_fit,
bragg_peaks_compare=bragg_peaks.cal[xind,yind],
scale_markers=1000,
scale_markers_compare=4e4,
plot_range_kx_ky=range_plot,
min_marker_size=1,
figsize = (5,5),
)
代码
文本
[28]
# Fit orientation to all probe positions
orientation_map = crystal.match_orientations(
bragg_peaks,
)
/home/cophus/anaconda3/envs/py4dstem/lib/python3.11/site-packages/py4DSTEM/process/diffraction/crystal_ACOM.py:776: UserWarning: Warning: bragg peaks not elliptically calibrated
  warn('Warning: bragg peaks not elliptically calibrated')
/home/cophus/anaconda3/envs/py4dstem/lib/python3.11/site-packages/py4DSTEM/process/diffraction/crystal_ACOM.py:781: UserWarning: bragg peaks not rotationally calibrated
  warn('bragg peaks not rotationally calibrated')
Matching Orientations: 100%|█████████████████████████████████████████████| 16384/16384 [00:49<00:00, 330.10 PointList/s]
代码
文本
[29]
# Plot the orienations
images_orientation = crystal.plot_fiber_orientation_maps(
orientation_map,
symmetry_order = 6,
corr_range = [0.9, 1.0],
figsize = (4,4),
)
代码
文本

Strain maps

For each diffraction pattern, we have both the measured diffraction pattern Bragg peaks, and the Bragg peaks calculated from the best-fit orientation. We can therefore directly calculate a strain map, just by measuring the best-fit transformation tensor between these two sets of peaks.

代码
文本
[30]
strain_map = crystal.calculate_strain(
bragg_peaks,
orientation_map,
rotation_range=np.pi/3, # 60 degrees
# corr_kernel_size=0.02,
)
/home/cophus/anaconda3/envs/py4dstem/lib/python3.11/site-packages/py4DSTEM/process/diffraction/crystal_ACOM.py:1667: UserWarning: bragg peaks not elliptically calibrated
  warn('bragg peaks not elliptically calibrated')
/home/cophus/anaconda3/envs/py4dstem/lib/python3.11/site-packages/py4DSTEM/process/diffraction/crystal_ACOM.py:1672: UserWarning: bragg peaks not rotationally calibrated
  warn('bragg peaks not rotationally calibrated')
Calculating strains: 100%|██████████████████████████████████████████████| 16384/16384 [00:11<00:00, 1386.36 PointList/s]
代码
文本
[31]
# plot the 4 components of the strain tensor
fig,ax = py4DSTEM.visualize.show_strain(
strain_map,
vrange_exx=[-3.0, 3.0],
vrange_theta=[0.0, 60.0],
ticknumber=3,
axes_plots=(),
bkgrd=False,
figsize=(6,6),
# cmap='hsv',
returnfig=True
)
代码
文本
[ ]

代码
文本
AI4S
py4dstem
STEM
AI4Spy4dstemSTEM
点个赞吧
本文被以下合集收录
py4DSTEM Tutorials
Linfeng Zhang
更新于 2024-07-25
22 篇7 人关注
推荐阅读
公开
ACOM of AuAgPd nanowires - disk detection
AI4Spy4dstemSTEM
AI4Spy4dstemSTEM
Linfeng Zhang
发布于 2023-09-18
公开
ptycho03_Si-multislice: Phase contrast imaging using multislice ptychography
AI4Spy4dstemSTEM
AI4Spy4dstemSTEM
Linfeng Zhang
发布于 2023-09-18
1 转存文件