DiffractionSim#
Rotate a unit cell and watch its electron diffraction pattern follow. The left panel draws the cell, the right panel the nanobeam pattern, CBED disks, or the Kikuchi pattern of the same orientation. Kinematical and Bloch wave intensities are computed in the browser, so dragging the crystal needs no Python round trip.
from quantem.widget import DiffractionSim
sim = DiffractionSim("Si (diamond cubic)", zone_axis=[1, 1, 0])
sim
The crystal can be a preset name (DiffractionSim.presets_available()), a CIF
path, an ASE Atoms object, or a quantem Crystal.
Requirements#
DiffractionSim needs quantem 0.1.10 or later, for quantem.diffraction.Crystal
and quantem.diffraction.bloch. quantem is a base dependency of
quantem.widget except on Intel Macs and Windows on ARM; there, constructing the
widget raises an ImportError that names this requirement, and the
DiffractionSim tests are skipped.
Reference#
- class quantem.widget.diffraction_sim.DiffractionSim(*args: t.Any, **kwargs: t.Any)#
Interactive unit cell and diffraction pattern simulator.
The left panel draws the unit cell, the right panel the diffraction pattern of the same orientation. Dragging either panel tilts the crystal. Kinematical and Bloch wave intensities, CBED disks and Kikuchi lines are computed in the browser from a reflection list prepared in Python.
Requires quantem 0.1.10 or later (
quantem.diffraction.Crystalandbloch), which brings ASE and spglib.- Parameters:
crystal (Crystal, ase.Atoms, str, pathlib.Path or None, optional) – A quantem
Crystal, an ASEAtomsobject, a CIF path, or the name of a preset (seepresets_available()).Nonestarts with silicon, or with the preset named instate.zone_axis (sequence of 3 float, optional) – Initial zone axis [uvw] along the beam.
Nonekeeps the identity orientation (c axis along the beam).voltage_kV (float, default 200) – Accelerating voltage in kV (the beam energy in keV), above 0 and up to 1000. Changing it recomputes the reflection list.
k_max_inv_A (float, default 4) – Largest scattering vector of the reflection list, in 1/Å, from 0.2 to 8. Changing it recomputes the reflection list.
pattern_range_inv_A (float, optional) – Scattering vector at the edge of the nanobeam and CBED panel, in 1/Å, from 0.2 to
k_max_inv_A.Noneuses 3, ork_max_inv_Awhen that is smaller.mode ({"nanobeam", "cbed", "kikuchi"}, default "nanobeam") – Nanobeam spots, convergent-beam disks, or the wide-angle Kikuchi pattern.
"kossel", the earlier name of"kikuchi", is accepted and stored as"kikuchi".render ({"markers", "disks", "pixels"}, default "markers") – Nanobeam: markers sized by intensity, disks of the convergence semiangle with brightness by intensity, or a pixelated pattern. Kikuchi: vector lines (
"markers"), or the pixel lookup of the reference pattern ("pixels", seecompute_kossel_reference()).dynamical (bool, default True) – Bloch wave intensities;
Falseuses kinematical|F|^2with a Gaussian excitation envelope of widthsigma_excitation_inv_A.thickness_A (float, default 500) – Specimen thickness in Å, above 0 and up to 10,000.
semiangle_mrad (float, default 2) – Convergence semiangle in mrad (CBED disks and nanobeam disk radius), above 0 and up to 50.
precession_deg (float, default 0) – Precession half angle in degrees, 0 to 10. Intensities are averaged over
n_precessionincident tilts on the precession ring.n_precession (int, default 24) – Incident tilts on the precession ring (half as many while dragging), 3 to 360.
sigma_excitation_inv_A (float, default 0.02) – Width of the kinematical excitation envelope, in 1/Å.
sg_max_inv_A (float, default 0.05) – Excitation error cutoff selecting the Bloch beams, in 1/Å. Reflections outside it take thin-slab intensities.
quality ({"fast", "medium", "fine"}, default "medium") – Bloch beam cap and CBED tilt sampling.
field_mrad (float, default 50) – Half angle of the Kikuchi pattern field of view, in mrad.
show_kikuchi (bool, default False) – Overlay the Kikuchi line pairs on the nanobeam and CBED patterns.
view_from ({"detector", "gun"}, default "detector") – Viewpoint shared by both panels (no UI control).
"detector"looks up the column from the detector side: the exit face of the cell is nearest you and tilts together with the Laue circle and the Kikuchi pattern."gun"is the operator’s view down the column; the entrance face is nearest and tilts opposite to the pattern.scaling ({"linear", "power", "log"}, default "linear") – Intensity scaling of the pixel renderings.
power (float, default 0.5) – Exponent of the
"power"scaling, 0.05 to 1.marker_power (float, default 0.5) – Marker area scales as
intensity**marker_power(0.5: square root of the intensity), 0.05 to 1. For nanobeam disks, the brightness exponent.marker_size_px (float, default 20) – Radius in CSS pixels of the strongest marker at a panel width of 420.
cmap (str, default "inferno") – Colormap of the pixel renderings: a name of the shared widget set (
"inferno","turbo","gray", …), the same name with a"_r"suffix to reverse it, or"turbo_black"and"turbo_black_r", imported from quantem.vmin_pct (float, default 0, 100) – Contrast window of the pixel renderings, in percent of the displayed intensity range.
vmax_pct (float, default 0, 100) – Contrast window of the pixel renderings, in percent of the displayed intensity range.
show_labels (bool, default True) – Label the cell axes a, b, c.
show_hkl (bool, default True) – Label the strongest reflections (or Kikuchi lines) with their hkl.
show_cell_axes (bool, default True) – Draw the cell axes.
n_cells (sequence of 3 int, default (1, 1, 1)) – Block of cells drawn in the left panel, 1 to 6 along each of a, b, c.
polyhedra (bool, default False) – Draw coordination polyhedra around every species except the most numerous one; around every atom of an elemental crystal.
show_ewald (bool, default True) – Side view of the Ewald sphere below the cell: the reciprocal lattice points near the x-z plane, the sphere through the origin (z stretched), and the excited reflections in green.
rotation_step_deg (float, default 15) – Step of the x, y, z rotation buttons, in degrees.
rotation_speed_deg_per_s (float, default 6) – Speed of the continuous rotation buttons, in degrees per second.
panel_width_px (int, default 420) – Width of the pattern panel in CSS pixels, 220 to 2000. The frontend shrinks it to fit a narrower container.
title (str, default "Diffraction Simulator") – Heading above the panels.
save_state (bool, default False) – Embed the full interactive state, including the Kikuchi reference pattern, in a saved notebook. By default the reference pattern is left out and a PNG of both panels, captured by the frontend, is saved for viewing the notebook without a kernel.
state (dict, str or pathlib.Path, optional) – Settings from
state_dict()or a file written bysave(), applied after the other arguments. Itspresetloads that crystal unlesscrystalis given.
- orientation#
Crystal orientation as a unit quaternion (w, x, y, z).
- Type:
list of 4 float
- preset#
Name of the loaded preset, or
""for other crystals. Setting it loads that preset.- Type:
str
- cmap_luts#
Read-only. Lookup tables of the colormaps imported from quantem.
- Type:
dict
- status#
Progress message shown under the panels.
- Type:
str
- export_status#
Result of the last HTML export.
- Type:
str
Notes
Bloch couplings are absorptive Weickenmeier-Kohl factors when every element has tabulated factors, and the elastic couplings
gamma F_g / piotherwise; the info line under the panels says “absorptive” only in the first case.Examples
>>> from quantem.widget import DiffractionSim >>> sim = DiffractionSim("Si (diamond cubic)", zone_axis=[1, 1, 0], mode="cbed") >>> sim.thickness_A = 800 >>> sim.save("si_110.json") >>> sim.export_html("si_110.html")
- static presets_available() list[str]#
Names of the built-in crystal structures.
- Returns:
Preset names accepted by the constructor,
set_crystal(), thepresettrait and thepresetsoption ofexport_html().- Return type:
list of str
- property crystal: quantem.diffraction.crystal.Crystal#
The loaded
quantem.diffraction.crystal.Crystal.
- set_crystal(crystal: quantem.diffraction.crystal.Crystal | Atoms | str | pathlib.Path) DiffractionSim#
Load a crystal and recompute the reflection list.
- Parameters:
crystal (Crystal, ase.Atoms, str or pathlib.Path) – A quantem
Crystal, an ASEAtomsobject, a CIF path, or a preset name.- Returns:
The widget, for chaining.
- Return type:
- Raises:
ValueError – If a string is neither a preset name nor an existing CIF file.
TypeError – If
crystalis none of the supported types.
- set_zone_axis(zone_axis: Sequence[float], in_plane_deg: float = 0.0) DiffractionSim#
Put a crystal direction [uvw] along the beam.
- Parameters:
zone_axis (sequence of 3 float) – Direct-lattice direction
u*a + v*b + w*c, not all zero.in_plane_deg (float, default 0) – Rotation about the beam, in degrees.
- Returns:
The widget, for chaining.
- Return type:
- Raises:
ValueError – If
zone_axisis not three finite numbers, or is zero.
- compute_kossel_reference(thicknesses_A: Sequence[float] = (300.0, 600.0, 1000.0), angle_step_mrad: float = 3.0, k_max_inv_A: float = 1.0) DiffractionSim#
Compute the reference pattern for the pixel rendering of the Kikuchi mode.
Takes about a minute for silicon at 3 mrad. Results are cached per energy, thicknesses, step and
k_max_inv_A.- Parameters:
thicknesses_A (sequence of float, default (300, 600, 1000)) – Specimen thicknesses in Å; the browser interpolates between them.
angle_step_mrad (float, default 3) – Angular step of the reference grid, in mrad.
k_max_inv_A (float, default 1) – Largest scattering vector of the Bloch calculation, in 1/Å.
- Returns:
The widget, for chaining.
- Return type:
- state_dict() dict[str, Any]#
Return the settings that restore the view, as a plain dict.
- Returns:
Trait name to value for the crystal preset, beam energy, orientation, mode and every display setting. The reflection list, Kikuchi reference pattern and colormap tables are derived data and are not included.
- Return type:
dict
- load_state_dict(state: dict[str, Any]) DiffractionSim#
Apply settings from
state_dict(); unknown keys are ignored.A non-empty
presetloads that crystal. Otherwise the current crystal is kept, so a CIF or ASE crystal is passed to the constructor again together withstate.- Parameters:
state (dict) – Settings, as returned by
state_dict(), or the versioned envelope written bysave().- Returns:
The widget, for chaining.
- Return type:
- save(path: str | Path) None#
Write the settings of
state_dict()to a versioned JSON file.- Parameters:
path (str or pathlib.Path) – Destination file. Missing parent directories are created.
- export_html(path: str | Path | None = None, *, title: str | None = None, presets: Sequence[str] | None = None) Path#
Write a standalone HTML viewer with the current state.
The page carries the widget runtime, so it opens with no network and no kernel, and every simulation runs in the browser: rotating the cell and changing thickness, mode or display settings need no Python. The beam energy and
k_max_inv_Aare fixed to the exported values, and the Kikuchi reference pattern is included when it was computed.- Parameters:
path (str or pathlib.Path, optional) – Destination HTML file. Missing parent directories are created. Defaults to
<crystal>_<mode>_diffractionsim.htmlin the working directory.title (str, optional) – Browser page title; defaults to the
titletrait.presets (sequence of str, optional) – Additional preset structures to embed so the crystal menu works in the page. Each adds its reflection list to the file.
- Returns:
The written file.
- Return type:
pathlib.Path
Physics#
voltage_kV and k_max_inv_A set the reflection list, computed in Python:
the reciprocal lattice points within k_max_inv_A, each with its kinematical
|F_g|^2 (Lobato and Van Dyck factors) and its Bloch coupling U_g
(absorptive Weickenmeier and Kohl factors at voltage_kV). Changing either
trait recomputes the list.
With dynamical=True, reflections within sg_max_inv_A of the Ewald sphere
enter a Bloch wave calculation; the rest take thin-slab intensities. With
dynamical=False, every reflection takes |F_g|^2 with a Gaussian excitation
envelope of width sigma_excitation_inv_A. precession_deg averages the
pattern over n_precession incident tilts on the precession cone.
mode is "nanobeam", "cbed" or "kikuchi". "kossel" is accepted as the
earlier name of "kikuchi" and stored as "kikuchi". The pixel rendering of
the Kikuchi mode needs a reference pattern from compute_kossel_reference()
(about a minute for silicon at 3 mrad).
Units are in the trait names: _keV, _A (Å), _inv_A (1/Å), _mrad,
_deg, _px (CSS pixels), _pct (percent).
Data sources#
Data |
Source |
Used for |
|---|---|---|
Kinematical scattering factors |
Lobato & Van Dyck, Acta Cryst. A 70, 636 (2014), doi:10.1107/S205327331401643X, from quantem |
|
Absorptive Bloch couplings |
Weickenmeier & Kohl, Acta Cryst. A 47, 590 (1991), doi:10.1107/S0108767391004774, from quantem |
|
Atom colors and radii |
ASE ( |
Cell drawing |
CIFs loaded in the browser (web build only) get the same absorptive
Weickenmeier-Kohl couplings, with core loss, phonon absorption and a
Debye-Waller factor for an RMS thermal displacement of 0.05 Å
(thermal_sigma_A). The factors are tabulated every 0.0025 1/Å and
interpolated; a CIF of the silicon preset reproduces the preset couplings to
1e-5 of the largest coupling (js/diffractionsim-web/wk.test.ts).
Interactive controls#
Control |
Trait |
Expected effect |
|---|---|---|
Crystal menu |
|
Loads the preset and recomputes the reflection list |
Energy menu |
|
Recomputes the reflection list at the new energy |
Zone axis field, Go |
|
Puts [uvw] (or [uvtw] for hexagonal cells) along the beam |
Copy |
none |
Copies both panels as one PNG |
Export |
none |
PNG of both panels, or a standalone HTML page |
Reset |
|
Returns to the identity orientation |
Drag the cell |
|
Tilts the crystal; the near face follows the pointer |
Drag the pattern |
|
Tilts the crystal so the pattern follows the pointer |
Shift-drag or two-finger twist |
|
Rotates about the beam |
Double-click a disk |
|
Tilts to the two-beam condition of that reflection |
Double-click empty space |
|
Moves the Laue circle centre to the click |
x, y, z buttons and Step |
|
Rotates about the screen axes by the step |
Spin ↔ ↕ and speed |
|
Rotates continuously about a screen axis |
Cell Axes, Axis Labels, Polyhedra, Ewald Sphere |
|
Cell panel overlays |
Cells |
|
Number of cells drawn along a, b, c (1 to 6) |
Nanobeam / CBED / Kikuchi |
|
Pattern type |
Markers / Disks / Pixels (Lines / Pixels) |
|
Pattern rendering |
Dynamical |
|
Bloch wave or kinematical intensities |
hkl Labels |
|
Labels the strongest reflections or lines |
Kikuchi Lines |
|
Overlays Kikuchi line pairs on nanobeam and CBED |
Quality |
|
Bloch beam cap and CBED tilt sampling |
Compute Reference |
|
Computes the Kikuchi reference pattern in Python |
Thickness |
|
Specimen thickness |
Semiangle |
|
CBED and nanobeam disk radius |
Precession |
|
Precession half angle |
Pattern Range |
|
Scattering vector at the panel edge |
Field Half-Angle |
|
Kikuchi pattern half angle |
Excitation Error σ |
|
Kinematical envelope width (Dynamical off) |
Marker sliders |
|
Marker area exponent and largest radius |
Scale, Exponent, Color, histogram |
|
Pixel rendering display |
cmap takes the shared widget colormaps, their _r reversals, and
turbo_black and turbo_black_r, whose lookup tables come from
quantem.core.visualization (trait cmap_luts).
State#
sim.save("si_110.json")
sim2 = DiffractionSim(state="si_110.json")
state_dict() holds the settings that restore the view: preset, energy,
orientation, mode and every display setting. It does not hold the reflection
list or the Kikuchi reference pattern, which are recomputed. A crystal from a
CIF or ASE is passed again: DiffractionSim("my.cif", state="my.json").
With save_state=False (the default), a saved notebook keeps the settings and
a PNG of both panels, which the frontend captures after the view settles, and
leaves out the Kikuchi reference pattern. save_state=True embeds everything.
HTML export#
sim.export_html("si_110.html", presets=["Al (fcc)", "Ti (hcp)"])
The page follows the HTML export protocol and carries the widget
runtime, so it opens with no network. All simulations run in the browser:
rotation, thickness, mode and display changes need no Python. The beam energy
and k_max_inv_A are fixed to the exported values; presets embeds
additional structures for the crystal menu. The Export menu in the widget
writes the same page through the export bridge. The page holds no image data,
so there are no encoding or binning options.
Web pages and Dash apps#
npm run build:web (not npm run build) writes
packages/quantem.diffsim/src/quantem/diffsim/static/diffraction-sim.js, a
single ES module with export default { render }, built from
js/diffractionsim-web/ without React. Every calculation runs in the browser,
including the structure factors of a structure given as CIF text, so a page
needs no Python and no quantem. The module is published on PyPI as
quantem.diffsim, which has no dependencies and adds a few helpers:
import quantem.diffsim as diffsim
diffsim.save_html("si.html", structure, zone_axis=[1, 1, 0], title="Si") # one offline page
diffsim.settings(structure, zone_axis=[0, 0, 1], mode="nanobeam") # the dict the module reads
structure can be a pymatgen Structure (written as its conventional
standard cell, with its space group), an ASE Atoms object, CIF text or a
CIF path. A structure passed this way replaces the structure menu and the
CIF button. The page also shows a “Powered by quantEM” link to the quantEM
documentation (show_logo=False removes it).
In a Dash app, quantem.diffsim.dash.register(app) serves the module from
the app’s Flask server and adds one clientside callback, and
quantem.diffsim.dash.layout(name) places a simulator. A server callback that
writes diffsim.settings(...) to quantem.diffsim.dash.settings_id(name)
redraws it, for example when the page shows a new structure. The package
README has a complete example.
The preset structure menu is js/diffractionsim-web/presets.ts (about 1 MB),
generated by scripts/diffraction_sim_presets.py at 200 keV and k_max 3.0
1/Å. The file is committed because the web build needs it; run the script
again after changing the presets or the crystal code (--check compares with
the committed file). The settings are listed at the top of
js/diffractionsim-web/index.ts and use the trait names; the web build
defaults to CBED with Kikuchi lines on. A MyST page loads the same module with
the anywidget directive:
:::{anywidget} diffraction-sim.js
{"preset": "Si (diamond cubic)", "zone_axis": [1, 1, 0], "thickness_A": 400}
:::