pyTEM was developed for use on the BASF SE TEM Laboratory's Talos F200i transmission electron microscope.
The authors provide no guarantee that the software will function as intended, either in part or in whole, on any
other microscope installation.
pyTEM is available under the MIT license here.
pyTEM is a high-level scripting interface enabling the user-friendly control of, and automated data
acquisition on, Thermo Fisher Scientific and FEI microscopes from a pure Python environment. Bolted directly on top
of a COM interface, pyTEM is a Python wrapper for, and extension of, the prerequisite Thermo Fisher Scientific
/ FEI scripting and advanced scripting interfaces.
While it may depend on your microscope installation, pyTEM will likely need to be run on a microscope control
computer with the prerequisite Thermo Fisher Scientific / FEI scripting and advanced scripting interfaces installed
and properly configured. For detailed information regarding your microscope's scripting capabilities, please refer to
the documentation accompanying your microscope or contact to your microscope supplier.
In addition to the main scripting interface, pyTEM ships with various scripts. Besides being useful
in-and-of-themselves, these scripts demonstrate how to interface with and control the microscopes using pyTEM.
A list of available scripts can be found below. These scripts are included as part of pyTEM for the sake
of consolidating the TEM Laboratory's scripting efforts.
Supported Python versions: 3.8.
pyTEM is developed and maintained by the TEM Microscopy Laboratory at BASF SE in Ludwigshafen, Germany. If you
have any questions about the pyTEM project or would like to contribute or collaborate, please contact Philipp
Müller at philipp.mueller@basf.com.
Issues should be reported to the issues board here.
pyTEM is a microscope scripting interface. This means that you can issue commands to, and receive microscope data
from, compatible microscopes by calling pyTEM functions. This is not a complete interface in that it does not
provide access to all the microscope's functionality. However, it provides access to all fundamental microscope
functions as well as some more advanced functions which were required for the development of one or more
pyTEM scripts.
pyTEM aims to be more user-friendly than the underlying Fisher Scientific / FEI scripting and advanced
scripting interfaces. To this end:
pyTEMfunctions return only built-in data types or instances of useful, simple classes (no pointers).pyTEMaccepts input and returns results in user-friendly units. For example, stage position is set in microns/degrees, and tilt speed in degrees-per-second.pyTEMprovides many additional functions not directly available through the underlying interface. For example, tilt-while-acquiring, metadata evaluation, and save-to-file functionality.pyTEMis open source and licensed such that users can make any changes or modifications required to suit their own installation.
Finally, pyTEM is a good starting place for those interested in learning how to control their microscope from a
pure Python environment.
pyTEM controls are divided across Python
mixins. This simplified
class-diagram
was built to help articulate pyTEM's architecture. Notice that some mixins include other mixins, and a pyTEM
Interface includes all mixins along with some interface-level controls.
Microscope acquisition controls including acquisition() and acquisition_series() functions. By means of
multitasking, both functions offer beam-blanker optimization and acquire-while-tilting functionality.
These functions return pyTEM Acquistion and AcquisitionSeries objects, respectively. Both the
Acquistion and AcquisitionSeries classes provide helpful methods for further image and metadata
manipulation as well as save-to-file functionality.
from pyTEM.Interface import Interface
from pathlib import Path
my_microscope = Interface()
# Get a list of available cameras.
available_cameras = my_microscope.get_available_cameras()
if len(available_cameras) > 0:
# Let's see what each camera can do...
for camera in available_cameras:
my_microscope.print_camera_capabilities(camera_name=camera)
# Perform a single blanker-optimized acquisition using the first available camera.
acq = my_microscope.acquisition(camera_name=available_cameras[0], exposure_time=1,
sampling='4k', blanker_optimization=True)
# Downsample the acquisition (bilinear decimation by a factor of 2).
acq.downsample()
# Display a pop-up with the results of our acquisition.
acq.show_image()
# Save the acquisition to file.
downloads_path = str(Path.home() / "Downloads")
acq.save_to_file(out_file=downloads_path + "/test_acq.tif")
else:
print("No available cameras!")
When in imaging mode, get and set both TEM and STEM magnification. When in diffraction mode, get and set camera-length.
from pyTEM.Interface import Interface
my_microscope = Interface()
# Make sure we are in TEM imaging mode.
my_microscope.set_mode(new_mode="TEM")
my_microscope.set_projection_mode(new_projection_mode="imaging")
# Print out the current magnification.
current_magnification = my_microscope.get_magnification()
print("Current magnification: " + str(current_magnification) + "x Zoom")
# Print a list of available magnifications.
my_microscope.print_available_magnifications()
# TEM magnification is set by index, let's increase the magnification by three notches.
current_magnification_index = my_microscope.get_magnification_index()
my_microscope.set_tem_magnification(new_magnification_index=current_magnification_index + 3)
# And decrease it back down by one notch.
my_microscope.shift_tem_magnification(magnification_shift=-1)
Get and set both image and beam shift.
from pyTEM.Interface import Interface
my_microscope = Interface()
# Print out the current image shift.
u = my_microscope.get_image_shift()
print("Current image shift in the x-direction: " + str(u[0]))
print("Current image shift in the y-direction: " + str(u[1]))
# Print out the current beam shift.
v = my_microscope.get_beam_shift()
print("\nCurrent beam shift in the x-direction: " + str(v[0]))
print("Current beam shift in the y-direction: " + str(v[1]))
# Shift the image 2 microns to the right, and 3 microns up.
my_microscope.set_image_shift(x=u[0] + 2, y=u[1] + 3)
# Move the beam shift to (-10 um, 5 um).
my_microscope.set_beam_shift(x=-10, y=5)
# Print out the new image shift.
u = my_microscope.get_image_shift()
print("\nNew image shift in the x-direction: " + str(u[0]))
print("New image shift in the y-direction: " + str(u[1]))
# Print out the new beam shift.
v = my_microscope.get_beam_shift()
print("\nNew beam shift in the x-direction: " + str(v[0]))
print("New beam shift in the y-direction: " + str(v[1]))
# Zero both image and beam shift.
my_microscope.zero_shifts()
Microscope mode controls, including getters and setters for instrument, illumination, and projection mode.
from pyTEM.Interface import Interface
my_microscope = Interface()
# Make sure we are in TEM imaging mode.
my_microscope.set_mode(new_mode="TEM")
my_microscope.set_projection_mode(new_projection_mode="imaging")
# Print out the current projection submode.
my_microscope.print_projection_submode()
# Print out the current illumination mode.
print("\nCurrent illumination mode: " + my_microscope.get_illumination_mode())
if my_microscope.get_illumination_mode() == "microprobe":
print("This mode provides nearly parallel illumination at the cost of a larger probe size.")
else:
print("Use this mode to get a small convergent electron beam.")
# Switch to STEM mode.
my_microscope.set_mode(new_mode="STEM")
Insert and retract the FluCam screen.
from pyTEM.Interface import Interface
my_microscope = Interface()
# Make sure the FluCam screen is inserted.
if my_microscope.get_screen_position() == "retracted":
my_microscope.insert_screen()
Blank and un-blank the electron beam.
from pyTEM.Interface import Interface
my_microscope = Interface()
if my_microscope.beam_is_blank():
print("The beam is blanked... un-blanking beam...")
my_microscope.unblank_beam()
else:
print("The beam is un-blanked... blanking beam...")
my_microscope.blank_beam()
Microscope stage controls, including those to get and set the stage position, monitor the stage status, and inspect the current specimen-holder's capabilities.
from pyTEM.Interface import Interface
my_microscope = Interface()
# Print out the stage's current status.
my_microscope.print_stage_status()
# Reset the microscope stage to the home position.
if not my_microscope.stage_is_home():
my_microscope.reset_stage_position()
# Update the stage position along the x, y, and alpha-tilt axes. Move at half speed.
my_microscope.set_stage_position(x=5, y=10, alpha=-45, speed=0.5)
# Print out the current stage position.
my_microscope.print_stage_position()
Vacuum system controls, including those to read gauge pressures, monitor the vacuum in the column, and control the column valve.
from pyTEM.Interface import Interface
my_microscope = Interface()
# Print out the current status of the vacuum system.
my_microscope.print_vacuum_status()
# Print out vacuum info in a table-like format.
my_microscope.print_vacuum_info()
# Check if the column is under vacuum.
if my_microscope.column_under_vacuum():
print("The column is currently under vacuum; it is safe to open the column valve.")
else:
print("Column vacuum is insufficient to open the column valve.")
# Make sure the column valve is closed.
if my_microscope.get_column_valve_position() == "open":
my_microscope.close_column_valve()
Additionally, some pyTEM functions are defined within the Interface class itself (rather than included from
one of the other mixins). Often, these functions utilize functions from the included mixins.
from pyTEM.Interface import Interface
my_microscope = Interface()
# Normalize all lenses.
my_microscope.normalize()
# Prepare for holder removal.
my_microscope.prepare_for_holder_removal()
# Return the microscope to a safe state.
my_microscope.make_safe()
As we have seen, a pyTEM interface can be established by creating an instance of the pyTEM.Interface class.
However, one can also create interfaces that support a limited subset of pyTEM functions. For example, we can
create a pyTEM interface with only stage controls:
from pyTEM.lib.mixins.StageMixin import StageInterface
stage_interface = StageInterface()
Using stage_interface, we can perform the same series of stage commands performed above:
# Print out the stage's current status.
stage_interface.print_stage_status()
# Reset the microscope stage to the home position.
if stage_interface.stage_is_home():
pass
else:
stage_interface.reset_stage_position()
# Update the x, y, and alpha stage positions. Move at half speed.
stage_interface.set_stage_position(x=5, y=10, alpha=-45, speed=0.5)
# Print out the current stage position.
stage_interface.print_stage_position()
This technique can be useful when speed is an issue, or when it's best-practice to restrict unnecessary control. Custom interfaces can be created by including only the required mixins. For example, if you need stage and beam-blanker controls but nothing else, you may want to use an instance of the following limited interface:
import comtypes.client as cc
from pyTEM.lib.mixins.StageMixin import StageMixin
from pyTEM.lib.mixins.BeamBlankerMixin import BeamBlankerMixin
class StageBeamBlankerInterface(StageMixin, BeamBlankerMixin):
"""
A microscope interface with only stage and beam blanker controls.
"""
def __init__(self):
try:
self._tem = cc.CreateObject("TEMScripting.Instrument")
except OSError as e:
print("Unable to connect to the microscope.")
raise e
Several pyTEM functions and scripts use Hyperspy's estimate_shift2D() function to estimate the pixel
offset between images. This function uses a phase correlation algorithm based on the following paper:
Schaffer B, Grogger W, Kothleitner G. Automated spatial drift correction for EFTEM image series. Ultramicroscopy.
2004 Dec;102(1):27-36. doi: 10.1016/j.ultramic.2004.08.003. PMID: 15556698.
pyTEM scripts are sequences of commands that perform useful data acquisitions, initialize
or return the microscope to some pre-defined state, or achieve some other common task. They, often heavily, rely on
the pyTEM Interface or other pyTEM library functions. pyTEM scripts are distributed with,
and automatically installed alongside, pyTEM itself.
pyTEM scripts are run from the command line. For example:
micro_ed --verbose
To view script usage, use the --help option:
micro_ed --help
Often, pyTEM scripts utilize custom Tkinter UIs to simplify and streamline IO.
Since all pyTEM scripts utilize pyTEM itself, running pyTEM scripts requires all of pyTEM
along with its prerequisites and dependencies.
micro_ed is BASF's micro-crystal electron diffraction (MicroED) automated imaging script. MicroED allows for the
fast, high-resolution 3D structure determination of small chemical compounds and biological macromolecules. More on
MicroED here.
micro_ed achieves automated image alignment by computing the image deviation during a preparatory tilt
sequence and then applying a compensatory image shift during the main acquisition sequence. This automated image
alignment functionality is optional. micro_ed only collects the data, it does not analyse it.
micro_ed results can be saved as a single multi-image stack file or as multiple single-image files. Since
performing a MicroED acquisition sequence requires some actions that aren't easily automated (such as selecting
suitable particles and setting the eucentric height), a series of Tkinter message boxes guide the user through
those steps requiring manual interaction.
align_images allows the user to load some images from file (possibly from a single multi-image stack file or
possibly from multiple single-image files), align the images, and then save the results back to file as a single
multi-image stack.
Colour images are converted to 8bit unsigned greyscale prior to alignment.
Given 16 natural light-micrographs of a bulk carbon sample sandwiched between polarizers of varying cross, produce both anisotropy and orientation maps.
Colour images are converted to 8bit unsigned greyscale prior to analysis.
This script uses the technique explained in the following paper:
Gillard, Adrien & Couégnat, Guillaume & Caty, O. & Allemand, Alexandre & P, Weisbecker & Vignoles, Gerard. (2015).
A quantitative, space-resolved method for optical anisotropy estimation in bulk carbons. Carbon. 91. 423-435.
10.1016/j.carbon.2015.05.005.
Allows users to control their microscope with an Xbox controller. This is especially helpful for microscope operators working off-site.
Because pyTEM is often required on microscope control machines which lack internet connectivity, pyTEM is not
listed on the Python Package Index, nor anywhere else. Rather, we provide a wheel file in /dist and the
following offline install instructions.
- Download requirements.txt.
- Download the required dependencies with pip
pip download -d ./pytem_dependencies -r requirements.txt. - Download the
pyTEMwheel from /dist.
Transfer requirements.txt, the entire pytem_dependencies folder you just created, and the pyTEM
wheel to the offline machine (any directory is fine; most opt for current user's Downloads directory).
- Ensure both pip and wheel are already installed.
- Install the required dependencies with pip:
pip install --no-index --find-links ./pytem_dependencies -r requirements.txt - Install
pyTEMitself with pip. Example:pip install pyTEM-0.1.0-py3-none-any.whl
If you run into any problems installing the required dependencies, check that you are using the same version of pip and wheel on both the online and offline systems.
- Install wheel with
pip install wheel. - Download the whole
pyTEMproject directory. - Navigate to the pyTEM folder.
- Run
setup.pywith thebdist_wheelsetuptools command. Example:python setup.py bdist_wheel
pyTEM is developed and maintained by the TEM microscopy laboratory at BASF SE in Ludwigshafen, Germany. The
initial development was performed by RISE (Research Internships in Science and Engineering) Interns from North America.
More on the RISE program here.
Meagan Jennings (Sept - Dec 2021)
Baltimore, Maryland, USA
- Figured out how to interface with and control the microscope from a pure Python environment.
- developed
TEMPackage, the predecessor topyTEM. View the project as it existed at the time of Meagan's final commit here. - Developed
microED_Tilt_Series, the predecessor topyTEM'smicro_edscript. View the project on GitLab here. - Wrote the original TEM Scripting Guide, which can be found in /docs.
Michael Luciuk (May - Aug 2022)
Saskatoon, Saskatchewan, Canada
- Refactored the original
TEMPackagemodule into thepyTEMlibrary that we know and love today. - Refactored the original
microED_Tilt_Seriesscript into themicro_edscript that we know and love today. - Updated and improved both
pyTEMandmicro_ed. - Developed the
align_imagesscript and did some initial work on thebulk_carbon_analysisscript.
You can view the pyTEM project as it existed at the time of Michael's final commit
here.