Plotting for publications
Please consider citing Atomap: a new software tool for the automated analysis of atomic resolution images using two-dimensional Gaussian fitting if you publish work where you have used Atomap as a tool.
Figures for publications are often customized, and here are a few tips on how to extract the data you wish to plot in a fancy plot.
Saving specific data
When making advanced figures containing specific data for publication, it can be a good idea to save this data for example in separate numpy files. This makes it quick to load the data when using for example matplotlib to make figures.
>>> import numpy as np
>>> np.savez("datafile.npz", x=sublattice_A.x_position, y=sublattice_A.y_position, e=sublattice_A.ellipticity)
Alternatively, the data can be saved in comma-separated values (CSV) file, which can be opened in spreadsheet software:
>>> np.savetxt("datafile.csv", (sublattice_A.x_position, sublattice_A.y_position, sublattice_A.sigma_x, sublattice_A.sigma_y, sublattice_A.ellipticity), delimiter=',')
Signals can be saved by using the inbuilt save function.
>>> s_monolayer.save("monolayer_distances.hdf5", overwrite=True)
Here, we will first save analysis data for the fantasite dummy data atom lattice. It can be a good idea to save analysis results as numpy, csv or hyperspy signals, as the runtime of the analysis can sometimes be lengthy. Making nice figures often require a lot of tweaking, trial and error, so it is nice to have the data readily available without having to re-run the full analysis. First, the analysis results are generated, and then saved.
import os
import numpy as np
from hyperspy.signals import Signal2D
from atomap.dummy_data import get_fantasite_atom_lattice
my_path = os.path.join(os.path.dirname(__file__), 'make_nice_figures')
if not os.path.exists(my_path):
os.makedirs(my_path)
# First we will analyse and save the structural data of interest
# Here, we use the fantasite atom_lattice dummy data
atom_lattice = get_fantasite_atom_lattice()
# Saving atom positions and ellipticity
sublattice_A = atom_lattice.sublattice_list[0]
np.savez(
os.path.join(my_path, 'sublattice_A.npz'), x=sublattice_A.x_position,
y=sublattice_A.y_position, e=sublattice_A.ellipticity)
sublattice_B = atom_lattice.sublattice_list[1]
np.savez(
os.path.join(my_path, 'sublattice_B.npz'), x=sublattice_B.x_position,
y=sublattice_B.y_position, e=sublattice_B.ellipticity)
# Saving distance difference map
sublattice_A.construct_zone_axes()
zone = sublattice_A.zones_axis_average_distances[0]
s_dd = sublattice_A.get_atom_distance_difference_map([zone])
s_dd.save(os.path.join(my_path, 'distance_difference_map.hspy'),
overwrite=True)
# Saving the synthetic ADF-image.
im = atom_lattice.image
s_adf = Signal2D(im)
s_adf.save(os.path.join(my_path, 'ADF_image.hspy'), overwrite=True)
# Saving the line profile
z1 = sublattice_A.zones_axis_average_distances[0]
z2 = sublattice_A.zones_axis_average_distances[1]
plane = sublattice_A.atom_planes_by_zone_vector[z2][23]
s_dd_line = sublattice_A.get_atom_distance_difference_line_profile(z1, plane)
s_dd_line.save(os.path.join(my_path, 'dd_line.hspy'), overwrite=True)
Matplotlib
The saved results can then be loaded into the script that is making nice figures. The below code block will create this figure.
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from mpl_toolkits.axes_grid1.anchored_artists import AnchoredSizeBar
import matplotlib.font_manager as fm
import matplotlib.patheffects as patheffects
import hyperspy.api as hs
my_path = os.path.join(os.path.dirname(__file__), 'make_nice_figures')
if not os.path.exists(my_path):
os.makedirs(my_path)
# Load the atomic resolution image
s_adf = hs.load(os.path.join(my_path, 'ADF_image.hspy'))
# Load the structural data
atoms_A = np.load(os.path.join(my_path, 'sublattice_A.npz'))
atoms_B = np.load(os.path.join(my_path, 'sublattice_B.npz'))
dd_map = hs.load(os.path.join(my_path, 'distance_difference_map.hspy'))
dd_line = hs.load(os.path.join(my_path, 'dd_line.hspy'))
# Scaling the data
scale = 0.142
s_adf.axes_manager[0].scale = scale
s_adf.axes_manager[1].scale = scale
# dd_map has twice the amount of pixels, so the scale is half
dd_map.axes_manager[0].scale = scale/2
dd_map.axes_manager[1].scale = scale/2
# Crop images
s_adf = s_adf.isig[40:460, 40:460]
dd_map = dd_map.isig[80:920, 80:920]
# Make a figure with 3 sub-figures, of difference sizes
fig = plt.figure(figsize=(4.3, 2)) # in inches
gs = gridspec.GridSpec(1, 5)
ax_adf = plt.subplot(gs[:2])
ax_al = plt.subplot(gs[2:4])
ax_lp = plt.subplot(gs[4])
# Plot ADF-image
cax_adf = ax_adf.imshow(
np.rot90(s_adf.data), interpolation='nearest',
origin='upper', extent=s_adf.axes_manager.signal_extent)
# Make scalebar on ADF-image
fontprops = fm.FontProperties(size=12)
scalebar0 = AnchoredSizeBar(
ax_adf.transData,
20, '2 nm', 4,
pad=0.1,
color='white',
frameon=False,
label_top=True,
size_vertical=2,
fontproperties=fontprops)
# The next line is needed due to a bug in matplotlib 2.0
scalebar0.size_bar.get_children()[0].fill = True
ax_adf.add_artist(scalebar0)
# Add markers for atom positions
for idx, x in enumerate(atoms_A['x']):
y = atoms_A['y'][idx]
if (240 < x < 350) and (96 < y < 200):
ax_adf.scatter(y*scale, x*scale, color='r', s=0.5)
for idx, x in enumerate(atoms_B['x']):
y = atoms_B['y'][idx]
if (240 < x < 350) and (96 < y < 200):
ax_adf.scatter(y*scale, x*scale, color='b', s=0.5)
# Plot distance difference map
cax_al = ax_al.imshow(
np.rot90(dd_map.data),
interpolation='nearest',
origin='upper',
extent=dd_map.axes_manager.signal_extent,
cmap='viridis')
scalebar1 = AnchoredSizeBar(
ax_al.transData,
20, '2 nm', 4,
pad=0.1,
color='white',
frameon=False,
label_top=True,
size_vertical=2,
fontproperties=fontprops)
# The next line is needed due to a bug in matplotlib 2.0
scalebar1.size_bar.get_children()[0].fill = True
ax_al.add_artist(scalebar1)
# Remove ticks for images
for ax in [ax_adf, ax_al]:
ax.set_xticks([])
ax.set_yticks([])
# Plot line profile
x_line_profile = dd_line.metadata.line_profile_data.x_list*scale/10
y_line_profile = dd_line.metadata.line_profile_data.y_list*scale
ax_lp.plot(y_line_profile, x_line_profile)
ax_lp.set_xlabel("Distance difference, [Å]", fontsize=7)
ax_lp.set_ylabel("Distance from interface, [nm]", fontsize=7)
ax_lp.tick_params(axis='both', which='major', labelsize=6)
ax_lp.tick_params(axis='both', which='minor', labelsize=6)
ax_lp.yaxis.set_label_position('right')
ax_lp.yaxis.set_ticks_position('right')
ax_lp.set_ylim(-1.5, 4.5)
# Add annotation
path_effects = [
patheffects.withStroke(linewidth=2, foreground='black',
capstyle="round")]
ax_adf.text(
0.015, 0.90, "a", fontsize=12, color='white',
path_effects=path_effects,
transform=ax_adf.transAxes)
ax_al.text(
0.015, 0.90, "b", fontsize=12, color='white',
path_effects=path_effects,
transform=ax_al.transAxes)
ax_lp.text(
0.05, 0.90, "c", fontsize=12, color='w',
path_effects=path_effects,
transform=ax_lp.transAxes)
# Adjust space between subplots and margins
gs.update(left=0.01, wspace=0.05, top=0.95, bottom=0.2, right=0.89)
# Save
fig.savefig(os.path.join(my_path, 'Atom_lattice.png'), dpi=300)