Atom-position analysis using Atomap

Supplementary information to the paper: "Structural and chemical properties of superconducting Co-doped BaFe$_2$As$_2$ thin films grown on CaF$_2$"

This notebook was run with the HyperSpy (1.5.2) and Atomap (0.2.0) python packages. For information on HyperSpy/Atomap you can visit the websites: https://hyperspy.org/ and https://atomap.org/

Information about the usage of Jupyter notebooks and how to run this notebook interactively: https://jupyter.org/. Put the ".ipynb" file and the image "StackingFault_cropped.dm3" in the same folder.

Load the necessary packages:

In [1]:
%matplotlib nbagg
#%matplotlib qt #can be used alternatively

import hyperspy.api as hs
import atomap.api as am
import numpy as np
import matplotlib.pyplot as plt

Load and display the image:

In [2]:
s = hs.load('StackingFault_cropped.dm3')
s.plot()

We extract and save the calibration for later:

In [3]:
calib = s.original_metadata.ImageList.TagGroup0.ImageData.Calibrations.Dimension.TagGroup0.Scale #in nm
print('Pixelsize in nm: ', calib)
Pixelsize in nm:  0.029555000364780426

Find bright Ba columns by feature separation. Click into the "Navigator" window and use left/right arrow keys to find good estimate for the separation value.

In [4]:
s_peaks = am.get_feature_separation(s, separation_range=(2, 15))
s_peaks.plot()
100%|█████████████████████████████████████████████████████████████████████████████████| 13/13 [00:00<00:00, 104.07it/s]
100%|██████████████████████████████████████████████████████████████████████████████| 714/714 [00:00<00:00, 3292.63it/s]

Here a separation value of 9 finds most of the Ba columns. The rest is done by manual adding/removing with mouse clicks.

In [5]:
atom_positions = am.get_atom_positions(s, separation=9)
In [6]:
#Add/remove missing/false Ba atoms by mouse clicks
atom_positions_new = am.add_atoms_with_gui(s, atom_positions)

Save raw positions as a sublattice:

In [7]:
sublattice_Ba = am.Sublattice(atom_positions_new, s)
In [8]:
sublattice_Ba.plot()

Now refine positions by (1) search for center of mass and (2) subsequential 2-dimensional Gaussian fitting:

In [9]:
sublattice_Ba.find_nearest_neighbors()
sublattice_Ba.refine_atom_positions_using_center_of_mass()
sublattice_Ba.refine_atom_positions_using_2d_gaussian()
Center of mass: 100%|██████████████████████████████████████████████████████████████| 264/264 [00:00<00:00, 6443.61it/s]
Gaussian fitting: 100%|██████████████████████████████████████████████████████████████| 264/264 [00:05<00:00, 46.40it/s]

Inspect final position of fitted Ba column locations:

  • Position 1 of slider: After center of mass
  • Position 2 of slider: After Gaussian fitting
In [10]:
sublattice_Ba.get_position_history().plot()
100%|██████████████████████████████████████████████████████████████████████████████| 264/264 [00:00<00:00, 7338.87it/s]

Now we search and construct zone axes:

In [11]:
sublattice_Ba.construct_zone_axes()

Inspect the zone axes:

In [12]:
sublattice_Ba.plot_planes() 

For measuring the $c$ lattice parameter, we are interested in the zone axis with index 0, i.e. horizontal Ba planes. In the next step, the distances between the Ba monolayers are evaluated. We sort the data to start from the bottom, i.e. starting from the plane with index 12.

In [13]:
zone = sublattice_Ba.zones_axis_average_distances[0]
plane = sublattice_Ba.atom_planes_by_zone_vector[zone][12]
s_monolayer_line = sublattice_Ba.get_monolayer_distance_line_profile(zone, plane)

#Save data to arrays
position = s_monolayer_line.metadata.line_profile_data.x_list
value = s_monolayer_line.metadata.line_profile_data.y_list
standard_deviation = s_monolayer_line.metadata.line_profile_data.std_list
WARNING:root:
The mean monolayer separation and distance to the first                 
monolayer deviate with more than 10 %. Consider if there                 
are too many outliers

Optional: Save sublattice for later use.

In [14]:
atom_lattice = am.Atom_Lattice(sublattice_list=[sublattice_Ba], image=s.data, name="Ba columns")
atom_lattice.save("BaColumns_AtomapAtomLattice.hdf5", overwrite=True)

Data evaluation examples

Plot and inspect the measured $c/2$ Ba-plane spacings:

In [15]:
fig, ax = plt.subplots()
_ = ax.set_title('Ba-plane spacing in vertical direction')
_ = ax.errorbar(position*calib, value*calib, yerr=standard_deviation*calib, ecolor='red')
_ = ax.set(xlabel='Distance / nm', ylabel='$c/2$ lattice parameter')
fig.show()

The Ba-plane distance at the stacking fault (SF) and the adjacent Ba planes clearly deviate from the other values. The distances were evaluated in pixels and can be calibrated by multiplication with the pixelsize ($calib$).

In [16]:
value #Ba-plane separation in pixels
Out[16]:
array([22.90429084, 23.05499405, 22.93913868, 22.9400597 , 22.94389444,
       23.87228446, 12.63766527, 23.90013218, 23.01435575, 22.84208798,
       22.63917979, 23.01523088])
In [21]:
print('Ba-plane spacing at the stacking fault: (', np.around(value[6]*calib,3), '+/-', np.around(standard_deviation[6]*calib,3), ') nm')
Ba-plane spacing at the stacking fault: ( 0.374 +/- 0.017 ) nm

Find (weighted) mean value for $c/2$ from surrounding Ba122 matrix, i.e. without the stacking fault and the adjacent Ba planes (indices 5,6, and 7):

In [18]:
outliers = [5,6,7]
indices = np.setdiff1d(np.arange(len(value)), outliers)
print(indices)
[ 0  1  2  3  4  8  9 10 11]

Calculate weighted (arithmetic) mean with standard deviations as weights:

In [19]:
wm = np.average(value[indices], weights=standard_deviation[indices])
wm_err = 1/np.sqrt(np.sum(standard_deviation[indices])) #standard error for weighted mean
print('Weighted mean for c/2 from Ba122 matrix: (', np.around(wm*calib,3), '+/-', np.around(wm_err*calib,3), ') nm')
Weighted mean for c/2 from Ba122 matrix: ( 0.678 +/- 0.016 ) nm

Enlargement of $c/2$ of Ba planes adjacent to SF relative to matrix:

In [22]:
wm_adj = np.average(value[[5,7]], weights=standard_deviation[[5,7]])
wm_adj_err = 1/np.sqrt(np.sum(standard_deviation[[5,7]])) #standard error for weighted mean
In [23]:
print('Weighted mean for c/2 from planes adjacent to stacking fault: (', np.around(wm_adj*calib,3), '+/-', np.around(wm_adj_err *calib,3), ') nm')
print('Enlargement of c/2 relative to matrix: ', np.around((wm_adj-wm)/wm*100,1) ,'%')
Weighted mean for c/2 from planes adjacent to stacking fault: ( 0.706 +/- 0.026 ) nm
Enlargement of c/2 relative to matrix:  4.2 %

Optional: Save data to txt-file.

In [24]:
data = np.vstack((position*calib, value*calib, standard_deviation*calib, position, value, standard_deviation))
np.savetxt('data_StackingFault.txt', np.transpose(data), delimiter ='\t')