Supercell

The central class. Constructs a periodic simulation cell from a reference crystal and provides the full disorder-generation pipeline (Voronoi grain build → optional orientation refinement → FIRE-style relaxation), measurement, visualisation, and HTML-export helpers.

class tricor.Supercell(distribution, cell_dim_angstroms, *, relative_density=0.96, measure_g3=False, plot_g3_compare=False, label=None, rng_seed=None, g3_weight_r_scale=None, g3_weight_exponent=2.0, g3_weight_floor=0.1, spatial_bin_size=None, **kwargs)[source]

Bases: _GrainMixin, _ShellRelaxMixin, _PlottingMixin, _MonteCarloMixin, _ResampleMixin

Random supercell scaffold driven by a target G3Distribution.

Construction

Parameters:
  • distribution (G3Distribution) –

  • cell_dim_angstroms (float | Sequence[float]) –

  • relative_density (float) –

  • measure_g3 (bool) –

  • plot_g3_compare (bool) –

  • label (str | None) –

  • rng_seed (int | None) –

  • g3_weight_r_scale (float | None) –

  • g3_weight_exponent (float) –

  • g3_weight_floor (float) –

  • spatial_bin_size (float | None) –

  • kwargs (Any) –

classmethod from_atoms(atoms, cell_dim_angstroms, *, r_max=10.0, r_step=0.2, phi_num_bins=90, relative_density=0.96, rng_seed=None, label=None, **kwargs)[source]

Create a random supercell directly from a reference crystal.

This is a convenience constructor that measures the reference g3 distribution internally, avoiding the need to create a G3Distribution manually. Use generate() to build the desired structure (amorphous, nanocrystalline, etc.).

Parameters:
  • atoms (Atoms) – Reference crystal structure (ASE Atoms object).

  • cell_dim_angstroms (float | Sequence[float]) – Physical supercell lengths in Angstrom. A single scalar produces a cubic box.

  • r_max (float) – Maximum radius for the g3 measurement grid.

  • r_step (float) – Radial bin width for the g3 measurement grid.

  • phi_num_bins (int) – Number of angular bins for the g3 measurement grid.

  • relative_density (float) – Density relative to the crystalline reference.

  • rng_seed (int | None) – Random seed for reproducible initialization.

  • label (str | None) – Human-readable label.

  • **kwargs (Any) – Forwarded to Supercell.__init__().

Returns:

A new random supercell ready for generate() or shell_relax().

Return type:

Supercell

Generation

generate(shell_target, num_steps=200, *, grain_size=None, crystalline_fraction=1.0, bond_weight=1.0, angle_weight=0.5, repulsion_weight=3.0, hard_core_scale=1.0, nonbond_push_scale=1.0, displacement_sigma=0.0, atom_species_index=None, grain_sources=None, refine_orientations=False, refine_orientations_kwargs=None, show_progress=True, **shell_relax_kwargs)[source]

Generate a disordered supercell from liquid to nanocrystalline.

Covers the full spectrum of disorder by combining Voronoi grain construction with spring-network relaxation. See PRESETS for recommended parameter sets for Si.

Parameters:
  • shell_target (CoordinationShellTarget) – First-shell coordination targets from the reference crystal.

  • num_steps (int) – Number of relaxation sweeps.

  • grain_size (float | None) – Diameter of crystalline grains in Angstrom. None means no grains - start from random positions (liquid/amorphous).

  • crystalline_fraction (float) – Volume fraction filled by crystalline grains (0–1). Only used when grain_size is set. The remaining volume is filled with random (amorphous) positions.

  • bond_weight (float) – Harmonic spring strength pulling bonded neighbours toward the target bond distance. Larger = tighter distances.

  • angle_weight (float) – Spring strength pushing bond angles toward the target angle. Larger = tighter angles. Near-zero = liquid-like freedom.

  • displacement_sigma (float) – Gaussian displacement (Angstrom) applied to atoms within crystalline grains as thermal broadening. 0 = no jitter.

  • show_progress (bool) – Display a text progress bar.

  • **shell_relax_kwargs (Any) – Additional keyword arguments forwarded to shell_relax() (e.g. repulsion_weight, hard_core_scale, step_size).

  • repulsion_weight (float) –

  • hard_core_scale (float) –

  • nonbond_push_scale (float) –

  • atom_species_index (ndarray | None) –

  • grain_sources (list[dict] | None) –

  • refine_orientations (bool) –

  • refine_orientations_kwargs (dict | None) –

Returns:

Summary dict with regime, construction parameters, and relaxation loss values.

Return type:

dict[str, Any]

Orientation refinement

refine_initial_orientations(shell_target, *, amplitudes_deg=(30.0, 15.0, 5.0, 2.0), trials_per_amplitude_per_grain=50, max_rounds_per_amplitude=2, bond_weight=1.0, angle_weight=0.5, repulsion_weight=3.0, hard_core_scale=1.0, nonbond_push_scale=1.0, time_budget_sec=120.0, final_fire_steps=0, capture_trajectory=False, cost_function='pair_distance', score_cutoff_factor=1.5, topology_rebuild='per_grain', rng_seed=None, show_progress=True)

Optimise per-grain rotations via SO(3) coordinate descent BEFORE the global FIRE quench.

Walks each Voronoi grain through a sequence of progressively finer rotation perturbations, accepting any rotation that lowers a fast topology-free pair-distance score against the grain’s local environment. Designed to be called between the Voronoi tile (which assigns random initial rotations) and the global FIRE quench. The intended workflow is:

cell.generate(shell, num_steps=0, ...)        # build only
cell.refine_initial_orientations(shell)        # this method
cell.shell_relax(shell, num_steps=150, ...)    # FIRE quench

Or equivalently use cell.generate(refine_orientations=True, refine_orientations_kwargs=...) which chains the three steps in one call.

Parameters:
  • shell_target (CoordinationShellTarget) – Target whose pair_peak defines the per-pair bond length the score targets.

  • amplitudes_deg (tuple of float, optional) – Schedule of rotation amplitudes (degrees) the SO(3) coordinate search walks through. Default (30, 15, 5, 2): the largest step lets a misaligned grain escape its starting basin, the smallest step locks in the chosen orientation.

  • trials_per_amplitude_per_grain (int, optional) – Number of random rotations sampled per (amplitude, grain). Default 50. The best-scoring trial is accepted if it beats the current orientation by more than score_cutoff_factor.

  • max_rounds_per_amplitude (int, optional) – Number of full passes over all grains within one amplitude phase. Default 2.

  • bond_weight (float, optional) – Spring weights forwarded to the per-trial score. Only used when cost_function="cached_topology"; the default "pair_distance" mode ignores them.

  • angle_weight (float, optional) – Spring weights forwarded to the per-trial score. Only used when cost_function="cached_topology"; the default "pair_distance" mode ignores them.

  • repulsion_weight (float, optional) – Spring weights forwarded to the per-trial score. Only used when cost_function="cached_topology"; the default "pair_distance" mode ignores them.

  • hard_core_scale (float, optional) – Repulsion thresholds passed through to the per-trial score’s clash-penalty term.

  • nonbond_push_scale (float, optional) – Repulsion thresholds passed through to the per-trial score’s clash-penalty term.

  • time_budget_sec (float, optional) – Wall-time guard rail (seconds). The search bails after this even if amplitudes remain. Default 120.

  • final_fire_steps (int, optional) – If > 0, run a whole-cell shell_relax for this many steps after the SO(3) search completes — bakes a final quench into a single call. Default 0 (caller is expected to run their own shell_relax).

  • capture_trajectory (bool, optional) – Record the cell’s atom positions at every accepted rotation. Default False (faster). Set to True for trajectory-replay HTML export.

  • cost_function ({"pair_distance", "cached_topology"}, optional) – Score to minimise. "pair_distance" (default, recommended) is a topology-free Σ (d - pair_peak)² over neighbour pairs in the grain’s local frame — fast and consistent across grains and trials. "cached_topology" uses _total_energy_fast with a rebuilt bond list (more physically faithful but the rebuilt topology drifts as positions change and can walk into a worse basin on big cells).

  • score_cutoff_factor (float, optional) – Acceptance threshold relative to the current baseline score. Higher values accept more aggressively. Default 1.5.

  • topology_rebuild ({"per_grain", "per_amp", "once"}, optional) – Cadence for rebuilding the bond list (only used when cost_function="cached_topology"). Default "per_grain".

  • rng_seed (int, optional) – Seed for the random rotation sampler. None (default) uses the cell’s own RNG.

  • show_progress (bool, optional) – Display a tqdm progress bar over the (amplitudes × rounds × grains) workload. Default True.

  • self (Supercell) –

Returns:

History captured under self.refine_initial_orientations_history:

  • iteration (ndarray of int) — accept indices, starting at 0 for the initial state.

  • global_cost (ndarray of float) — total cost at each accepted state.

  • cost_bond / cost_angle / cost_rep — cost decomposition (only populated for cost_function="cached_topology").

  • accepted_grain (ndarray of int) — grain index that moved at each acceptance (-1 for the initial state).

  • rotation_amplitude_deg (ndarray of float) — the current amplitude phase at each acceptance.

  • amplitude_phase (ndarray of int) — phase index into amplitudes_deg.

  • trajectory (ndarray of float32, optional) — only present when capture_trajectory=True: positions (num_accepts, num_atoms, 3) at each accepted rotation.

Return type:

dict

refine_grains(shell_target, *, time_budget_sec=300.0, max_passes=20, n_rot=8, n_trans=4, rotation_min_deg=10.0, rotation_max_deg=180.0, local_fire_steps=50, neighbor_shell_radius_factor=2.5, bond_weight=1.0, angle_weight=0.5, repulsion_weight=3.0, hard_core_scale=1.0, nonbond_push_scale=1.0, final_quench_steps=200, capture_trajectory=True, show_progress=True, rng_seed=None)

Iterative per-grain rotation refinement with local FIRE.

Walks the grain list round-robin; for each grain, tries n_rot * n_trans candidate (rotation, translation) pairs, keeping the lowest-cost outcome. A trial is judged by total bond + angle + repulsion cost after a short local_fire_steps-step FIRE pass on the grain plus its neighbour shell.

Returns the history dict that gets stashed on self.refine_grains_history and used by Supercell.export_trajectory_html(history='refine_grains')().

Parameters:
  • time_budget_sec (float) – Stop after this many seconds of wall-clock have elapsed. For demo-quality results 60 - 300 s is plenty; for production runs raise to 1800 - 3600 s.

  • max_passes (int) – Stop after this many full round-robin passes regardless of time. A pass with zero accepts triggers early convergence.

  • n_rot (int, optional) – Per-grain rotation trials. Wall time scales linearly with n_rot * n_trans * num_grains.

  • n_trans (int, optional) – Per-grain translation trials. Combined with n_rot sets the basin coverage per grain.

  • rotation_min_deg (float, optional) – Lower bound on trial rotation angle (degrees). Lower values encourage fine local refinements late in the search.

  • rotation_max_deg (float, optional) – Upper bound on trial rotation angle (degrees). Trial rotations sample uniformly in [rotation_min_deg, rotation_max_deg] about a uniformly-random axis.

  • neighbor_shell_radius_factor (float) – The local FIRE region around each grain extends radius_factor * pair_peak_max Å beyond the grain atoms. ~2.5 covers the grain’s own atoms plus the atoms in adjacent grains that share a Voronoi face, which is what bonds across grain boundaries actually depend on.

  • local_fire_steps (int) – Number of FIRE descent steps per trial. 50 is enough to absorb the boundary strain from a fresh rotation; raising past 100 wastes budget.

  • bond_weight (float, optional) – Spring weight for bond-distance terms. Mirror the value used when the cell was originally generated.

  • angle_weight (float, optional) – Spring weight for bond-angle terms.

  • repulsion_weight (float, optional) – Spring weight for hard-core + nonbond-clearance terms.

  • hard_core_scale (float, optional) – Multiplier on shell_target.pair_inner setting the minimum allowed pair distance.

  • nonbond_push_scale (float, optional) – Multiplier on shell_target.pair_peak setting the non-bonded shell-clearance radius.

  • final_quench_steps (int) – After the round-robin loop finishes, run this many full-cell FIRE steps (no freezing, no restraint) to lock in the global minimum. Set 0 to skip.

  • capture_trajectory (bool) – When True, append a frame to the history each time a grain rotation is accepted. Adds the initial state and the post-final-quench state too.

  • self (Supercell) –

  • shell_target (CoordinationShellTarget) –

  • show_progress (bool) –

  • rng_seed (int | None) –

Return type:

dict

Notes

Wall-clock for the demo runs in this repo (cell_dim_angstroms = (40, 40, 40)):

regime

grains

~accepts/run

wall time

amorphous

~600

~50

5–7 min

MRO

~50

~10

2–3 min

nano (NC)

~5

~3

30–60 s

For production-quality output (e.g. ML training datasets), raise time_budget_sec to 1800 - 3600, raise n_rot to 16 - 32, and run multiple seeds in parallel.

refine_grains_coarse_to_fine(shell_target, *, initial_uniform_trials=32, angle_schedule_deg=(45.0, 22.0, 11.0, 5.0, 2.0, 1.0), trials_per_amplitude=12, max_rounds_per_amplitude=3, local_fire_steps=50, neighbor_shell_radius_factor=2.5, bond_weight=1.0, angle_weight=0.5, repulsion_weight=3.0, hard_core_scale=1.0, nonbond_push_scale=1.0, final_quench_steps=200, time_budget_sec=180.0, capture_trajectory=True, show_progress=True, rng_seed=None)

Coarse-to-fine basin hopping for per-grain rotations.

Walks an angle-amplitude schedule from coarse to fine. For each (amplitude, grain) pair, samples trials_per_amplitude candidate rotations bounded in angle by amplitude and composed onto the grain’s current orientation, applies a short local FIRE on each, and accepts the lowest-cost outcome. The same amplitude is repeated up to max_rounds_per_amplitude rounds while it keeps producing accepts; then the schedule steps down.

This finds dozens of accepts where uniform-SO(3) sampling finds only 1-2 because every trial is anchored to the current orientation, so fine refinements within the chosen basin keep yielding small improvements; translations are also bounded by the amplitude (trans_scale = amplitude / 180°); and multi-round at each amplitude exhausts the local search before stepping to a smaller amplitude.

Parameters:
  • shell_target (CoordinationShellTarget) – Target whose pair_peak defines the per-pair bond length the cost function targets.

  • initial_uniform_trials (int, optional) – Stage-1 uniform-SO(3) basin search per grain (rotations sampled independently of the current orientation, used only to escape the seed orientation). Default 32. Set 0 to skip the basin search.

  • angle_schedule_deg (tuple of float, optional) – Stage-2 amplitude schedule (degrees), coarse → fine. Default (45, 22, 11, 5, 2, 1).

  • trials_per_amplitude (int, optional) – Candidate rotations per (amplitude, grain). Default 12.

  • max_rounds_per_amplitude (int, optional) – Maximum round-robin passes over all grains within one amplitude phase. Default 3.

  • local_fire_steps (int, optional) – FIRE steps applied to each trial’s neighbour shell to evaluate its cost. Default 50.

  • neighbor_shell_radius_factor (float, optional) – Local-FIRE neighbour shell extends to this multiple of shell_target.pair_peak around the perturbed grain. Default 2.5.

  • bond_weight (float, optional) – Spring weights forwarded to the per-trial FIRE + cost evaluation.

  • angle_weight (float, optional) – Spring weights forwarded to the per-trial FIRE + cost evaluation.

  • repulsion_weight (float, optional) – Spring weights forwarded to the per-trial FIRE + cost evaluation.

  • hard_core_scale (float, optional) – Repulsion thresholds for the per-trial FIRE.

  • nonbond_push_scale (float, optional) – Repulsion thresholds for the per-trial FIRE.

  • final_quench_steps (int, optional) – Whole-cell FIRE quench steps applied after the SO(3) search completes. Default 200; set 0 to skip.

  • time_budget_sec (float, optional) – Wall-time guard rail (seconds). The search bails after this even if amplitudes remain. Default 180.

  • capture_trajectory (bool, optional) – Record per-frame atom positions after each acceptance. Default True (needed for trajectory-replay HTML).

  • show_progress (bool, optional) – Display a tqdm progress bar. Default True.

  • rng_seed (int, optional) – Seed for the rotation sampler. None (default) uses the cell’s own RNG.

  • self (Supercell) –

Returns:

History captured under self.refine_grains_coarse_to_fine_history — same shape as refine_grains()’s history (so the trajectory + cost plotters work unchanged), plus a rotation_amplitude_deg array marking which amplitude phase produced each accept.

Return type:

dict

Relaxation

shell_relax(shell_target, num_steps=200, *, bond_weight=1.0, angle_weight=0.5, repulsion_weight=3.0, k_restraint=0.0, r_initial_override=None, freeze_mask=None, freeze_grain_interiors=False, hard_core_scale=1.0, nonbond_push_scale=1.0, step_size=0.1, step_decay=0.995, neighbor_update_interval=10, neighbor_cutoff_scale=1.5, max_force_clip=2.0, capture_trajectory=False, show_progress=True)

Relax random positions to match first-shell targets using spring forces.

Moves all atoms simultaneously each step via three vectorized force terms: bond springs toward the target nearest-neighbor distance, angle springs toward the target bond angle, and soft repulsion to eliminate overlaps and close-packed background. Bond topology (K-nearest assignment) is rebuilt periodically using ASE’s neighbor_list.

Parameters:
  • shell_target (CoordinationShellTarget) – First-shell coordination targets extracted from the reference crystal via CoordinationShellTarget.from_atoms().

  • num_steps (int) – Number of relaxation sweeps.

  • bond_weight (float) – Strength of the harmonic spring pulling bonded neighbors toward pair_peak distance.

  • angle_weight (float) – Strength of the angular spring pushing bond angles toward angle_mode_deg.

  • repulsion_weight (float) – Strength of the short-range repulsive force below pair_hard_min.

  • k_restraint (float) – Spring constant (eV / Ų) for a global position-restraint energy ½ k_restraint Σ ‖r_i - r_initial_i‖² that tethers every atom to its starting position. 0.0 (default) disables the term and reproduces unrestrained relaxation. Small values (~0.1 - 1.0) preserve regime character (grain layout, amorphous topology) while still permitting local relaxation; large values (≫ 10) hold the structure rigid.

  • hard_core_scale (float) – Multiplier for the hard-core repulsion radius. 1.0 uses max(pair_hard_min, pair_inner) as the wall. Values below 1.0 allow shorter bonds (softer wall for liquid). Values above 1.0 enforce a larger exclusion zone.

  • nonbond_push_scale (float) – Multiplier for the non-bonded shell clearance distance. 1.0 pushes non-bonded atoms to 1.5 * pair_peak. Values below 1.0 allow non-bonded atoms closer (broader 2nd shell for liquid). 0.0 disables non-bonded push.

  • step_size (float) – Initial maximum displacement per step (Angstrom).

  • step_decay (float) – Multiplicative decay applied to step_size each iteration.

  • neighbor_update_interval (int) – Rebuild the bond topology every this many steps.

  • neighbor_cutoff_scale (float) – Neighbor search cutoff as a multiple of max_pair_outer.

  • max_force_clip (float) – Per-atom force magnitude is clipped to this value before integration to keep the dynamics stable.

  • freeze_grain_interiors (bool) – If True, atoms identified as deep grain interior (more than 0.5 × max(pair_peak) away from the nearest grain-boundary plane) are held fixed during relaxation. False (default since 2026-05) lets every atom relax, which is required for multi-species systems where the interior atoms must accommodate cross-species spring strain (SiO2, SrTiO3, sp²/sp³ carbon). Setting True reproduces the pre-2026 behaviour and is occasionally useful for single-species nanocrystalline cells where the interiors are already at their target geometry. Has no effect on cells built without a grain_size.

  • show_progress (bool) – Display a text progress bar.

  • self (Supercell) –

  • r_initial_override (np.ndarray | None) –

  • freeze_mask (np.ndarray | None) –

  • capture_trajectory (bool) –

Returns:

Summary with parameters and final/initial loss values.

Return type:

dict[str, Any]

bond_relax(shell_target, n_iter=40, attract_frac=0.2, repel_frac=1.0, max_step=0.2)[source]

Combined attract-to-bond-peak + repel-from-hard-core sweep.

A fast O(N log N) alternative to a full FIRE relaxation for cleaning up Voronoi-tiled or ML-predicted positions. Each iteration:

  • Pulls bonded species pairs (those with non-zero shell_target.coordination_target) toward shell_target.pair_peak.

  • Pushes any pair below shell_target.pair_hard_min apart.

Mutates self.atoms.positions in place. Uses scipy.spatial.cKDTree so cost scales linearly with N at constant density — at 200³ Å × 600 k atoms, ~1 s/iter on CPU. Drives Si-O to its 1.61 Å peak and non-bonded pairs (Si-Si, O-O) onto their hard-core walls in 40-80 iterations.

Parameters:
  • shell_target – The CoordinationShellTarget whose pair_peak / pair_hard_min / pair_outer / coordination_target matrices drive the forces.

  • n_iter (int) – Number of sweeps. 40 typically reaches the bond peak to within 0.01 Å.

  • attract_frac (float) – Per-sweep gap-closing fraction toward the bond peak.

  • repel_frac (float) – Per-sweep gap-closing fraction away from the hard-core wall. 1.0 (default) closes the gap in one shot, with max_step providing the safety against overshoot in dense regions.

  • max_step (float) – Per-atom displacement cap (Å) per sweep.

Return type:

None

enforce_hard_core(shell_target, n_iter=40, push_fraction=0.5)[source]

Geometric projection step that clears hard-core overlaps.

Iteratively finds pairs below shell_target.pair_hard_min (via scipy.spatial.cKDTree) and pushes each violating pair apart along their bond vector by push_fraction × deficit. Pure geometry - no force springs - so it cannot pull a pair through its wall the way the FIRE finisher’s bond springs can. Use this as a final cleanup whenever you suspect FIRE’s bond-spring forces have compressed pairs below their hard-core distance in dense regions (a real failure mode at 100+ Å cells).

Mutates self.atoms.positions in place. Cost: ~1 s per sweep at 200³ Å × 600 k atoms; converges in roughly O(log(initial_deficit / push_fraction)) sweeps for moderate overlaps, more for severe ones from a fresh Voronoi tile. Defaults are tuned to clear NB 01 / NB 02-scale overlaps in a single call.

Parameters:
  • shell_target – The CoordinationShellTarget whose pair_hard_min matrix sets the wall distances.

  • n_iter (int) – Number of projection sweeps. Early-terminates if no violations remain.

  • push_fraction (float) – Per-iter fraction of the deficit to close. 0.5 (default) is the natural choice - both atoms move symmetrically and meet in the middle. Larger values risk overshoot; smaller values just need more sweeps.

Return type:

None

thermal_relax(shell_target, *, num_sweeps=1000, T_schedule='anneal', T_start=0.05, T_end=0.001, hold_sweeps=200, step_sigma=0.05, smart_dt=0.02, adapt_step=True, target_accept=0.4, move_probs=None, bond_weight=1.0, angle_weight=0.5, repulsion_weight=3.0, k_restraint=0.0, hard_core_scale=1.0, nonbond_push_scale=1.0, neighbor_update_interval=100, rep_neighbor_update_interval=20, capture_stride=10, capture_trajectory=True, restore_best=True, freeze_interior=None, freeze_mask=None, grain_moves=None, grain_move_interval=1, grain_sigma_rot=0.01, grain_sigma_trans=0.01, show_progress=True)

Temperature-dependent Metropolis Monte-Carlo relaxation.

Sits alongside shell_relax() - same spring-network energy (bond + angle + repulsion from shell_target), but moves atoms by Metropolis accept/reject with a temperature schedule. Good for escaping the local minima that gradient descent settles into and for producing annealed amorphous configurations.

A typical workflow is: run shell_relax() or generate() once to bring the initial random / tile configuration into a low-energy basin, then call thermal_relax() with a moderate-to-cold anneal schedule to explore the basin and settle into the global minimum of that basin.

Parameters:
  • shell_target (CoordinationShellTarget) – First-shell coordination targets, same object used by shell_relax().

  • num_sweeps (int) – Number of sweeps. Each sweep performs len(atoms) trial moves.

  • T_schedule"hold" (constant T_start), "anneal" (hold at T_start for hold_sweeps then linearly drop to T_end), or a callable(sweep_index) -> T.

  • T_start (float) – Parameters for the "anneal" schedule.

  • T_end (float) – Parameters for the "anneal" schedule.

  • hold_sweeps (int) – Parameters for the "anneal" schedule.

  • step_sigma (float) – Initial Gaussian trial-move amplitude in Å.

  • adapt_step (bool) – If True, adapt step_sigma every 20 sweeps to target target_accept acceptance rate.

  • move_probs (dict | None) – Dictionary with optional keys "displace", "swap", "smart". Weights are normalised; default {"displace": 1.0}. "swap" exchanges virtual species between two atoms of different species; useful for multi-element + composite-shell-target systems. "smart" is a force-biased Langevin proposal with Metropolis correction (reserved for v2; currently treated as no-op).

  • bond_weight (float, optional) – Same as shell_relax(). Default 1.0.

  • angle_weight (float, optional) – Same as shell_relax(). Default 0.5.

  • repulsion_weight (float, optional) – Same as shell_relax(). Default 3.0.

  • k_restraint (float, optional) – Spring constant (eV / Ų) for a global position-restraint energy ½ k_restraint Σ ‖r_i - r_initial_i‖² that tethers every atom to its starting position. 0.0 (default) disables the term and reproduces unrestrained MC. Small values (~0.1 - 1.0) preserve the input regime character (grain layout, amorphous topology) while still permitting local relaxation; large values (≫ 10) hold the structure essentially rigid. Differentiable + globally defined, so unlike a hard freeze_interior the cost surface stays smooth and the relaxation can find consistent low-strain configurations across grain boundaries.

  • hard_core_scale (float, optional) – Same as shell_relax(). Default 1.0.

  • nonbond_push_scale (float, optional) – Same as shell_relax(). Default 1.0.

  • neighbor_update_interval (int) – Rebuild the bond topology every this many sweeps.

  • capture_stride (int) – Store a history frame every this many sweeps. Smaller = finer trajectory movie, larger = less memory.

  • capture_trajectory (bool) – Whether to store per-sweep positions. Disable for very long runs where only cost / T / accept_rate matter.

  • freeze_interior (bool | None) – Legacy hard-freeze of crystalline grain interiors. Only takes effect when explicitly set to True and the cell has populated _grain_ids / _grain_seeds (i.e. came from generate() with grain_size). Default None leaves all atoms free; prefer k_restraint > 0 for a smooth, differentiable, regime-preserving alternative.

  • freeze_mask (np.ndarray | None) – Explicit per-atom freeze mask of shape (num_atoms,). True entries are pinned for the entire run. Overrides freeze_interior. Useful when the cell isn’t grain-tiled but you still want to hold specific atoms (e.g. an interface) fixed.

  • grain_moves (bool, optional) – If True (or None and the cell has ≥2 grains), propose rigid rotation + translation of each grain every grain_move_interval sweeps.

  • grain_move_interval (int, optional) – Sweep cadence of the rigid-grain proposals. Default 1 (one set of grain moves per sweep when enabled).

  • grain_sigma_rot (float, optional) – Std-dev of the per-grain rotation angle (radians). Default 0.01.

  • grain_sigma_trans (float, optional) – Std-dev of the per-grain translation (Å). Default 0.01.

  • self (Supercell) –

  • smart_dt (float) –

  • target_accept (float) –

  • rep_neighbor_update_interval (int) –

  • restore_best (bool) –

  • show_progress (bool) –

Returns:

History dictionary assigned to self.thermal_relax_history. Layout matches shell_relax_history() where possible, so export_trajectory_html() can visualise the trajectory unchanged.

Return type:

dict

Measurement

measure_g3(*, force=False, show_progress=True, backend='auto', sample_fraction=1.0, sample_rng_seed=None)

Measure the current random supercell on the target distribution grid.

Parameters:
  • force (bool) – If True, discard any cached measurement and recompute.

  • show_progress (bool) – If True, display a text progress bar while the supercell histogram is accumulated.

  • backend (str) – Kernel selector forwarded to G3Distribution.measure_g3(). "auto" (default) picks the numba-parallel path when numba is installed.

  • sample_fraction (float) – Monte-Carlo origin subsampling. sample_fraction < 1.0 iterates only a uniform random subset of origin atoms (preserves PBC tile, full neighbour set). At 200³ Å set this to ~``(40/200)**3`` so the histogram cost matches a 40³ Å reference cell.

  • sample_rng_seed (int | None) – Monte-Carlo origin subsampling. sample_fraction < 1.0 iterates only a uniform random subset of origin atoms (preserves PBC tile, full neighbour set). At 200³ Å set this to ~``(40/200)**3`` so the histogram cost matches a 40³ Å reference cell.

  • self (Supercell) –

Return type:

G3Distribution

sync_g3(*, show_progress=True)

Recompute the supercell’s g2 + g3 from scratch and rebuild Monte-Carlo caches.

Forces a fresh measure_g3() (bypassing any cached result) and then re-initialises the per-atom contribution tables that monte_carlo() uses to compute incremental ΔG3 / Δcost on each trial move. Call after externally modifying self.atoms.positions (e.g. injecting a numpy.random.normal() thermal jitter) or after chaining a shell_relax() between MC rounds — otherwise the MC caches refer to stale neighbour pairings and the proposed-move statistics drift.

Parameters:
  • show_progress (bool, optional) – Display a tqdm-style progress bar while the underlying measure_g3() iterates. Default True.

  • self (Supercell) –

Returns:

The freshly measured distribution, also stored as self.current_distribution.

Return type:

G3Distribution

Inline visualisation (Jupyter)

view_structure(shell_target=None, *, polyhedra=True, **kwargs)

Return an interactive 3D structure viewer widget.

Renders atoms as spheres (coloured by element). Two overlay modes, independently toggle-able in the widget:

  • Bonds - cylinders between atoms within a radial cutoff.

  • Polyhedra - translucent tetrahedra / octahedra / cuboctahedra around atoms that pass a distance + angle tolerance check (see export_trajectory_html() / _detect_tetrahedra() for the underlying algorithm). Enabled by default for materials whose coordination polyhedron we can auto-detect (Si / C tetrahedra at half-scale, Cu cuboctahedra, SiO2 tetrahedra, SrTiO3 octahedra).

Sliders in the side panel let you tune the radial tolerance, angular tolerance, centre-vertex bond length, and polyhedra scale (0.5 places vertices at bond midpoints, 1.0 at atoms).

Parameters:
  • shell_target (CoordinationShellTarget | None) – Sets the default bond cutoff and bond_length from shell_target.max_pair_outer / pair_peak. If None, uses the shell_target stored from the last generate() call.

  • polyhedra (dict | bool | None) – Polyhedra config. True / None (default) auto-pick kind + settings from species; False disables polyhedra; a dict overrides individual settings - e.g. {'kind': 'octahedra', 'center_symbol': 'Ti', 'vertex_symbol': 'O', 'bond_length': 1.96}.

  • **kwargs – Forwarded to StructureWidget (e.g. atom_scale, bond_cutoff, show_bonds, slab_x, etc.).

  • self (Supercell) –

Returns:

An anywidget instance for display in Jupyter.

Return type:

StructureWidget

plot_structure(shell_target=None, *, output=None, width=1024, height=1024, fps=60, duration=6.0, elevation=15.0, atom_size=10.0, bond_cutoff=None, show_cell=True, show_atoms=True, background='white', colormap='Reds', tetrahedral_thresh=0.4, show_progress=True)

Render a bond-centric rotating 3D view of the atomic structure.

Bonds are the primary visual: crystalline (tetrahedral) bonds are drawn thick and coloured by depth; boundary / amorphous bonds are drawn faint. Atoms are optional small dots. The animation performs a full periodic 360-degree rotation.

Classification follows the MATLAB plotAtoms02 convention: an atom is crystalline if it has exactly K nearest neighbours within bond_cutoff and the mean displacement of those neighbours is less than tetrahedral_thresh (i.e. the local coordination is symmetric / tetrahedral).

Parameters:
  • shell_target (CoordinationShellTarget | None) – First-shell targets. Used to set bond_cutoff and the coordination number K automatically.

  • output (str | None) – File path for a .mp4 (recommended) or .gif. None shows a static figure.

  • width (int) – Frame size in pixels.

  • height (int) – Frame size in pixels.

  • fps (int) – Frames per second (GIF only).

  • duration (float) – Total GIF length in seconds. Rotation is always exactly 360 degrees so the loop is seamless.

  • elevation (float) – Camera elevation in degrees.

  • atom_size (float) – Matplotlib scatter marker size. Set to 0 to hide atoms.

  • bond_cutoff (float | None) – NN bond length cutoff in Angstrom.

  • show_cell (bool) – Draw the periodic cell outline.

  • show_atoms (bool) – Draw atom dots.

  • background (str) – Figure background colour.

  • colormap (str) – Matplotlib colormap for crystalline bonds (coloured by depth / y-coordinate after rotation, like MATLAB bone).

  • tetrahedral_thresh (float) – Maximum norm of mean NN displacement vector for an atom to be classified as crystalline. Smaller = stricter.

  • show_progress (bool) – Print frame counter during GIF rendering.

  • self (Supercell) –

plot_g2(*, r_max=10.0, r_step=0.05, title='', height=420, show_progress=False)

Return an inline Jupyter display of the g(r) pair-correlation viewer.

Convenience wrapper around export_g2_html() that packages the HTML as an IPython.display.HTML object so you can just do cells['MRO'].plot_g2() in a notebook cell. The viewer is embedded via a srcdoc iframe so it renders isolated from the surrounding notebook CSS / JS.

Parameters:
plot_g3(pair=0, *, normalize=True)

Return an interactive explorer for the supercell’s measured g3.

Requires measure_g3() to have been called first.

Parameters:
  • pair (int | str) – Triplet index or label (e.g. 0 or "Si-Si-Si").

  • normalize (bool) – If True, display the reduced (density-normalised) g3.

  • self (Supercell) –

plot_g3_compare(pair=0, *, normalize=True)

Interactive side-by-side comparison of the current supercell’s g3 against its target g3.

Renders an anywidget-based two-panel viewer in Jupyter: left panel is the supercell’s measured g3 for the chosen species-pair triplet channel, right panel is the corresponding target g3 (set via Supercell.target_distribution). Drag the radial-shell slider below either panel to inspect a g3 slice at fixed root-bond radius.

Parameters:
  • pair (int or str, optional) – Which triplet channel to display. Either an integer index into target_distribution.angle_index or a string label like "Si-Si-Si" resolved by G3Distribution._resolve_pair_index(). Default 0 (first channel).

  • normalize (bool, optional) – If True (default), normalise both g3 values by the uniform-random reference so bins read as enhancements (>1) or depletions (<1). If False, raw counts.

  • self (Supercell) –

Returns:

Interactive anywidget instance. Display it inline in Jupyter (returning the widget from a cell auto-renders).

Return type:

G3CompareWidget

plot_shell_relax(*, log_y=False)

Plot the FIRE relaxation loss history captured by the most recent shell_relax() call.

Renders the per-step total loss alongside its best-so-far envelope and the three component contributions (bond, angle, repulsion). When the run was launched with k_restraint > 0, the position-restraint contribution is added as a fifth curve in purple.

Parameters:
  • log_y (bool, optional) – Display the loss axis on a log scale. Useful for runs that span several orders of magnitude (e.g. num_steps > 200 with stiff springs). Default False.

  • self (Supercell) –

Returns:

The created figure.

Return type:

matplotlib.figure.Figure

Raises:

ValueError – If shell_relax() has not been run yet (self.shell_relax_history is None).

plot_thermal_relax(*, log_y=False, log_x=False)

Plot thermal-MC history: cost + T on shared x-axis.

Two stacked panels with sharex=True so the hold-plateau / anneal-ramp shape in temperature lines up visually with the cost trajectory.

Parameters:
  • log_y (bool) – Log-scale the cost axis. Recommended for convergence checking - a converged run shows a clear plateau when the cost curve flattens on a log scale.

  • log_x (bool) – Log-scale the sweep axis. Off by default because a typical hold+anneal schedule is linear in sweep index, so log-x distorts the temperature ramp visually. Useful if your schedule spans many orders of magnitude in sweep number or you want to emphasize early-sweep dynamics.

  • self (Supercell) –

plot_thermal_before_after(*, r_max=8.0, title=None)

Compare g(r) before and after the most recent thermal_relax() call.

Builds a g(r) overlay viewer with two curves: the cell state cached at the start of thermal_relax() (“before”) and the current state after the Monte-Carlo run completes (“after”). Useful for visualising how an anneal schedule sharpened or broadened the radial distribution.

Parameters:
  • r_max (float, optional) – Maximum radial distance (Å) plotted on the x-axis. Default 8.0 Å.

  • title (str, optional) – Title shown above the viewer. Default uses the supercell’s label.

  • self (Supercell) –

Returns:

The rendered comparison viewer (auto-displays inline in Jupyter when returned from a cell).

Return type:

IPython.display.HTML

Raises:

ValueError – If thermal_relax() has not been run yet (no cached pre-thermal snapshot or history).

plot_monte_carlo(*, log_y=False, show_run_boundaries=True)

Plot the Monte-Carlo cost history captured by the most recent monte_carlo() call.

Two curves are drawn on a single matplotlib axis: instantaneous cost (current MC state) and best-so-far cost (envelope). Vertical dashed markers separate consecutive monte_carlo runs when show_run_boundaries=True.

Parameters:
  • log_y (bool, optional) – Display the cost on a logarithmic y-axis. Useful for anneal schedules that span several orders of magnitude. Default False.

  • show_run_boundaries (bool, optional) – If True, draw vertical dashed lines at the start of each new MC run when mc_history["run_index"] increments (e.g. when chained calls extend the history). Default True.

  • self (Supercell) –

Returns:

The created figure.

Return type:

matplotlib.figure.Figure

Raises:

ValueError – If monte_carlo() has not been run yet (self.mc_history is None).

HTML export

export_trajectory_html(output_path, *, bond_cutoff=None, atom_scale=0.17, bond_radius=0.06, background_color='#f7f8f5', title='', tetrahedra=None, tetrahedra_color=(0.35, 0.45, 0.95), tetrahedra_opacity=0.45, octahedra=None, octahedra_color=(0.95, 0.55, 0.25), octahedra_opacity=0.4, cuboctahedra=None, cuboctahedra_color=(0.55, 0.35, 0.85), cuboctahedra_opacity=0.4, polyhedra_groups=None, show_bonds=None, history=None)

Export an interactive 3D trajectory viewer as a self-contained HTML file.

Requires shell_relax() or generate() to have been run with capture_trajectory=True. The resulting HTML embeds the full position trajectory, uses Three.js (from CDN) for rendering, and provides play/pause/slider controls.

Parameters:
  • output_path (str) – Path to write the HTML file.

  • bond_cutoff (float | None) – Maximum bond length in Angstrom. If None and the supercell was generated with a shell_target, uses shell_target.max_pair_outer * 1.2. Otherwise 3.0.

  • atom_scale (float) – Radius scale for atom spheres (multiplied by covalent radii).

  • bond_radius (float) – Radius of bond cylinders in Angstrom.

  • background_color (str) – CSS colour for the viewer background.

  • title (str) – Optional title displayed above the viewer.

  • show_bonds (bool | None) – Whether to emit bond cylinders. None (default) auto-picks: True when no tetrahedra are requested, False when they are (tetrahedra supersede bonds). Pass True / False explicitly to override.

  • history (dict | str | None) – Which trajectory history to render. None (default) uses self.shell_relax_history. Pass "thermal_relax" to render self.thermal_relax_history, or pass an explicit history dict. Both shell-relax and thermal-relax dicts have a "trajectory" key when their parent call was run with capture_trajectory=True.

  • self (Supercell) –

  • tetrahedra (dict | None) –

  • tetrahedra_opacity (float) –

  • octahedra (dict | None) –

  • octahedra_opacity (float) –

  • cuboctahedra (dict | None) –

  • cuboctahedra_opacity (float) –

  • polyhedra_groups (list[dict] | None) –

Returns:

The output path.

Return type:

str

export_g3_html(output_path, *, r_max=10.0, r_step=0.1, phi_num_bins=45, background_color='#f7f8f5', title='', show_progress=False, show_all_triplets=False)

Export a static 2D g3 viewer as a self-contained HTML file.

Renders one heatmap per species-triplet of the reduced three-body distribution (density / uniform, where 1.0 = white). The viewer uses a diverging RdBu_r colormap centred at 1.0 and lets the user pick the triplet and adjust the upper colour limit.

A fresh G3Distribution is measured from the current atoms on the coarse export grid (by default 50 x 45 bins per triplet) so the embedded JSON stays small (~500 KB) without affecting anything the supercell itself has cached.

Parameters:
  • output_path (str) – Path to write the HTML file.

  • r_max (float) – Measurement grid for the exported distribution.

  • r_step (float) – Measurement grid for the exported distribution.

  • phi_num_bins (int) – Measurement grid for the exported distribution.

  • background_color (str) – Cosmetic.

  • title (str) – Cosmetic.

  • show_progress (bool) – Forwarded to the g3 measurement call.

  • show_all_triplets (bool) – If True, the viewer renders a grid of all triplet heatmaps simultaneously (sharing one legend and colour scale) instead of the interactive single-panel view. Useful for multi- species cases like SiO₂ where it’s otherwise unclear which triplet channel is being displayed.

  • self (Supercell) –

Return type:

str

export_g2_html(output_path=None, *, r_max=10.0, r_step=0.05, background_color='#f7f8f5', title='', show_progress=False)

Export a standalone interactive 2D g(r) viewer.

Shows the per-species-pair pair-correlation function g_{AB}(r) as a single g(r) plot with a dropdown to switch species pair, and an “overlay all” checkbox to compare all pairs on one axis. Essentially the bottom panel of export_g3_html() lifted out on its own with a species-pair selector - useful as a quick 2-body PDF viewer without any angular content.

Parameters:
  • output_path (str | None) – Path to write the HTML file. When None the HTML is not written to disk; the raw HTML string is still returned so the caller can embed it directly (for example via IPython.display.HTML()) - see also plot_g2() for a ready-made Jupyter wrapper.

  • r_max (float) – Radial grid for the measurement. A finer r_step (default 0.05 Å) gives smoother curves than the coarse 0.1 Å grid used by the g3 viewer.

  • r_step (float) – Radial grid for the measurement. A finer r_step (default 0.05 Å) gives smoother curves than the coarse 0.1 Å grid used by the g3 viewer.

  • background_color (str) – Cosmetic.

  • title (str) – Cosmetic.

  • show_progress (bool) – Forwarded to the underlying measure_g3() call (this exporter reuses the g3 machinery because the g2 array is a by-product; phi_num_bins is set low for speed).

  • self (Supercell) –

Returns:

The resolved output path when output_path was provided, otherwise the HTML source string.

Return type:

str

Class attributes

PRESETS: dict[str, dict[str, Any]] = {'LRO': {'angle_weight': 1.0, 'bond_weight': 2.0, 'displacement_sigma': 0.04, 'grain_size': 18.0, 'hard_core_scale': 0.95, 'nonbond_push_scale': 0.9, 'num_steps': 150}, 'MRO': {'angle_weight': 0.9, 'bond_weight': 1.9, 'displacement_sigma': 0.04, 'grain_size': 13.0, 'hard_core_scale': 0.95, 'nonbond_push_scale': 0.7, 'num_steps': 150, 'repulsion_weight': 2.5}, 'SRO': {'angle_weight': 1.0, 'bond_weight': 2.2, 'displacement_sigma': 0.04, 'grain_size': 10.0, 'hard_core_scale': 0.95, 'nonbond_push_scale': 0.6, 'num_steps': 200, 'repulsion_weight': 2.0}, 'amorphous': {'angle_weight': 0.6, 'bond_weight': 1.2, 'displacement_sigma': 0.08, 'grain_size': 6.0, 'hard_core_scale': 0.9, 'nonbond_push_scale': 0.5, 'num_steps': 150, 'repulsion_weight': 1.5}, 'liquid': {'angle_weight': 0.5, 'bond_weight': 0.4, 'grain_size': None, 'hard_core_scale': 0.75, 'nonbond_push_scale': 0.7, 'num_steps': 100, 'repulsion_weight': 0.5}, 'nanocrystalline': {'angle_weight': 1.5, 'bond_weight': 3.0, 'displacement_sigma': 0.02, 'grain_size': 20.0, 'num_steps': 150}}

Presets

Ready-to-use parameter dictionaries available as Supercell.PRESETS. See the silicon preset summary for the per-regime values used in the static examples.