Scripted data collection¶
Instamatic is a tool for automated electron diffraction data collection. It has interfaces for interfacing with the TEM (JEOL/TFS) and several cameras (ASI Timepix/Cheetah/TVIPS/Merlin).
This notebook shows some interactive examples of what basic data collection scripts may look like.
First, we must import and initialize the controller object, which defines the interface to the TEM and the camera. For this example, the microscope interaction is simulated.
from instamatic import TEMController
from instamatic.formats import write_tiff
ctrl = TEMController.initialize()
Config directory: C:\Users\Stef\AppData\Roaming\instamatic\config Microscope: simulate (server) Starting TEM server (localhost:8088 on pid=21424) Connected to TEM server (localhost:8088) Camera : simulate (stream) Mode: mag1 High tension: 200 kV Current density: 98601.82 pA/cm2 GunShift(x=48458, y=42062) GunTilt(x=40862, y=60491) BeamShift(x=41254, y=44050) BeamTilt(x=27402, y=50643) ImageShift1(x=29417, y=48175) ImageShift2(x=50884, y=24678) DiffShift(x=46599, y=49096) Stage(x=15844.0, y=-14350.0, z=-6974.0, a=14.0, b=32.0) Magnification(value=25000, index=10) DiffFocus(value=n/a) Brightness(value=10064) SpotSize(1) Saved alignments: ('neutral.yaml',)
Discrete rotation electron diffraction¶
This is an example script to collect electron diffraction data with discrete rotation steps with steps of 10° from -60 to 60°. The data are saved in a tiff file.
exposure = 0.5 # s
angles = range(-50, 51, 10)
for i, angle in enumerate(angles):
ctrl.stage.set(a=angle)
print(f"Frame {i} @ {ctrl.stage.a:.1f}°")
img, h = ctrl.get_image(exposure=exposure)
write_tiff(f"red_{i:04d}.tiff", img, header=h)
Frame 0 @ -50.0° Frame 1 @ -40.0° Frame 2 @ -30.0° Frame 3 @ -20.0° Frame 4 @ -10.0° Frame 5 @ 0.0° Frame 6 @ 10.0° Frame 7 @ 20.0° Frame 8 @ 30.0° Frame 9 @ 40.0° Frame 10 @ 50.0°
We can show the last image, but they are not very interesting, because they are simulated as noise :-)
%matplotlib inline
import matplotlib.pyplot as plt
plt.imshow(img)
<matplotlib.image.AxesImage at 0x178c518a880>
Continuous rotation electron diffraction¶
This is an example of what data collection would look like with a continuous rotation. The difference is that the target angle is set using wait=False
. This means that the call is non-blocking, so that the script can continue. We will stop data collection once the angle has reached the target angle.
exposure = 0.5
start_angle = 50 # degrees
end_angle = -50 # degrees
ctrl.stage.set(a=start_angle)
ctrl.stage.set(a=end_angle, wait=False)
buffer = []
i = 0
while ctrl.stage.a != end_angle:
i += 1
img, h = ctrl.get_image(exposure=exposure)
buffer.append((img, h))
print(f"Frame {i} @ {ctrl.stage.a:.1f}°")
for img, h in buffer:
write_tiff(f"cred_{i:04d}.tiff", img, header=h)
Frame 1 @ 38.1° Frame 2 @ 25.8° Frame 3 @ 13.1° Frame 4 @ 0.2° Frame 5 @ -12.0° Frame 6 @ -24.4° Frame 7 @ -36.7° Frame 8 @ -49.1° Frame 9 @ -50.0°
Serial electron diffraction¶
In a serial electron diffraction experiment, data are collected for a large number of crystals. The coordinates can be obtained from images taken at low magnification, but in this example a grid of coordinates will be created.
from instamatic.tools import prepare_grid_coordinates
nx, ny = 3, 3
stepsize = 1000 # nm -> µm
coords = prepare_grid_coordinates(nx=nx, ny=ny, stepsize=stepsize)
print(coords)
[[-1000. -1000.] [ 0. -1000.] [ 1000. -1000.] [-1000. 0.] [ 0. 0.] [ 1000. 0.] [-1000. 1000.] [ 0. 1000.] [ 1000. 1000.]]
We then loop over every coordinate and collect a diffraction pattern. We can then defocus the diffraction pattern to obtain an image of the crystal in the primary beam.
exposure = 0.2 # s
ctrl.mode.set("diff")
for i, (x, y) in enumerate(coords):
ctrl.stage.set(x=x, y=y)
print(f"\nImage {i} @ Position: {ctrl.stage.xy}")
img, h = ctrl.get_image(exposure=exposure)
write_tiff(f"sed_diff_{i:04d}.tiff", img, header=h)
ctrl.difffocus.defocus(offset=1500)
img, h = ctrl.get_image(exposure=exposure)
write_tiff(f"sed_image_{i:04d}.tiff", img, header=h)
ctrl.difffocus.refocus()
Image 0 @ Position: (-1000.0, -1000.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 1 @ Position: (0.0, -1000.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 2 @ Position: (1000.0, -1000.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 3 @ Position: (-1000.0, 0.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 4 @ Position: (0.0, 0.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 5 @ Position: (1000.0, 0.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 6 @ Position: (-1000.0, 1000.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 7 @ Position: (0.0, 1000.0) Defocusing from 52197 to 53697 Refocusing to 52197 Image 8 @ Position: (1000.0, 1000.0) Defocusing from 52197 to 53697 Refocusing to 52197
Alternatively, Instamatic can read .nav
files exported from SerialEM and loop over the items with the acquire flag set. We make use of the acquire_at_items
functionality to make instamatic
handle the stage movement for us. Here is a simple example of how to acquire a diffraction pattern at every position and write it to a tiff file.
from instamatic.formats import write_tiff
from pyserialem import read_nav_file
exposure = 0.2
markers = read_nav_file("nav.nav", acquire_only=True)
ctrl.mode.set("diff")
def acquire(ctrl):
img, h = ctrl.get_image(exposure=exposure)
write_tiff(f"diff_{i:04d}.tiff", img, header=h)
ctrl.acquire_at_items(markers, acquire=acquire)
Acquire[1]: acquire Acquiring on 50 items. Press <Ctrl-C> or ⬛ to interrupt.
0%| | 0/50 [00:00<?, ?it/s]
Total time taken: 44 s for 50 items (0.88 s/item) All done!
acquire_at_items
offers a lot of options to run custom experiments, by specifying routines to run before, after or during the experiment.
def pre_acquire_1(ctrl):
print("I run before the experiment")
def pre_acquire_2(ctrl):
print("And so do I!")
def every_3(ctrl):
print("I run every 3 cycles")
def every_5(ctrl):
print("I run every 5 cycles")
def every_8(ctrl):
print("I run every 8 cycles")
def acquire(ctrl):
print("I'm actually collecting data...")
img, h = ctrl.get_image(exposure=exposure)
def post_acquire(ctrl):
print("I run after the experiment is finished (or interrupted)!")
every_n = {
3: every_3,
5: every_5,
8: every_8
}
ctrl.acquire_at_items(markers,
pre_acquire=(pre_acquire_1, pre_acquire_2),
acquire=acquire,
post_acquire=post_acquire,
every_n=every_n)
Pre-acquire: pre_acquire_1, pre_acquire_2 Acquire[1]: acquire Acquire[3]: every_3 Acquire[5]: every_5 Acquire[8]: every_8 Post-acquire: post_acquire Acquiring on 50 items. Press <Ctrl-C> or ⬛ to interrupt. I run before the experiment And so do I!
0%| | 0/50 [00:00<?, ?it/s]
I'm actually collecting data... I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I run every 5 cycles I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I run every 8 cycles I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I run every 5 cycles I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I run every 5 cycles I'm actually collecting data... I run every 8 cycles I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I run every 5 cycles I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I run every 8 cycles I'm actually collecting data... I run every 5 cycles I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I run every 5 cycles I'm actually collecting data... I'm actually collecting data... I run every 8 cycles I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I run every 5 cycles I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I run every 5 cycles I run every 8 cycles I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I'm actually collecting data... I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I run every 5 cycles I'm actually collecting data... I'm actually collecting data... I'm actually collecting data... I run every 3 cycles I run every 8 cycles I'm actually collecting data... I'm actually collecting data... I run every 5 cycles I run after the experiment is finished (or interrupted)! Total time taken: 44 s for 50 items (0.88 s/item) All done!