All application notes

Application note / 01

Calibration-free beam centring in SerialEM using the JEOL TemExt Plugin

A simple way of centring the electron beam with CLA1 without a stored beam-shift calibration: measure how the beam responds to two small deflector changes, calculate the local 2D response, and use it immediately.

The problem

SerialEM can measure the position of a sufficiently well-defined electron beam with MeasureBeamPosition. This gives us the displacement of the beam centre from the centre of the camera image in pixels.

That is only half of the problem. We still need to know how much beam shift to apply to remove that displacement.

One solution is to calibrate the relationship between CLA1 and camera coordinates beforehand. I wanted to avoid this. In particular, I did not want the script to assume that CLA1-X corresponds to camera X or that the two CLA1 axes have the same sensitivity.

Instead, the script measures the relationship itself each time it runs. It moves CLA1-X by a small known amount and observes what happens to the beam. It then does the same with CLA1-Y. Those two measurements are enough to calculate the local transformation between CLA1 coordinates and the camera image.

Once that transformation is known, the required CLA1 correction can be calculated directly.

Why two measurements are necessary

It would be tempting to measure an X error in the image and correct it with CLA1-X, then do the same for Y. On a real microscope this is not generally correct.

The CLA1 coordinate system can be rotated relative to the camera and the two axes can have different sensitivities. Changing one CLA1 component can therefore move the beam along both camera axes.

This can be seen directly in a measurement from a JEOL microscope used while testing the script. The locally measured response matrix was:

-1.806846   -0.682839
-0.566016    1.842703

The off-diagonal terms are not small. An X change of CLA1 produces a measurable Y displacement, while changing CLA1-Y also moves the beam in camera X. Treating the two axes independently would throw away useful information.

Measuring the response instead of storing it

Let the initial displacement of the beam from the centre of the image be

e = [ex, ey]T.

Close to the current operating point, a small change of a two-axis deflector can be treated as approximately linear:

Δr = J ΔD

where ΔD is the change of the two deflector values, Δr is the resulting movement of the beam in the image, and J is the local 2 × 2 response matrix.

The important point is that we do not need to know J beforehand.

Starting from the current CLA1 values, the script adds a known value δx to CLA1-X and measures the resulting beam movement. It then returns to the original state, adds δy to CLA1-Y, and measures the second movement.

If the measured changes are

rx = [Δxx, Δyx]T

and

ry = [Δxy, Δyy]T,

then the local response is

J = [ Δxxx   Δxyy ; Δyxx   Δyyy ].

We can now solve

J ΔD = -e

for the CLA1 change required to bring the beam to the centre of the image.

In other words, the script performs a small local calibration, uses it immediately, and then discards it.

Choosing a useful calibration step

There is one practical problem with this approach. The CLA1 perturbation has to be large enough for its effect to be measured reliably.

If, for example, changing CLA1 by 50 DAC units moves the measured beam position by only one or two pixels, the calibration will be very sensitive to uncertainty in MeasureBeamPosition. Inverting an inaccurate response matrix can then give a poor correction even though the mathematics itself is perfectly valid.

I therefore made the calibration step adaptive.

The routine starts with a user-defined initialDelta. After applying the test shift it calculates the magnitude of the measured displacement:

R = √(Δx² + Δy²).

If this is smaller than minResponse, the original deflector state is restored, the delta is doubled, and the measurement is repeated.

deltaX = $initialDelta

Loop 10

    testX = $initialCLA1X + $deltaX
    Plugin JEOL SetDecCLA1 $testX $initialCLA1Y

    T
    MeasureBeamPosition

    responseXX = $RepVal1 - $initialBeamX
    responseYX = $RepVal2 - $initialBeamY

    responseX = SQRT ( $responseXX * $responseXX + $responseYX * $responseYX )

    Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y

    If $responseX >= $minResponse
        validX = 1
        Break
    EndIf

    deltaX = $deltaX * 2

    If $deltaX > $maxDelta
        Break
    EndIf

EndLoop

X and Y are tested independently. They do not have to finish with the same delta because each measured response is divided by the perturbation that produced it when the response matrix is constructed.

This turned out to be useful for another reason: the same idea can be used with deflectors whose sensitivities are very different from CLA1.

Direct microscope access with TemExt

I originally implemented this approach using VirtualTemCon. SerialEM called VirtualTemCon functions and microscope values were returned through files. It worked, but it was an unnecessarily indirect route.

The JEOL TemExt Plugin for SerialEM makes the implementation much simpler. CLA1 can be read directly:

Plugin JEOL GetCLA1
initialCLA1X = $JEOLVal1
initialCLA1Y = $JEOLVal2

and changed directly:

Plugin JEOL SetDecCLA1 $targetCLA1X $targetCLA1Y

The whole operation therefore stays inside a SerialEM script. There is no intermediate application and no temporary file exchange.

A real example

The example below was obtained during testing of the TemExt version of the script.

The initial beam displacement reported by SerialEM was:

X = -492.88 px
Y = -380.89 px

corresponding to a radial displacement of approximately 623 pixels.

Both beam-position measurements used all four detected quadrants. The initial fit error reported by MeasureBeamPosition was 0.340.

The two test perturbations produced the following local response matrix:

-1.806846   -0.682839
-0.566016    1.842703

Its determinant was -3.715979. Solving the resulting two-dimensional system gave the following CLA1 correction:

CLA1-X   -314.40 DAC
CLA1-Y   +110.13 DAC

After applying this correction, SerialEM measured:

X = -25.11 px
Y = +13.50 px

giving a residual radial displacement of 28.51 pixels.

A single locally calibrated correction therefore reduced the radial centring error from approximately 623 pixels to 28.5 pixels, or by 95.4%.

SerialEM showing the electron beam after self-calibrating CLA1 beam centring, together with the SerialEM log containing the measured response matrix and calculated CLA1 correction.
Figure 1. Result of a single self-calibrating CLA1 correction. The initial beam centre was approximately 623 pixels from the image centre. Two CLA1 perturbations were used to determine the local response matrix, from which the required deflector correction was calculated. The residual radial displacement after one correction was 28.5 pixels.

The residual is not particularly surprising. The response matrix describes the microscope locally, whereas in this example the calculated correction was relatively large. If greater precision is required, the procedure can simply be repeated around the new operating point. The second iteration then measures a new local response rather than assuming that the first matrix remains exact over a large deflector range.

This is not specific to CLA1

CLA1 is a convenient demonstration because its purpose is immediately familiar, but there is nothing CLA1-specific in the calculation.

The method requires only a two-axis control that can be read and changed, and an observable displacement of the beam that SerialEM can measure.

On a JEOL microscope the same approach can therefore be used, for example, with Gun Shift (GunAlign1) or Spot Alignment (SpotA). The TemExt getter and setter change, but the self-calibration and matrix solution remain the same.

This is also where adaptive selection of the test displacement becomes particularly useful. A delta appropriate for CLA1 is not necessarily appropriate for Gun Shift or Spot Alignment. Instead of storing a separate calibration step for every deflector, the routine can determine a useful measurement amplitude when it runs.

Checks worth adding before routine use

The example above is deliberately simple, but a production version should not blindly invert every response matrix it obtains.

First, both perturbations need to produce a meaningful beam displacement. This is already handled by the adaptive delta.

Second, the two measured response vectors must be sufficiently independent. If they are almost parallel, the matrix becomes poorly conditioned and measurement noise can be amplified into a very large calculated correction. A normalised measure based on the angle between the response vectors is preferable to testing the raw determinant against an arbitrary constant.

Third, every requested deflector value should be checked against its permitted range, and an additional maximum correction should be imposed. A mathematically valid but implausibly large correction is a reason to stop and investigate, not a reason to move the microscope.

Finally, the quality information returned by MeasureBeamPosition can be used to reject unsuitable beam images. In the example above all measurements used four quadrants and the reported fit errors remained between approximately 0.30 and 0.36.

Where I want to take this next

There are several obvious extensions, but I would rather test them than assume that they improve the result.

One is to estimate the repeatability of MeasureBeamPosition and choose the minimum calibration displacement relative to the measured positional noise rather than using a fixed number of pixels.

Another is to measure both positive and negative perturbations of each deflector axis. A central-difference estimate would cost additional Trial images but would provide a useful test of local linearity.

More immediately, the same routine will be tested with Gun Shift and Spot Alignment. If the same calculation works reliably for all three controls, it becomes more useful to think of this as a general self-calibrating two-axis control method rather than a CLA1 centring script.

Conclusion

The useful part of this method is not that SerialEM can move CLA1. It is that the script does not need to know beforehand how CLA1 movement maps onto the camera.

It asks the microscope.

Two small experimental perturbations provide the local two-dimensional response. That response can be inverted immediately to calculate the correction required to centre the beam. If the perturbation is too small to measure reliably, the script increases it automatically.

In the first TemExt test shown here, one self-calibrated correction reduced an initial beam displacement of approximately 623 pixels to 28.5 pixels. No previously stored CLA1-to-camera calibration was used.

The same principle can be applied to other two-axis JEOL deflectors exposed through TemExt, making it a useful building block for more general microscope automation.

The complete script

The full function as used for the example above. It takes four numeric arguments: initialDelta, the first CLA1 test step in DAC units; minResponse, the smallest beam displacement in pixels accepted as a measurable response; maxDelta, the upper limit for the test step; and tolerance, the radial offset in pixels below which no correction is applied. The first line shows the call used in the test: a 200 DAC starting step, a 100 px minimum response, a 2000 DAC ceiling and a 10 px tolerance.

Download CenterBeamCLA1.txt

CallFunction CenterBeamCLA1 200 100 2000 10


# ============================================================
# Adaptive self-calibrating beam centring with CLA1
# JEOL TemExt Plugin / SerialEM
# ============================================================

Function CenterBeamCLA1 4 0 initialDelta minResponse maxDelta tolerance

    # --------------------------------------------------------
    # Read original CLA1 state
    # --------------------------------------------------------

    Plugin JEOL GetCLA1
    initialCLA1X = $JEOLVal1
    initialCLA1Y = $JEOLVal2

    # --------------------------------------------------------
    # Measure initial beam position
    # --------------------------------------------------------

    T
    MeasureBeamPosition

    initialBeamX = $RepVal1
    initialBeamY = $RepVal2

    initialError = SQRT ( $initialBeamX * $initialBeamX + $initialBeamY * $initialBeamY )

    Echo Initial beam offset: X = $initialBeamX px, Y = $initialBeamY px

    If $initialError <= $tolerance
        Echo Beam already centred within requested tolerance.
        Return
    EndIf


    # ========================================================
    # Determine measurable X perturbation
    # ========================================================

    deltaX = $initialDelta
    validX = 0

    Loop 10

        testX = $initialCLA1X + $deltaX

        Plugin JEOL SetDecCLA1 $testX $initialCLA1Y

        T
        MeasureBeamPosition

        responseXX = $RepVal1 - $initialBeamX
        responseYX = $RepVal2 - $initialBeamY

        responseX = SQRT ( $responseXX * $responseXX + $responseYX * $responseYX )

        Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y

        If $responseX >= $minResponse
            validX = 1
            Break
        EndIf

        deltaX = $deltaX * 2

        If $deltaX > $maxDelta
            Break
        EndIf

    EndLoop

    If $validX == 0
        Echo ERROR: Unable to obtain measurable CLA1-X response.
        Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y
        Return
    EndIf


    # ========================================================
    # Determine measurable Y perturbation
    # ========================================================

    deltaY = $initialDelta
    validY = 0

    Loop 10

        testY = $initialCLA1Y + $deltaY

        Plugin JEOL SetDecCLA1 $initialCLA1X $testY

        T
        MeasureBeamPosition

        responseXY = $RepVal1 - $initialBeamX
        responseYY = $RepVal2 - $initialBeamY

        responseY = SQRT ( $responseXY * $responseXY + $responseYY * $responseYY )

        Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y

        If $responseY >= $minResponse
            validY = 1
            Break
        EndIf

        deltaY = $deltaY * 2

        If $deltaY > $maxDelta
            Break
        EndIf

    EndLoop

    If $validY == 0
        Echo ERROR: Unable to obtain measurable CLA1-Y response.
        Plugin JEOL SetDecCLA1 $initialCLA1X $initialCLA1Y
        Return
    EndIf


    # ========================================================
    # Construct and validate local response matrix
    #
    # J = | responseXX/deltaX   responseXY/deltaY |
    #     | responseYX/deltaX   responseYY/deltaY |
    # ========================================================

    J11 = $responseXX / $deltaX
    J21 = $responseYX / $deltaX

    J12 = $responseXY / $deltaY
    J22 = $responseYY / $deltaY

    determinant = $J11 * $J22 - $J12 * $J21

    Echo Local response matrix:
    Echo $J11   $J12
    Echo $J21   $J22
    Echo Determinant: $determinant

    # A scale-normalised conditioning test should be used
    # in the final implementation rather than an arbitrary
    # absolute determinant threshold.


    # ========================================================
    # Solve J * correction = -initialErrorVector
    # ========================================================

    correctionX = (-$J22 * $initialBeamX + $J12 * $initialBeamY) / $determinant

    correctionY = ( $J21 * $initialBeamX - $J11 * $initialBeamY) / $determinant

    targetCLA1X = ROUND ( $initialCLA1X + $correctionX 0 )
    targetCLA1Y = ROUND ( $initialCLA1Y + $correctionY 0 )


    # ========================================================
    # Apply calculated correction
    # ========================================================

    Echo Applying CLA1 correction:
    Echo X: $correctionX DAC
    Echo Y: $correctionY DAC

    Plugin JEOL SetDecCLA1 $targetCLA1X $targetCLA1Y


    # ========================================================
    # Verify result
    # ========================================================

    T
    MeasureBeamPosition

    finalX = $RepVal1
    finalY = $RepVal2

    finalError = SQRT ( $finalX * $finalX + $finalY * $finalY )

    Echo Final beam offset: X = $finalX px, Y = $finalY px
    Echo Residual radial error: $finalError px

EndFunction

Copy the whole block into a SerialEM script window. The CallFunction line can be moved to any other script that has this one loaded.

Resources

Tested with SerialEM and the JEOL TemExt Plugin. Microscope-control scripts should be tested carefully on the intended microscope configuration before being incorporated into unattended workflows.