# STEM Atomic Resolution Image Analysis Skill

## overview

Atomic-resolution STEM image analysis (HAADF, MAADF). Individual atomic
columns are resolved as bright spots on a dark background. Applicable
to any crystalline material viewed along a zone axis. Covers column
detection, sublattice separation, lattice characterization, defect
identification, and structural variation analysis.

## planning

### foundational
Before running detection, estimate the expected number of atomic
columns from the image dimensions, pixel calibration (if available),
and known lattice parameters for the material. Use metadata, any
provided system info, and domain knowledge about the crystal structure
to form this estimate. After detection, compare the actual count
against this estimate — significant discrepancy (outside 0.90-1.10x)
suggests detection issues or a structurally interesting region.

Choose a detection method that reliably finds atomic columns across
the full image despite intensity variations (different species,
thickness gradients). Background subtraction or bandpass filtering
before detection helps with non-uniform illumination. Refine detected
positions with 2D Gaussian fitting for sub-pixel precision. Determine
lattice parameters from the FFT (which directly shows periodicity)
rather than solely from nearest-neighbor distances.

### advanced
**Atom finding tools:** The functions in `scilink.tools.atom_finding_tools`
handle multi-sublattice detection, refinement, and separation. Use them
instead of writing detection code from scratch:
```python
from scilink.tools.atom_finding_tools import (
    detect_atoms, find_zone_axes, find_missing_atoms, subtract_atoms
)
```
Plan the pipeline around these tools: `detect_atoms` for initial column
finding with 2D Gaussian refinement, `find_zone_axes` to identify
lattice vectors, `find_missing_atoms` to predict weaker sublattice
positions at fractional sites, and `subtract_atoms` to remove the
dominant sublattice and reveal fainter ones underneath. For materials
with multiple sublattices (perovskites, layered oxides, rock-salt),
plan for iterative detect→subtract→detect cycles.

**Sublattice separation:** Intensity-based clustering alone is not
sufficient for complex structures. For layered materials, columns of
different species form distinct rows or planes. Use both intensity AND
position within the unit cell to assign sublattices. Verify that the
assignment produces the correct stoichiometric ratios.

**Unit cell identification:** The nearest-neighbor distance is NOT the
lattice parameter for complex structures. The true unit cell repeat
may be 2x, 3x, or more of the shortest column spacing. Use the FFT
to identify the full periodicity, or count how many distinct column
rows/intensities repeat along each direction.

**Structural anomalies:** After fitting the ideal lattice, examine the
spatial distribution of displacements, intensity variations, and local
lattice parameter changes across the image. Regions where these
quantities deviate systematically from the bulk may indicate structural
features worth reporting — let the data guide the interpretation rather
than searching for specific defect types.

## analysis

### foundational
Column detection typically involves: normalization, background
subtraction or bandpass filtering, blob/peak detection, and 2D
Gaussian refinement. Measure basic statistics: column count, intensity
distribution, nearest-neighbor distances, and lattice parameters from
FFT.

### advanced

**Atom finding tools — detailed usage:**

`detect_atoms(image, separation, threshold_rel=0.02, refine=True, percent_to_nn=0.4)`
finds atomic column positions with optional sub-pixel Gaussian refinement.
- **separation** (int): minimum atom spacing in **pixels**. Estimate from
  known lattice parameter / pixel size, or from FFT peak position:
  `separation ≈ image_width / (2 × FFT_peak_distance_from_center)`.
  If unsure, use 70-80% of the apparent nearest-neighbor distance.
- **threshold_rel** (float): peak sensitivity. Default 0.02. Raise to
  0.05-0.1 for noisy images; lower to 0.01 for faint columns.
- **percent_to_nn** (float): Gaussian fit mask as fraction of NN distance.
  0.4 default. Increase for sparse lattices, decrease for dense.
- Returns dict: `"positions"` (N,2 as x,y where x=col y=row),
  `"sigma_x"`, `"sigma_y"`, `"amplitude"`, `"rotation"` (all N arrays).

`find_zone_axes(positions, n_neighbors=9, distance_tolerance=None)`
detects lattice translation vectors by clustering displacement vectors.
- **n_neighbors**: 9 for simple lattices, 15-25 for complex unit cells.
- Returns list of (dx, dy) tuples, shortest first. Square lattice → 2
  vectors, hexagonal → 3. The shortest vector is the NN distance; the
  **lattice parameter** may be 2×+ for multi-sublattice structures.

`find_missing_atoms(positions, zone_vector, fraction=0.5, min_distance=3.0)`
predicts positions at fractional lattice sites along a zone vector.
- **fraction**: 0.5 = midpoint (binary compounds), 0.33/0.67 (ternary).
- **min_distance**: discard predictions within this distance of existing
  atoms (set to ~separation/3).
- Returns (M,2) predictions. Verify that the image has intensity there.

`subtract_atoms(image, positions, sigma_x, sigma_y, amplitude, rotation=None)`
removes fitted Gaussians from the image. Pass all outputs of `detect_atoms`
directly. Returns residual (clipped ≥ 0) where subtracted regions drop to
background. Run `detect_atoms` on the residual with lower threshold to
find the next sublattice.

**Multi-sublattice workflow:**
```
result1 = detect_atoms(image, separation, refine=True)
zone_vecs = find_zone_axes(result1["positions"])
predicted = find_missing_atoms(result1["positions"], zone_vecs[0], fraction=0.5)
residual = subtract_atoms(image, result1["positions"],
                          result1["sigma_x"], result1["sigma_y"],
                          result1["amplitude"], result1["rotation"])
result2 = detect_atoms(residual, separation, threshold_rel=0.01, refine=True)
# Repeat for third sublattice if needed
```
Stop when residual has no peaks above 3× noise std. Validate: check
stoichiometric ratios, heavier atoms should have higher amplitude,
each sublattice's NN distances should be consistent.

**Sublattice separation:** Combine intensity clustering with positional
analysis. After detecting all columns and fitting lattice vectors,
project each column position onto the unit cell to determine its
fractional coordinates. Columns at the same fractional position belong
to the same sublattice. Verify by checking that each sublattice has
consistent intensity (bright = heavy atoms in HAADF).

**Defect identification:** Compare detected positions to ideal lattice
sites. Restrict vacancy search to the interior of the detected region
to avoid edge false positives. Verify vacancy candidates with forced
Gaussian fit at expected position.

**Strain and displacement:** Map displacements from ideal lattice
positions spatially across the image. Only report distortions exceeding
the position fit uncertainty.

## interpretation

### foundational
**HAADF intensity:** Scales as ~Z^1.6-2. Brighter columns contain
heavier atoms.

**Lattice parameters:** Compare against known bulk values. Report in
both pixels and physical units when calibration is available.

### advanced
**Sublattice assignment:** Use chemical identity from intensity and
positional analysis to interpret which sublattice corresponds to which
atomic species.

**Strain:** Distinguish fitted-lattice residuals (local disorder) from
deviations against known ideal lattice (true strain). Least-squares
fitting absorbs mean strain — use known lattice constants when
available.

**Vacancy concentration:** In pristine crystals, typically 0.01-1%.
Above 5-10% usually indicates detection or fitting error, unless the
sample was intentionally modified (irradiation, beam damage, quenching).

## validation

### foundational
**Detection completeness:** Detected vs expected column count (from
image area and unit cell) should be within 0.90-1.10.

**NN distance consistency:** CV below 15% for well-ordered crystals.

**Unit cell sanity:** Measured lattice parameters should be close to
known bulk values when spatial calibration is available.

### advanced
**Sublattice populations:** Should match expected stoichiometry for
the material. Heavy doping can shift intensities between clusters.

**Lattice fit residual:** Below 0.3x the lattice spacing.

**Displacement field:** Mean displacement from ideal lattice should be
small (<0.3x lattice spacing). Large systematic displacements indicate
fitting errors, not real strain.
