PlanPtycho#

Check multislice ptychography settings against a known crystal before the experiment. Give a crystal (CIF path, ase.Atoms, or Materials Project id) and the microscope settings; the widget shows the beam through the specimen, the reconstruction’s model window, the probe on that window, the Bragg disks on the detector, and graded checks.

from quantem.widget import PlanPtycho

plan = PlanPtycho("SrTiO3.cif", zone_axis=(0, 0, 1), thickness_nm=110, tilt_mrad=(3.2, -2.4), c10_nm=-17)
plan.report()               # the checks as a DataFrame
plan.apply_thickness(40)    # recommended focus and scan size for 40 nm
plan.apply_preset("Arina · 300 kV · 25 mrad · 115 mm")
plan.window_A, plan.object_pixel_A

Needs ase, abtem and, for Materials Project ids, spglib (pip install "quantem.widget[crystal]").

Settings from a collaborator#

To check whether an acquisition someone else recorded can work for ptychography, pass the values as reported. Giving detector_mrad_per_px without a camera name makes the camera "custom"; c10_nm is the defocus in the quantem sign (negative focuses into the specimen; focus_depth_nm = -c10_nm):

PlanPtycho("crystal.cif", thickness_nm=40, voltage_kV=200, semiangle_mrad=24.5,
           detector_px=128, detector_mrad_per_px=0.9, scan_step_A=0.4, scan_size_px=256, c10_nm=-15)

In the widget every number can be typed: click it, enter the value, press Enter.

Presets#

Microscope (preset=, or the menu in the title bar), named by their settings:

Preset

kV

Semiangle

Camera

Camera length

mrad per pixel

Arina · 300 kV · 30 mrad · 91 mm (default)

300

30

Arina, 192 px

91 mm

0.554

Arina · 300 kV · 25 mrad · 115 mm

300

25

Arina, 192 px

115 mm

0.461

Arina · 300 kV · 21.4 mrad · 185 mm

300

21.4

Arina, 192 px

185 mm

0.269

EMPAD · 300 kV · 30 mrad · 91 mm

300

30

EMPAD, 128 px

91 mm

0.831 (scaled)

The Arina values are measured calibrations at 300 kV (semiangle over fitted bright-field disk radius); other camera lengths scale as 50.4 mrad mm / camera length. The EMPAD is scaled from them by pixel pitch (150 / 100 um) and is not measured. detector_px and detector_mrad_per_px override the calibration, e.g. detector_px=96 for a 2x binned Arina.

Thickness (apply_thickness, or “Recommended … nm” in the title bar), for 20 to 70, 100, 150 and 200 nm (cryo and biological sections): focus at mid-thickness and a scan wide enough that the margin check passes (at least 128 x 128). These come from the checks below; reconstructions tested the checks on 90-130 nm SrTiO3 with the focus 17 nm below the entrance. With the Arina at 91 mm and 30 mrad every check passes to 100 nm; at 150 and 200 nm the beam (46 and 61 A at best focus) outgrows the 35.5 A window. A 185 mm camera length widens the window to 73 A but reaches only 26 mrad: the widget shows that trade-off rather than choosing for you.

Checks#

Check

Formula

Grade

Beam fits the virtual window

widest beam 2 alpha abs(z - f) + 1.22 lambda / alpha vs lambda / dtheta

caution when wider

Scan margin

scan side vs 4 x widest beam radius

caution when smaller

Probe overlap

1 - step / entrance beam diameter

pass >= 60 %, fail < 30 %

Detector reach

detector edge / semiangle

pass >= 1.5, fail < 1

Column lean

thickness x tan(tilt) vs object pixel

caution when larger

Focus splits the specimen

0 <= focus depth <= thickness

caution when the whole slab is on one side

Window, margin, lean and focus only caution: simulated 110-130 nm SrTiO3 reconstructed with the beam wider than the window, a scan narrower than the beam (weaker only at the scan edge) and a 4 mrad tilt held at its measured value, all with the focus inside the specimen. Information rows give the object pixel, the depth of field (2 lambda / alpha^2), beam diameters, the phase of the strongest column per nm, and the first HOLZ ring (lattice period along the beam, counting centring translations). Slice thickness is a reconstruction choice and is not part of the plan.

The Python functions (plan_geometry, check_statuses, check_rows, recommended_settings, detector_sampling_mrad) and the browser arithmetic are pinned to the same js/planptycho/goldens.json, down to the text of each check.

Reference#

class quantem.widget.planptycho.PlanPtycho(*args: t.Any, **kwargs: t.Any)#

Check multislice ptychography settings against a known crystal before the experiment.

Parameters:
  • structure (str or ase.Atoms) – CIF path, ase.Atoms, or Materials Project id ("mp-5229", needs MP_API_KEY and spglib).

  • zone_axis (sequence of 3 int, default (0, 0, 1)) – Crystal direction [uvw] along the beam.

  • thickness_nm (float, default 50) – Specimen thickness.

  • preset (str, optional) – Microscope settings in routine use (MICROSCOPE_PRESETS); default "Arina · 300 kV · 30 mrad · 91 mm". voltage_kV, semiangle_mrad, detector and camera_length_mm override single values of it.

  • voltage_kV (float, optional) – Accelerating voltage and probe convergence semiangle.

  • semiangle_mrad (float, optional) – Accelerating voltage and probe convergence semiangle.

  • detector ({"Arina", "EMPAD", "custom"}, optional) – Camera; sets the pixel count and, with camera_length_mm, the angular sampling. The Arina sampling is measured at 91, 115 and 185 mm (0.554, 0.461, 0.269 mrad per pixel) and scales as 1 / camera length otherwise; the EMPAD scales from it by pixel pitch. "custom" takes detector_px and detector_mrad_per_px as reported (the default when detector_mrad_per_px is given without a camera: settings from a collaborator).

  • camera_length_mm (float, optional) – Nominal camera length.

  • c10_nm (float, optional) – Nominal defocus C10 (quantem sign: negative focuses below the entrance surface, into the specimen), as a reconstruction or an acquisition log reports it. Same as focus_depth_nm = -c10_nm; give one of the two.

  • focus_depth_nm (float, optional) – Where the probe is focused, measured into the specimen from the entrance surface (negative: above it). Default: mid-thickness, which makes the widest beam as narrow as it can be.

  • tilt_mrad ((float, float), default (0, 0)) – Specimen tilt off the zone axis, (row, col).

  • detector_px (int, optional) – Detector pixels per side used in the reconstruction; overrides the camera’s native count (96 after 2x binning of the Arina: same total angle, so the sampling per pixel doubles unless detector_mrad_per_px is given).

  • detector_mrad_per_px (float, optional) – Angular sampling per pixel; overrides the calibration.

  • wave_window_factor ({1, 2}, default 1) – Wave support relative to the measured detector grid. Factor 2 doubles the physical width and model pixels without changing object sampling or measured detector angles. Requires an intensity-integrating backend.

  • scan_step_A (float, default 0.5) – Probe step.

  • scan_size_px (int, default 128) – Scan positions per side.

  • title (str, optional) – Heading; defaults to the formula and zone axis.

Notes

Needs ase and abtem (pip install "quantem.widget[crystal]"). report() returns the checks as a DataFrame. The widget has no save_state or HTML export: the crystal is rebuilt from structure.

Public constructor

apply_preset(name: str) → None#

Set voltage, semiangle, camera and camera length from MICROSCOPE_PRESETS.

apply_thickness(thickness_nm: float) → None#

Set the thickness with the recommended_settings() for it (focus and scan size).

geometry() → dict#

The numbers behind the checks for the current settings (plan_geometry()).

report()#

The checks for the current settings as a DataFrame (one row per check: status, value, rule, note).

property window_A: float#

factor times wavelength / measured detector angle.

Type:

Real-space wave window

property object_pixel_A: float#

model window / wave pixels, independent of window factor.

Type:

Object pixel size

quantem.widget.planptycho.plan_geometry(*, voltage_kV: float, semiangle_mrad: float, focus_depth_nm: float, thickness_nm: float, detector_px: int, detector_mrad_per_px: float, scan_step_A: float, scan_size_px: int, tilt_mrad: Sequence[float] = (0.0, 0.0), holz_repeat_A: float | None = None, wave_window_factor: int = 1) → dict#

The numbers every check is built from (lengths in A, angles in mrad).

  • model window W = factor * lambda / dtheta, object pixel lambda / (N dtheta), detector reach theta_max = N dtheta / 2 (edge)

  • beam diameter at depth z: D(z) = 2 alpha |z - f| + 1.22 lambda / alpha (f = focus depth below the entrance)

  • depth of field 2 lambda / alpha^2; probe overlap 1 - step / D(0)

  • column lean across the thickness t tan|theta|; first HOLZ ring sqrt(2 lambda / H) (H = lattice period along the beam)

quantem.widget.planptycho.check_rows(geometry: dict, *, detector_px: int, scan_step_A: float, column_phase_rad_per_A: float = 0.0) → list[dict]#

Every check as {id, label, status, value, rule, note}, the text the widget shows (checkRows in geometry.ts renders the same strings; both are pinned to the goldens). note is empty when a check passes.

quantem.widget.planptycho.recommended_settings(thickness_nm: float, *, voltage_kV: float, semiangle_mrad: float, scan_step_A: float) → dict#

Starting settings for a thickness, derived from the checks: focus at mid-thickness (the narrowest widest beam), and a scan wide enough that the margin check passes (at least 128 positions, multiples of 16). Reconstructions tested the checks only on 90-130 nm SrTiO3 (30 mrad, focus 17 nm).

Interactive controls#

Control

Trait

Expected effect

Drag in Top

(view only)

moves the probe (starts at the scan centre); its beam at the view depth and the model window follow

Drag the dashed line in Side

focus_depth_nm

moves the focus through the stationary specimen; the labels show how much lies above and below it

Drag elsewhere in Side

view_depth_nm

sets the depth shown in Top and Probe

Sample switch on Probe

(view only)

draws the crystal at the view depth under the probe

Wheel / double-click on any panel

(view only)

zoom about the cursor / reset

Microscope and Recommended menus

several

apply a preset in one step

Zone

zone_axis

rebuilds the projection and Bragg disks in Python

Microscope: Voltage, Semiangle, C10

voltage_kV, semiangle_mrad, focus_depth_nm (= -C10)

the knobs an operator turns; C10 is the defocus (negative focuses into the specimen) and the focus depth is read out; numbers can be typed

Sample: Zone, Thickness, Tilt row / col

zone_axis, thickness_nm, tilt_mrad

facts about the specimen

Camera, Length

detector, camera_length_mm

set detector_px and detector_mrad_per_px from the calibration

Camera custom: Pixels, Sampling

detector, detector_px, detector_mrad_per_px

a camera without a calibration: type the reported values

Binning

detector_px, detector_mrad_per_px

1x / 2x / 4x: fewer pixels, the same total angle; the sampling per pixel is read out, not set (Python detector_mrad_per_px= overrides it for an uncalibrated camera)

Scan: Step, Size

scan_step_A, scan_size_px

probe step (magnification) and positions per side (64 to 512); the field of view is read out

The widget has no save_state or HTML export; it is rebuilt from the crystal in one call.

Simulation-cell planning from a CIF#

A CIF path or ASE Atoms is accepted by the existing constructor. The Simulation Cell section checks unit-cell repeats, potential pixels per cell, and the clearance around the complete scan-center span (N - 1) × step. Potential sampling is separate from the detector-derived reconstruction sampling.

plan = PlanPtycho(
    "BaTiO3.cif", zone_axis=(0, 0, 1), thickness_nm=60,
    focus_depth_nm=20, scan_step_A=0.373, scan_size_px=128,
    detector_px=192, detector_mrad_per_px=0.5570968023269496,
)
plan.simulation_repeats = [48, 48]
plan.simulation_pixels_per_cell = 96
plan.simulation_plan()

simulation_plan(repeats=(48, 48), pixels_per_cell=96, guard_A=5) can also calculate a candidate without changing the controls. The returned depth repeat count covers the requested slab; the final repeat may need truncating to obtain an exact thickness. The geometric margin includes the full directional tilt excursion conservatively on each side.

This section is a planning check, not a simulation launcher. The Detector panel is labelled schematic. Changing these controls does not update previously computed multislice diffraction. Validate propagated boundary power, a larger lateral cell, potential sampling and propagation slices with the simulation backend before accepting a dataset. A geometric pass deliberately leaves boundary_convergence_verified=False.

Optional Virtual Window#

wave_window_factor=2 previews a 384 × 384 wave for a 192 × 192 measured detector. The Virtual Window controls at the bottom of the planner switch between native and doubled support. This is an optional adjustment; microscope, detector, scan, and specimen settings stay above it. This doubles the physical model width while retaining the real-space pixel size and measured angular reach; it does not change the acquired detector pixels.

PlanPtycho("BaTiO3.cif", detector_px=192,
           detector_mrad_per_px=0.5570968023269496,
           wave_window_factor=2)

The reconstruction must support integrating its finer predicted diffraction intensities back onto the measured detector. The planning preview does not run or validate that reconstruction. Its free-space probe envelope is a geometric check, not a guarantee of multislice convergence. Use ShowCIF alongside the planner to inspect the same CIF and the atomic columns.