Source code for dials.algorithms.indexing.basis_vector_search.fft1d
from libtbx import phil
from scitbx import matrix
from scitbx.array_family import flex
from .strategy import Strategy
fft1d_phil_str = """\
characteristic_grid = None
.help = Sampling frequency in radians. See Steller 1997. If None, \
determine a grid sampling automatically using the input \
reflections, using at most 0.029 radians.
.type = float(value_min=0)
"""
[docs]class FFT1D(Strategy):
"""
Basis vector search using 1D FFTs in reciprocal space.
A set of dimensionless radial unit vectors, typically ~7000 in total, is chosen
so that they are roughly evenly spaced in solid angle over a hemisphere. The
reciprocal space displacements of the measured spot centroids are then projected
onto each of these radial vectors in turn (that is, we calculate the scalar
product of each displacement with each unit vector). A 1D FFT of the linear
density of projected spot positions is performed along each direction.
Aggregating the results of all the transforms, the three shortest non-collinear
wave vectors with the greatest spectral weight correspond to the basis vectors of
the direct lattice.
See:
Steller, I., Bolotovsky, R. & Rossmann, M. G. (1997). J. Appl. Cryst. 30, 1036-1040.
Sauter, N. K., Grosse-Kunstleve, R. W. & Adams, P. D. (2004). J. Appl. Cryst. 37, 399-409.
"""
phil_help = (
"Search for the basis vectors of the direct lattice by performing a series of "
"1D FFTs along various directions in reciprocal space. This has a lower "
"memory requirement than a single 3D FFT (the fft3d method). This method may "
"also be more appropriate than a 3D FFT if the reflections are from narrow "
"wedges of rotation data or from stills data."
)
phil_scope = phil.parse(fft1d_phil_str)
[docs] def __init__(self, max_cell, params=None, *args, **kwargs):
"""Construct an FFT1D object.
Args:
max_cell (float): An estimate of the maximum cell dimension of the primitive
cell.
characteristic_grid (float): Sampling frequency in radians. See Steller 1997.
If None, determine a grid sampling automatically using the input
reflections, using at most 0.029 radians.
"""
super().__init__(max_cell, params=params, *args, **kwargs)
[docs] def find_basis_vectors(self, reciprocal_lattice_vectors):
"""Find a list of likely basis vectors.
Args:
reciprocal_lattice_vectors (scitbx.array_family.flex.vec3_double):
The list of reciprocal lattice vectors to search for periodicity.
Returns:
A tuple containing the list of basis vectors and a flex.bool array
identifying which reflections were used in indexing.
"""
import iotbx.phil
from rstbx.phil.phil_preferences import indexing_api_defs
used_in_indexing = flex.bool(reciprocal_lattice_vectors.size(), True)
hardcoded_phil = iotbx.phil.parse(input_string=indexing_api_defs).extract()
# Spot_positions: Centroid positions for spotfinder spots, in pixels
# Return value: Corrected for parallax, converted to mm
# derive a max_cell from mm spots
# derive a grid sampling from spots
from rstbx.indexing_api.lattice import DPS_primitive_lattice
# max_cell: max possible cell in Angstroms; set to None, determine from data
# recommended_grid_sampling_rad: grid sampling in radians; guess for now
DPS = DPS_primitive_lattice(
max_cell=self._max_cell,
recommended_grid_sampling_rad=self._params.characteristic_grid,
horizon_phil=hardcoded_phil,
)
# transform input into what Nick needs
# i.e., construct a flex.vec3 double consisting of mm spots, phi in degrees
DPS.index(reciprocal_space_vectors=reciprocal_lattice_vectors)
solutions = DPS.getSolutions()
candidate_basis_vectors = [matrix.col(s.bvec()) for s in solutions]
return candidate_basis_vectors, used_in_indexing