pyextal.superspace.approximant¶
The commensurate approximant: a modulated structure as an ordinary 3D crystal.
When the modulation wave vectors are rational the structure has a genuine 3D
supercell, and inside that supercell it is a plain periodic crystal with the
modulation frozen into the atomic positions. That is worth having for its own
sake, but here it does a specific job: it is the one form of a modulated
structure the existing Bloch-wave engine can already compute. No Fortran
change, no rebuild, nothing touched – the supercell is written out as an
ordinary .dat file and bloch_parse reads it like any other crystal.
So the dynamical half of the TaS2 example costs nothing in risk. The kinematic (3+d) calculation and the dynamical supercell calculation are independent implementations of the same physics, and each checks the other: the supercell is exact where the incommensurate description is an idealisation, and the superspace description is exact where the supercell is an approximation.
The supercell is not small. 1T-TaS2’s in-plane CDW gives a 13-fold cell, 39
atoms; including the 1/3 c* stacking term makes it 39-fold and 117 atoms.
Both are far inside the engine’s limits (nk = 5000), but the reciprocal
lattice is 13 or 39 times denser, so keep gmax modest when selecting beams.
Functions¶
|
Lattice parameters of the supercell. |
Basic-cell translations that are distinct modulo the supercell. |
|
|
Freeze the modulation into an ordinary list of 3D atomic positions. |
|
Write the approximant as a |
|
Label supercell reflections with their (3+d) superspace indices. |
|
A dynamical zone-axis pattern of the approximant, via the Bloch engine. |
|
Dynamical pattern of the exact supercell, on the same beams as H. |
Module Contents¶
- pyextal.superspace.approximant.supercell_cell(lat, M)¶
Lattice parameters of the supercell.
- Parameters:
lat (SuperspaceLattice) – Supplies the basic metric.
M (array-like) – (3, 3) integer supercell matrix; rows are the supercell direct lattice vectors in terms of the basic ones.
- Returns:
(a, b, c, alpha, beta, gamma), Angstrom and degrees.- Return type:
tuple
- pyextal.superspace.approximant.lattice_cosets(M)¶
Basic-cell translations that are distinct modulo the supercell.
There are
|det M|of them, and they are the origins of the basic cells packed into one supercell.- Parameters:
M (array-like) – (3, 3) integer supercell matrix.
- Returns:
(|det M|, 3) integer translations.
- Return type:
np.ndarray
- Raises:
ValueError – If M is singular, or the search does not find them all.
- pyextal.superspace.approximant.supercell_atoms(lat, atoms, M=None, wrap=True)¶
Freeze the modulation into an ordinary list of 3D atomic positions.
Each basic-cell atom is replicated onto every lattice coset of the supercell. Its internal coordinate follows from where it sits,
xbar_j = q_j . (t + r), so each copy gets a different displacement – which is exactly what makes the supercell a superstructure rather than|det M|identical copies.- Parameters:
lat (SuperspaceLattice) – Must be commensurate.
atoms (list[ModulatedAtom]) – The basic structure, listed in P1.
M (array-like, optional) – Supercell matrix; taken from
SuperspaceLattice.supercell_matrix()when None.wrap (bool) – Wrap fractional coordinates into [0, 1).
- Returns:
(M, cell, sites)with sites a list of(label, element, position, B); position is (3,) in supercell fractional coordinates.- Return type:
tuple
- Raises:
ValueError – If the structure is incommensurate, so no supercell exists.
- pyextal.superspace.approximant.write_dat(path, lat, atoms, zone=(0, 0, 1), xaxis=(1, 0, 0), hv=200.0, conv=0.0, samp=20, M=None, gmax=0.9, sgmax=3.0, sgmin=0.3, omeg=30.0, bmin=0.1, aper=None, abs_pot=0.0, name='approximant', norm=None, kt=(0, 0, 0), header=None, hkl=None)¶
Write the approximant as a
.datfile the Bloch-wave engine can parse.The file uses
spg 1 1– P1 with every atom listed – because the supercell’s symmetry is lower than the basic structure’s, and letting the engine expand a higher group would put atoms in the wrong places.The
.datformat is parsed sequentially and is order-sensitive, so the keywords below are emitted in the order the parser expects; seedocs/source/configuration.md.selechooses the beams geometrically, which is convenient but means the beam count moves with the voltage and the cell – and the supercell reciprocal lattice is|det M|times denser than the basic one, so a gmax that is unremarkable for the parent cell can select thousands of beams here. Start small and check what you got.- Parameters:
path (path-like) – Destination file.
lat (SuperspaceLattice) – The commensurate lattice.
atoms (list[ModulatedAtom]) – Basic-structure atoms, listed in P1.
zone (tuple) – Zone axis in supercell direct indices. With the basis
SuperspaceLattice.supercell_matrix()returns, the parent’s [001] is still (0, 0, 1).xaxis (tuple) – In-plane reference reflection, supercell indices.
hv (float) – Accelerating voltage in kV.
conv (float) – Convergence semi-angle in units of
|g_xaxis|/|K|. Zero is a parallel beam, which is what a SAED pattern is.samp (int) – Sampling across the disc.
M (array-like, optional) – Supercell matrix; derived when None.
gmax (float) – Beam-selection radius, inverse Angstrom.
sgmax (float) – Largest excitation error for beam selection.
sgmin (float) – Excitation error below which a beam is fully diagonalised.
omeg (float) – Bethe-potential parameter; larger is more accurate.
bmin (float) – Bethe potential cutoff.
aper (float, optional) – Output aperture radius; gmax when None.
abs_pot (float) – Mean absorption potential in volts.
name (str) – Crystal name written into the file.
norm (tuple, optional) – Surface normal; zone when None.
kt (tuple) – Tangential incident wavevector.
header (str, optional) – Extra comment lines at the top.
hkl (array-like, optional) – Explicit beam list in supercell indices, written as an
hklblock instead of aseleline. Use it to make a supercell calculation solve the same scattering problem as a superspace one: pass the images of the superspace beams under M, and the two share a beam set so their intensities can be compared reflection by reflection.
- Returns:
(path, M, cell, sites)– the file written plus its contents.- Return type:
tuple
- pyextal.superspace.approximant.assign_superspace_index(lat, hkl_super, M=None, max_order=6, tol=1e-06)¶
Label supercell reflections with their (3+d) superspace indices.
This is what makes the dynamical calculation comparable with the kinematic one. The engine works in the supercell and knows nothing about modulation, so it reports a reflection as
(1, 0, 0); in the parent’s terms that same reflection is the first-order satellite(0 0 0 | 1 0). Going back is a matter of undoingh' = M hand then splitting the result into an integral main index plus a satellite part.The split is not unique for a commensurate structure – that is the same degeneracy
SuperspaceLattice.supercell_matrix()exists because of – so the assignment of lowest satellite order is chosen, which is the physically meaningful one: a reflection is called a first-order satellite rather than a fifth-order one that happens to land in the same place.- Parameters:
lat (SuperspaceLattice) – The commensurate parent lattice.
hkl_super (array-like) – (n, 3) integer supercell reflection indices.
M (array-like, optional) – Supercell matrix; derived when None.
max_order (int) – Largest satellite order to consider.
tol (float) – Integrality tolerance on the main part.
- Returns:
(H, ok)with H of shape (n, 3+d) and ok a boolean mask. Where ok is False no assignment within max_order was found and that row of H is zero.- Return type:
tuple[np.ndarray, np.ndarray]
- pyextal.superspace.approximant.dynamical_pattern(lat, atoms, thickness=200.0, zone=(0, 0, 1), xaxis=(1, 0, 0), hv=200.0, gmax=0.8, path=None, max_order=6, keep_engine=False, backend='numpy', **dat_kw)¶
A dynamical zone-axis pattern of the approximant, via the Bloch engine.
Writes the supercell as a
.dat, runs one incident orientation through the unmodified engine, and labels the output beams with their superspace indices so the result lines up withsaed_pattern().The engine keeps one crystal in Fortran module-global state, so this reparses and resets it. It calls
terminate()at the end unless keep_engine is set – and note thatterminate()ends the engine for the life of the process, so anything else needing it must run first. Leaving it out instead orphans the MPI workers at 100% CPU.- Parameters:
lat (SuperspaceLattice) – Must be commensurate.
atoms (list[ModulatedAtom]) – The basic structure, listed in P1.
thickness (float) – Specimen thickness in Angstrom.
zone (tuple) – Zone axis in supercell indices.
xaxis (tuple) – In-plane reference reflection, supercell indices.
hv (float) – Accelerating voltage in kV.
gmax (float) – Beam-selection radius, inverse Angstrom. The supercell reciprocal lattice is dense; raising this is expensive.
path (path-like, optional) – Where to write the
.dat; a temporary file when None.max_order (int) – Largest satellite order when labelling reflections.
keep_engine (bool) – Skip
terminate(), leaving the engine live.backend (str) – Eigensolver,
'numpy'(default; any build) or'fortran'(MPI build only).**dat_kw – Passed through to
write_dat().
- Returns:
With keys H (n, 3+d superspace indices), hkl_super (n, 3), intensity (n,), order (n,), labelled (n,) bool, M, cell, nbeams and path.
- Return type:
dict
- pyextal.superspace.approximant.supercell_saed(lat, atoms, H, thickness=200.0, zone=(0, 0, 1), xaxis=(1, 0, 0), hv=200.0, M=None, path=None, keep_engine=False, backend='numpy', **dat_kw)¶
Dynamical pattern of the exact supercell, on the same beams as H.
The commensurate structure is an ordinary 3D crystal, so this needs nothing from the superspace machinery: the modulation is frozen into
|det M|times as many atomic coordinates and the unmodified engine solves it. What it does need is the same beam set as the superspace calculation, or the two dynamical results would not be comparable – different beams means a different scattering problem. Each superspace reflection maps to the supercell indexM @ S(H), which is integral exactly because the structure is commensurate, and that list is written out explicitly.Agreement between this and
dynamical_saed()on the same structure is a strong check on the superspace engine: one route freezes the modulation into 507 atoms and never mentions a satellite, the other keeps 3 atoms and integrates over internal space.- Parameters:
lat (SuperspaceLattice) – Must be commensurate.
atoms (list[ModulatedAtom]) – The basic structure in P1.
H (array-like) – (n, 3+d) superspace beam list.
thickness (float) – Specimen thickness, Angstrom.
zone (tuple) – Zone axis in supercell indices.
xaxis (tuple) – In-plane reference reflection, supercell indices.
hv (float) – Accelerating voltage, kV.
M (array-like, optional) – Supercell matrix; derived when None.
path (path-like, optional) – Where to write the
.dat.keep_engine (bool) – Skip
terminate().backend (str) – Eigensolver,
'numpy'(default; any build) or'fortran'(MPI build only).**dat_kw – Passed to
write_dat().
- Returns:
H(the beams, unchanged and in order),hkl_super,intensitynormalised to the strongest diffracted beam,pattern(aDiffractionPattern, so the result renders throughplot_image()like any other),M,cell,natomsandnbeams.- Return type:
dict
- Raises:
ValueError – If the structure is incommensurate, or a beam does not map to an integral supercell index.