torchref.symmetry package
Symmetry operations module for TorchRef.
Provides crystallographic symmetry operations for: - Space group handling via SpaceGroup class (nn.Module with buffers) - Symmetry alias for backward compatibility (same as SpaceGroup) - Real space density maps (MapSymmetry) - Reciprocal space structure factor grids (ReciprocalSymmetry)
Example
from torchref.symmetry import SpaceGroup, Symmetry, MapSymmetry, ReciprocalSymmetry
# SpaceGroup is the unified class for space group handling
sg = SpaceGroup('P21') # From string
sg = SpaceGroup(4) # From number
sg = SpaceGroup(gemmi_sg) # Pass-through gemmi object
# SpaceGroup is an nn.Module with symmetry operations
transformed_coords = sg(fractional_coords)
print(sg.n_ops) # Number of symmetry operations
print(sg.matrices.shape) # (n_ops, 3, 3) rotation matrices
# Symmetry is now an alias for SpaceGroup (backward compatibility)
sym = Symmetry('P21') # Same as SpaceGroup('P21')
# Real space map symmetry
map_sym = MapSymmetry('P21', map_shape=(64, 64, 64), cell_params=cell)
symmetric_map = map_sym(density_map)
# Reciprocal space symmetry
recip_sym = ReciprocalSymmetry('P21', grid_shape=(64, 64, 64))
F_averaged = recip_sym(F_grid, mode='average')
- class torchref.symmetry.Cell(data, *, dtype=torch.float32, device=device(type='cpu'), requires_grad=False)[source]
Bases:
DeviceMixinDataclass for crystallographic unit cells with cached derived quantities.
Stores 6 parameters: [a, b, c, alpha, beta, gamma] - a, b, c: cell lengths in Angstroms - alpha, beta, gamma: cell angles in degrees
Derived quantities (fractional_matrix, volume, etc.) are computed on first access and cached. The cache is cleared when the cell is moved to a different device or dtype.
Examples
>>> cell = Cell([50, 60, 70, 90, 90, 90]) >>> cell.volume # Computed and cached tensor(210000.) >>> cell_gpu = cell.to('cuda') # Move to GPU (returns new Cell) >>> cell_gpu.device.type 'cuda'
- __init__(data, *, dtype=torch.float32, device=device(type='cpu'), requires_grad=False)[source]
Create a new Cell.
- Parameters:
data (array-like) – Unit cell parameters [a, b, c, alpha, beta, gamma]. Can be a list, numpy array, or torch tensor.
dtype (torch.dtype, optional) – Desired data type. Defaults to the configured
dtypes.float.device (torch.device or str, optional) – Desired device. Defaults to the configured
device.current.requires_grad (bool, optional) – Whether to track gradients. Defaults to False.
- Raises:
ValueError – If data does not have exactly 6 elements.
- detach()[source]
Return a new Cell with detached tensor (no gradient tracking).
- Returns:
New Cell with detached data.
- Return type:
- clone()[source]
Return a new Cell with cloned tensor data.
- Returns:
New Cell with cloned data.
- Return type:
- property fractional_matrix: Tensor
Orthogonalization matrix B (fractional -> Cartesian).
Returns the 3x3 matrix B such that: cart = frac @ B.T
- Returns:
Shape (3, 3) orthogonalization matrix.
- Return type:
- property inv_fractional_matrix: Tensor
Fractionalization matrix B^-1 (Cartesian -> fractional).
Returns the 3x3 matrix B^-1 such that: frac = cart @ B^-1.T
- Returns:
Shape (3, 3) fractionalization matrix.
- Return type:
- property volume: Tensor
Unit cell volume in cubic Angstroms.
- Returns:
Scalar tensor with the cell volume.
- Return type:
- property reciprocal_basis_matrix: Tensor
Reciprocal basis matrix with [a*, b*, c*] as rows.
- Returns:
Shape (3, 3) matrix where rows are the reciprocal basis vectors.
- Return type:
- compute_grid_size(max_res, oversampling=3.0)[source]
Compute minimum grid dimensions for a given resolution.
Uses Shannon-Nyquist sampling criterion to determine the minimum number of grid points needed along each axis.
- Parameters:
- Returns:
Minimum grid dimensions (nx, ny, nz).
- Return type:
Examples
>>> cell = Cell([50, 60, 70, 90, 90, 90]) >>> cell.compute_grid_size(2.0) (75, 90, 105)
- tolist()[source]
Convert Cell parameters to a standard Python list.
- Returns:
List of cell parameters [a, b, c, alpha, beta, gamma].
- Return type:
- fractional_to_cartesian(frac_coords)[source]
Convert fractional coordinates to Cartesian coordinates.
- Parameters:
frac_coords (torch.Tensor) – Tensor of fractional coordinates, shape (…, 3).
- Returns:
Tensor of Cartesian coordinates, shape (…, 3).
- Return type:
- cartesian_to_fractional(cart_coords)[source]
Convert Cartesian coordinates to fractional coordinates.
- Parameters:
cart_coords (torch.Tensor) – Tensor of Cartesian coordinates, shape (…, 3).
- Returns:
Tensor of fractional coordinates, shape (…, 3).
- Return type:
- class torchref.symmetry.SpaceGroup(space_group=None, dtype=torch.float32, device=device(type='cpu'))[source]
Bases:
DeviceMixin,DebugMixin,ModuleUnified space group handler for crystallographic symmetry operations.
This class combines space group normalization with symmetry operations, providing a single interface for: - Normalizing input (string, int, gemmi.SpaceGroup) in the constructor - Storing symmetry matrices and translations as PyTorch buffers - Applying symmetry operations to fractional coordinates - Grid size utilities for symmetry-compatible grids
- Parameters:
space_group (str, int, gemmi.SpaceGroup, SpaceGroup, or None) – Space group specification. Accepts: - Hermann-Mauguin symbol (e.g., ‘P21’, ‘P 21 21 21’) - Space group number (1-230) - gemmi.SpaceGroup object - Another SpaceGroup instance - None (defaults to P1)
dtype (torch.dtype, default torch.float64) – Data type for rotation matrices and translations.
device (torch.device, default: configured device.current) – Device for computation.
- matrices
Rotation matrices for all symmetry operations (registered buffer).
- Type:
torch.Tensor, shape (n_ops, 3, 3)
- translations
Translation vectors for all symmetry operations (registered buffer).
- Type:
torch.Tensor, shape (n_ops, 3)
Examples
# Create from various inputs sg = SpaceGroup('P21') sg = SpaceGroup('P 21') # Same result sg = SpaceGroup(4) # P21 by number sg = SpaceGroup(None) # Returns P1 # Access properties print(sg.name) # 'P21' (short name) print(sg.hm) # 'P 21' (Hermann-Mauguin with spaces) print(sg.number) # 4 # Apply symmetry operations coords = torch.tensor([[0.1, 0.2, 0.3]]) transformed = sg(coords) # Apply all symmetry operations # Grid utilities req = sg.get_grid_requirements() suggested = sg.suggest_grid_size((131, 163, 148))
- property gemmi: SpaceGroup
Access a gemmi.SpaceGroup object (created on demand, not stored).
- property spacegroup: SpaceGroup
Alias for gemmi property (backward compatibility).
- property space_group: SpaceGroup
Alias for gemmi property (backward compatibility).
- apply(xyz_fractional, apply_translation=True)[source]
Apply symmetry operations to fractional coordinates (rotation + translation).
For real space coordinates, applies the full symmetry operation: x’ = R·x + t
- Parameters:
xyz_fractional (torch.Tensor) – Input tensor of shape (N, 3) representing fractional coordinates.
- Returns:
Transformed coordinates of shape (N, 3, ops) where ops is the number of symmetry operations.
- Return type:
See also
apply_to_hklFor reciprocal space (Miller indices), rotation only.
- apply_to_hkl(hkl)[source]
Apply symmetry operations to Miller indices (rotation only, no translation).
For reciprocal space, only the rotational part of symmetry operations applies to Miller indices: h’ = R·h. The translation vector affects the phase of structure factors, not the indices themselves.
- Parameters:
hkl (torch.Tensor) – Input tensor of shape (N, 3) representing Miller indices.
- Returns:
Transformed Miller indices of shape (N, 3, ops) where ops is the number of symmetry operations.
- Return type:
See also
applyFor real space coordinates (rotation + translation).
- expand_coords_to_P1(xyz_fractional)[source]
Expand fractional coordinates by applying all symmetry operations.
- Parameters:
xyz_fractional (torch.Tensor) – Input tensor of shape (N, 3) representing fractional coordinates.
- Returns:
Expanded coordinates of shape (N * ops, 3).
- Return type:
- get_grid_requirements()[source]
Analyze symmetry operations to determine grid size requirements.
- Returns:
{‘nx_mod’: int, ‘ny_mod’: int, ‘nz_mod’: int} Required divisibility for each axis.
- Return type:
Examples
sg = SpaceGroup('P21') req = sg.get_grid_requirements() print(req) # {'nx_mod': 1, 'ny_mod': 2, 'nz_mod': 1}
- check_grid_compatibility(grid_shape)[source]
Check if a grid size is compatible with the symmetry operations.
- Parameters:
- Returns:
Dictionary with keys: - ‘compatible’ : bool - True if grid satisfies all requirements - ‘symmetry_compatible’ : bool - True if grid satisfies symmetry - ‘fft_friendly’ : bool - True if all dimensions are FFT-friendly - ‘can_use_direct_indexing’ : bool - True if no interpolation needed - ‘issues’ : list of str - Descriptions of incompatibilities - ‘requirements’ : dict - Required divisibility
- Return type:
Examples
sg = SpaceGroup('P21') result = sg.check_grid_compatibility((131, 163, 148)) print(result['compatible']) # False print(result['issues']) # ['ny=163 not divisible by 2']
- suggest_grid_size(min_grid_shape, make_fft_friendly=True)[source]
Suggest an optimal grid size that satisfies symmetry requirements.
- Parameters:
- Returns:
Suggested grid dimensions (nx, ny, nz).
- Return type:
Examples
sg = SpaceGroup('P21') suggested = sg.suggest_grid_size((131, 163, 148)) print(suggested) # (135, 164, 150) or similar
- torchref.symmetry.spacegroup_to_str(spacegroup, style='short')[source]
Convert space group to string representation.
- Parameters:
spacegroup (SpaceGroupLike) – Space group in any supported format.
style (str, default 'short') – Output style: - ‘short’: No spaces (e.g., ‘P212121’) - ‘hm’: Hermann-Mauguin with spaces (e.g., ‘P 21 21 21’) - ‘xhm’: Extended Hermann-Mauguin (e.g., ‘P 21 21 21’)
- Returns:
Space group name in requested style.
- Return type:
- torchref.symmetry.get_symmetry_operations(spacegroup)[source]
Get symmetry operations from a space group.
- Parameters:
spacegroup (SpaceGroupLike) – Space group in any supported format.
- Returns:
List of symmetry operations.
- Return type:
list of gemmi.Op
- torchref.symmetry.get_operations_as_tensors(spacegroup, dtype=torch.float32, device=device(type='cpu'))[source]
Get symmetry operations as PyTorch tensors.
- Parameters:
spacegroup (SpaceGroupLike) – Space group in any supported format.
dtype (torch.dtype, optional) – Data type for tensors. Defaults to the configured
dtypes.float.device (torch.device, optional) – Device for tensors. Defaults to the configured
device.current.
- Returns:
matrices (torch.Tensor, shape (n_ops, 3, 3)) – Rotation matrices.
translations (torch.Tensor, shape (n_ops, 3)) – Translation vectors (in fractional coordinates).
- torchref.symmetry.is_same_spacegroup(sg1, sg2)[source]
Check if two space groups are the same.
- Parameters:
sg1 (SpaceGroupLike) – Space groups to compare.
sg2 (SpaceGroupLike) – Space groups to compare.
- Returns:
True if the space groups are identical.
- Return type:
- torchref.symmetry.get_point_group(spacegroup)[source]
Get the point group symbol for a space group.
- Parameters:
spacegroup (SpaceGroupLike) – Space group in any supported format.
- Returns:
Point group symbol (e.g., ‘222’, ‘mmm’, ‘4/mmm’).
- Return type:
- torchref.symmetry.get_crystal_system(spacegroup)[source]
Get the crystal system for a space group.
- Parameters:
spacegroup (SpaceGroupLike) – Space group in any supported format.
- Returns:
Crystal system name (triclinic, monoclinic, orthorhombic, tetragonal, trigonal, hexagonal, or cubic).
- Return type:
- torchref.symmetry.is_centrosymmetric(spacegroup)[source]
Check if a space group is centrosymmetric.
- Parameters:
spacegroup (SpaceGroupLike) – Space group in any supported format.
- Returns:
True if the space group has an inversion center.
- Return type:
- torchref.symmetry.n_operations(spacegroup)[source]
Get the number of symmetry operations in a space group.
- Parameters:
spacegroup (SpaceGroupLike) – Space group in any supported format.
- Returns:
Number of symmetry operations.
- Return type:
- torchref.symmetry.Symmetry
alias of
SpaceGroup
- torchref.symmetry.MapSymmetry(space_group, map_shape, cell_params, dtype_float=torch.float32, verbose=1, device=device(type='cpu'))[source]
Factory function to create the appropriate MapSymmetry implementation.
This function checks if the grid size is compatible with direct indexing (no interpolation needed). If compatible, returns MapSymmetryDirect for maximum performance. Otherwise, returns MapSymmetryInterpolation as fallback.
- Parameters:
space_group (str, int, or gemmi.SpaceGroup) – Space group specification (e.g., ‘P21’, 4, gemmi.SpaceGroup(‘P 21’)).
map_shape (tuple of int) – Shape of the density map (nx, ny, nz).
cell_params (torch.Tensor, shape (6,)) – Unit cell parameters [a, b, c, alpha, beta, gamma] in Å and degrees.
dtype_float (torch.dtype, default torch.float32) – Floating point precision to use.
verbose (int, default 1) – Verbosity level (0=silent, 1=info, 2=debug).
device (torch.device, default: configured device.current) – Device to use for computation.
- Returns:
The appropriate implementation based on grid compatibility.
- Return type:
MapSymmetryDirect or MapSymmetryInterpolation
- class torchref.symmetry.MapSymmetryDirect(space_group, map_shape, cell_params, dtype_float=torch.float32, verbose=1, device=device(type='cpu'))[source]
Bases:
DeviceMixin,ModuleFast direct-indexing implementation of crystallographic symmetry operations.
Computes symmetry mates one operation at a time (streaming) so that memory usage is O(grid) regardless of the number of symmetry operations, rather than O(n_ops * grid) for storing precomputed index grids.
NOTE: Do not instantiate this class directly. Use the MapSymmetry() factory function instead, which will automatically select the appropriate implementation.
- __init__(space_group, map_shape, cell_params, dtype_float=torch.float32, verbose=1, device=device(type='cpu'))[source]
- get_symmetry_mate(density_map, operation_index)[source]
Apply a single symmetry operation via direct indexing.
- forward(density_map, apply_symmetry=True, combine_mode='sum')[source]
Apply symmetry operations to density map.
Computes one symmetry mate at a time and accumulates into the result, so peak memory is only 1 index grid + 2 density maps regardless of the number of symmetry operations.
- torchref.symmetry.ReciprocalSymmetry(space_group, grid_shape, dtype_float=torch.float32, verbose=1, device=device(type='cpu'))[source]
Factory function to create the appropriate ReciprocalSymmetry implementation.
- Parameters:
space_group (str, int, or gemmi.SpaceGroup) – Space group specification (e.g., ‘P21’, 4, gemmi.SpaceGroup(‘P 21’)).
grid_shape (tuple of int) – Shape of the reciprocal space grid (nh, nk, nl). The grid spans from -n//2 to n//2 for each dimension.
dtype_float (torch.dtype, default: configured dtypes.float) – Floating point precision to use.
verbose (int, default 1) – Verbosity level (0=silent, 1=info, 2=debug).
device (torch.device, default: configured device.current) – Device to use for computation.
- Returns:
Implementation for reciprocal space grid symmetry operations.
- Return type:
- class torchref.symmetry.ReciprocalSymmetryGrid(space_group, grid_shape, dtype_float=torch.float32, verbose=1, device=device(type='cpu'))[source]
Bases:
DeviceMixin,ModuleReciprocal space symmetry operations for Miller index grids.
Handles symmetry operations in reciprocal space including: - Miller index transformation under symmetry operations - Systematic absence detection - Centric reflection identification - Friedel pair handling - Symmetry expansion/averaging of structure factors
- In reciprocal space, symmetry operations transform Miller indices as:
(h’, k’, l’) = (h, k, l) @ R^T
where R is the rotation matrix from real space symmetry.
- symmetry
Base symmetry operations handler.
- Type:
Examples
recip_sym = ReciprocalSymmetry('P21', grid_shape=(64, 64, 64)) F_expanded = recip_sym(F_asym) # Expand from asymmetric unit F_avg = recip_sym.symmetry_average(F_full) # Average symmetry-related reflections
- __init__(space_group, grid_shape, dtype_float=torch.float32, verbose=1, device=device(type='cpu'))[source]
Initialize reciprocal space symmetry operator.
- Parameters:
space_group (str) – Space group name.
grid_shape (tuple of int) – Shape of the reciprocal space grid (nh, nk, nl).
dtype_float (torch.dtype, default: configured dtypes.float) – Floating point precision.
verbose (int, default 1) – Verbosity level.
device (torch.device, default: configured device.current) – Computation device.
- apply_to_indices(hkl, operation_index=None)[source]
Apply symmetry operation(s) to Miller indices.
- Parameters:
hkl (torch.Tensor, shape (..., 3)) – Miller indices (h, k, l).
operation_index (int, optional) – If specified, apply only this operation. If None, apply all operations.
- Returns:
Transformed Miller indices. If operation_index is None: shape (n_ops, …, 3) Otherwise: shape (…, 3)
- Return type:
- get_phase_shift(hkl, operation_index)[source]
Get phase shift for a symmetry operation on given Miller indices.
The phase shift is exp(2πi h·t) where t is the translation.
- Parameters:
hkl (torch.Tensor, shape (..., 3)) – Miller indices.
operation_index (int) – Symmetry operation index.
- Returns:
Phase shift in radians, shape (…).
- Return type:
- get_symmetry_mate(F_grid, operation_index)[source]
Apply a single symmetry operation to a structure factor grid.
- Parameters:
F_grid (torch.Tensor, shape (nh, nk, nl)) – Complex structure factor grid.
operation_index (int) – Index of the symmetry operation (0 to n_ops-1).
- Returns:
Structure factors after applying symmetry operation. Includes phase shift from translation component.
- Return type:
torch.Tensor, shape (nh, nk, nl)
- get_all_symmetry_mates(F_grid)[source]
Get all symmetry-related structure factor grids.
- Parameters:
F_grid (torch.Tensor, shape (nh, nk, nl)) – Complex structure factor grid.
- Returns:
List of symmetry-related grids.
- Return type:
list of torch.Tensor
- symmetry_average(F_grid, weights=None)[source]
Average structure factors over all symmetry equivalents.
This is useful for enforcing symmetry constraints on structure factors.
- Parameters:
F_grid (torch.Tensor, shape (nh, nk, nl)) – Complex structure factor grid.
weights (torch.Tensor, optional) – Weights for averaging, shape (nh, nk, nl). If None, equal weights are used.
- Returns:
Symmetry-averaged structure factors.
- Return type:
torch.Tensor, shape (nh, nk, nl)
- expand_to_p1(F_asym, asym_mask=None)[source]
Expand structure factors from asymmetric unit to full P1.
Takes structure factors defined on the asymmetric unit and generates the full reciprocal space by applying all symmetry operations.
- Parameters:
F_asym (torch.Tensor, shape (nh, nk, nl)) – Structure factors on asymmetric unit (other positions can be zero).
asym_mask (torch.Tensor, optional) – Boolean mask indicating asymmetric unit positions. If None, non-zero values are assumed to be the asymmetric unit.
- Returns:
Full structure factor grid with all symmetry equivalents filled.
- Return type:
torch.Tensor, shape (nh, nk, nl)
- apply_friedel(F_grid)[source]
Apply Friedel’s law: F(-h,-k,-l) = F*(h,k,l).
For normal (non-anomalous) scattering, the structure factor at -h is the complex conjugate of F(h).
- Parameters:
F_grid (torch.Tensor, shape (nh, nk, nl)) – Complex structure factor grid.
- Returns:
Structure factors with Friedel symmetry enforced.
- Return type:
torch.Tensor, shape (nh, nk, nl)
- get_epsilon()[source]
Compute epsilon (multiplicity) factors for each reflection.
Epsilon is the number of symmetry operations that map h to itself (or to its Friedel mate for acentric space groups).
- Returns:
Epsilon factors for each reflection.
- Return type:
torch.Tensor, shape (nh, nk, nl)
- forward(F_grid, mode='average')[source]
Apply symmetry to structure factor grid.
- Parameters:
F_grid (torch.Tensor, shape (nh, nk, nl)) – Complex structure factor grid.
mode (str, default 'average') – Operation mode: - ‘average’: Average over all symmetry equivalents - ‘expand’: Expand from asymmetric unit to full grid - ‘sum’: Sum all symmetry mates (for accumulation)
- Returns:
Processed structure factor grid.
- Return type:
torch.Tensor, shape (nh, nk, nl)
- torchref.symmetry.expand_hkl(hkl, spacegroup, include_friedel=True, remove_absences=True, device=None)[source]
Expand Miller indices under crystallographic symmetry.
This is the core low-level function for HKL expansion. It takes Miller indices and a space group, and returns the expanded indices along with an index mapping and phase offsets needed to expand any associated data.
- Parameters:
hkl (torch.Tensor, shape (N, 3)) – Input Miller indices (asymmetric unit).
spacegroup (str, int, or gemmi.SpaceGroup) – Space group specification.
include_friedel (bool, default True) – Include Friedel mates (-h, -k, -l).
remove_absences (bool, default True) – Remove systematically absent reflections.
device (torch.device, optional) – Computation device. If None, uses hkl’s device.
- Returns:
expanded_hkl (torch.Tensor, shape (M, 3), dtype=int32) – All unique expanded Miller indices.
orig_indices (torch.Tensor, shape (M,), dtype=int64) – Index mapping expanded → original reflection. Use to expand any data:
F_expanded = F_orig[orig_indices]phase_shifts (torch.Tensor, shape (M,), dtype=float32) – Phase offsets from translations (radians). Apply to phases:
phase_expanded = phase_orig[orig_indices] + phase_shifts
- Return type:
Examples
import torch from torchref.symmetry import expand_hkl hkl_asu = torch.tensor([[1, 0, 0], [0, 1, 0], [1, 1, 1]], dtype=torch.int32) hkl_p1, indices, phases = expand_hkl(hkl_asu, 'P21') # Expand amplitude data F_asu = torch.tensor([100.0, 80.0, 75.0]) F_p1 = F_asu[indices] # Expand phase data (apply phase shifts) phi_asu = torch.tensor([0.0, 1.5, 2.0]) phi_p1 = phi_asu[indices] + phases
- torchref.symmetry.complete_hkl(input_hkl, cell, spacegroup, d_min, device=None)[source]
Complete a set of Miller indices by identifying missing reflections.
Generates all possible reflections within the resolution limit for the given spacegroup (removing systematic absences), then maps the input reflections to this complete set.
NOTE: This does NOT expand symmetry - stays in the same spacegroup. Use this to identify which reflections are missing from a dataset.
- Parameters:
input_hkl (torch.Tensor, shape (N, 3)) – Input Miller indices (may be incomplete).
cell (torch.Tensor, shape (6,)) – Unit cell parameters [a, b, c, alpha, beta, gamma].
spacegroup (str, int, or gemmi.SpaceGroup) – Space group specification.
d_min (float) – High resolution limit in Angstroms.
device (torch.device, optional) – Computation device. If None, uses input_hkl’s device.
- Returns:
complete_hkl (torch.Tensor, shape (M, 3), dtype int32) – All possible Miller indices within resolution (minus systematic absences).
input_indices (torch.Tensor, shape (M,), dtype int64) – Index mapping: complete → input. For present reflections, gives the index in input_hkl. For missing reflections, gives -1. Use:
F_complete[~missing] = F_input[input_indices[~missing]]missing_mask (torch.Tensor, shape (M,), dtype bool) – True where reflection is missing from input.
- Return type:
Examples
import torch from torchref.symmetry import complete_hkl # Incomplete dataset input_hkl = torch.tensor([[1, 0, 0], [0, 1, 0]], dtype=torch.int32) cell = torch.tensor([50.0, 60.0, 70.0, 90.0, 90.0, 90.0]) complete, indices, missing = complete_hkl(input_hkl, cell, 'P21', d_min=10.0) # Fill F values F_input = torch.tensor([100.0, 80.0]) F_complete = torch.zeros(len(complete)) present_mask = ~missing F_complete[present_mask] = F_input[indices[present_mask]]
- torchref.symmetry.reduce_hkl(hkl_p1, spacegroup, include_friedel=True, device=None)[source]
Reduce P1 Miller indices to asymmetric unit of a target spacegroup.
This is the inverse of expand_hkl(). Takes a complete set of P1 reflections and maps them back to the asymmetric unit of the target spacegroup. Multiple P1 reflections that are symmetry-equivalent merge into single ASU reflections.
The return uses a 2D index mapping with “constant multiplicity” design: each ASU reflection has exactly n_ops slots (for symmetry operations), enabling simple vectorized aggregation.
- Parameters:
hkl_p1 (torch.Tensor, shape (N, 3)) – Input Miller indices in P1 (complete hemisphere).
spacegroup (str, int, or gemmi.SpaceGroup) – Target space group specification.
include_friedel (bool, default True) – If True, also consider Friedel mates when finding ASU representative.
device (torch.device, optional) – Computation device. If None, uses hkl_p1’s device.
- Returns:
hkl_asu (torch.Tensor, shape (M, 3), dtype int32) – Unique Miller indices in the asymmetric unit.
reduction_indices (torch.Tensor, shape (M, n_ops * (2 if include_friedel else 1)), dtype int64) – For each ASU reflection, indices into hkl_p1 for its symmetry equivalents. Value of -1 indicates no P1 reflection at that position. Use:
F_asu = aggregate(F_p1[reduction_indices], dim=1)phase_shifts (torch.Tensor, shape (M, n_ops * (2 if include_friedel else 1)), dtype float32) – Phase shifts to apply before aggregation. For Friedel mates, the phase is negated.
- Return type:
Examples
import torch from torchref.symmetry import expand_hkl, reduce_hkl # Start with ASU, expand to P1, then reduce back hkl_asu = torch.tensor([[1, 0, 0], [0, 1, 0], [1, 1, 1]], dtype=torch.int32) hkl_p1, exp_idx, exp_phase = expand_hkl(hkl_asu, 'P21') # Reduce back to ASU hkl_asu_back, red_idx, red_phase = reduce_hkl(hkl_p1, 'P21') # Aggregate F values from P1 to ASU F_p1 = torch.randn(len(hkl_p1)) valid_mask = red_idx >= 0 F_gathered = torch.where(valid_mask, F_p1[red_idx.clamp(min=0)], torch.zeros_like(F_p1[0])) F_asu = F_gathered.sum(dim=1) / valid_mask.sum(dim=1).clamp(min=1)
Notes
The “constant multiplicity” design means the second dimension is always n_ops (or 2*n_ops with Friedel), regardless of whether all equivalent reflections exist in hkl_p1. Missing positions are marked with -1. This enables efficient batch aggregation without variable-length operations.
- torchref.symmetry.canonicalize_hkl(hkl, spacegroup, include_friedel=True, device=None)[source]
Map Miller indices to canonical CCP4 ASU representatives.
Uses the standard CCP4 asymmetric unit convention to select a unique representative for each reflection. The implementation is fully vectorized — symmetry equivalents are generated via batched matrix multiply and the ASU membership check is evaluated as a numpy boolean expression over all reflections at once.
- Parameters:
hkl (torch.Tensor, shape (N, 3), dtype int32) – Input Miller indices.
spacegroup (str, int, or gemmi.SpaceGroup) – Space group specification.
include_friedel (bool, default True) – Whether Friedel mates are considered equivalent.
device (torch.device, optional) – Computation device. If None, uses hkl’s device.
- Returns:
canonical_hkl (torch.Tensor, shape (N, 3), dtype int32) – Remapped indices, sorted lexicographically by (h, k, l).
phase_shifts (torch.Tensor, shape (N,), dtype float32) – Additive phase correction in radians.
friedel_flags (torch.Tensor, shape (N,), dtype bool) – True where Friedel conjugation was applied.
sort_indices (torch.Tensor, shape (N,), dtype int64) – Permutation from original to sorted order.
- Return type:
Notes
Phase correction contract — to convert structure factors to the canonical basis, the caller applies:
phi_new = torch.where(friedel_flags, -phi_old, phi_old) + phase_shifts
- torchref.symmetry.expand_reflections(reflection_data, include_friedel=True, remove_absences=True, verbose=1)[source]
Expand reflection data from asymmetric unit to P1 using symmetry operations.
Takes a ReflectionData object containing reflections in the asymmetric unit and generates all symmetry-equivalent reflections, returning a new ReflectionData object with the expanded set.
This is a high-level wrapper around expand_hkl() that handles all ReflectionData fields automatically.
- Parameters:
reflection_data (ReflectionData) – Input reflection data with hkl, F, F_sigma, etc.
include_friedel (bool, default True) – If True, also include Friedel mates (-h, -k, -l).
remove_absences (bool, default True) – If True, remove systematically absent reflections from output.
verbose (int, default 1) – Verbosity level.
- Returns:
New ReflectionData object with expanded reflections. The spacegroup is set to ‘P1’ since symmetry has been expanded.
- Return type:
See also
expand_hklLow-level function for HKL expansion without ReflectionData.
Examples
from torchref.io.datasets.reflection_data import ReflectionData from torchref.symmetry import expand_reflections data = ReflectionData().load_mtz('data.mtz') data_p1 = expand_reflections(data) print(f"Expanded from {len(data)} to {len(data_p1)} reflections")
- torchref.symmetry.expand_reciprocal_grid(F_grid, space_group, mode='average', include_friedel=True, device=None)[source]
Expand or symmetrize a reciprocal space grid using crystallographic symmetry.
This is a convenience function that creates a ReciprocalSymmetryGrid and applies it to the input grid.
- Parameters:
F_grid (torch.Tensor, shape (nh, nk, nl)) – Input structure factor grid (can be complex or real).
space_group (str) – Space group symbol (e.g., ‘P21’, ‘P212121’).
mode (str, default 'average') – Operation mode: - ‘average’: Average over all symmetry equivalents (symmetrize) - ‘expand’: Expand from asymmetric unit to full grid - ‘sum’: Sum all symmetry mates
include_friedel (bool, default True) – If True, also apply Friedel symmetry after space group symmetry.
device (torch.device, optional) – Device for computation. If None, uses F_grid’s device.
- Returns:
Symmetrized or expanded structure factor grid.
- Return type:
torch.Tensor, shape (nh, nk, nl)
Examples
import torch from torchref.symmetry import expand_reciprocal_grid # Create a test grid with some values in asymmetric unit F = torch.zeros(32, 32, 32, dtype=torch.complex64) F[5, 3, 2] = 1.0 + 0.5j # Expand to full grid F_full = expand_reciprocal_grid(F, 'P21', mode='expand') # Or symmetrize an existing full grid F_sym = expand_reciprocal_grid(F_noisy, 'P21', mode='average')
- torchref.symmetry.get_symmetry_grid_requirements(space_group)[source]
Get grid size requirements for a given space group.
This is a convenience wrapper around spacegroup.get_grid_requirements().
Returns a dict with keys ‘nx_mod’, ‘ny_mod’, ‘nz_mod’ indicating the required divisibility for each axis.
- torchref.symmetry.check_grid_compatibility(grid_shape, space_group)[source]
Check if a grid is compatible with the space group symmetry.
This is a convenience wrapper around spacegroup.check_grid_compatibility().
- Parameters:
- Returns:
Dictionary with the following keys:
’compatible’ : bool
’issues’ : list of str (description of problems)
’requirements’ : dict (required divisibility)
’can_use_direct_indexing’ : bool (True if interpolation not needed)
’fft_friendly’ : bool
- Return type:
- torchref.symmetry.recommend_grid_size(current_shape, space_group)[source]
Recommend a nearby compatible grid size.
- torchref.symmetry.find_fft_friendly_size(n, divisibility=1)[source]
Find the nearest FFT-friendly size >= n that satisfies divisibility constraint.
FFT-friendly means factors only of 2, 3, and 5 (radix-2,3,5 FFT algorithms).
- torchref.symmetry.is_fft_friendly(n)[source]
Check if a number has only factors of 2, 3, and 5.
These are optimal for radix-2,3,5 FFT algorithms.
- torchref.symmetry.calculate_optimal_grid_size(cell_params, max_res, space_group)[source]
Calculate optimal grid size for a given unit cell and space group.
Grid sizes are chosen to:
Satisfy Shannon-Nyquist sampling (3x oversampling relative to max_res)
Respect symmetry requirements (screw axis divisibility)
Be FFT-friendly (factors of 2, 3, 5 only)
Submodules
- torchref.symmetry.cell module
CellCell.__init__()Cell.reset_cache()Cell.detach()Cell.clone()Cell.deviceCell.dtypeCell.dataCell.requires_gradCell.aCell.bCell.cCell.alphaCell.betaCell.gammaCell.fractional_matrixCell.inv_fractional_matrixCell.volumeCell.reciprocal_basis_matrixCell.compute_grid_size()Cell.tolist()Cell.fractional_to_cartesian()Cell.cartesian_to_fractional()Cell.__repr__()Cell.__getitem__()Cell.__len__()
CellTensor
- torchref.symmetry.grid_utils module
- torchref.symmetry.map_symmetry module
- torchref.symmetry.map_symmetry_interpolation module
- torchref.symmetry.reciprocal_symmetry module
ReciprocalSymmetry()ReciprocalSymmetryGridReciprocalSymmetryGrid.space_groupReciprocalSymmetryGrid.grid_shapeReciprocalSymmetryGrid.symmetryReciprocalSymmetryGrid.n_opsReciprocalSymmetryGrid.__init__()ReciprocalSymmetryGrid.apply_to_indices()ReciprocalSymmetryGrid.get_phase_shift()ReciprocalSymmetryGrid.get_symmetry_mate()ReciprocalSymmetryGrid.get_all_symmetry_mates()ReciprocalSymmetryGrid.symmetry_average()ReciprocalSymmetryGrid.expand_to_p1()ReciprocalSymmetryGrid.apply_friedel()ReciprocalSymmetryGrid.is_systematic_absence()ReciprocalSymmetryGrid.is_centric()ReciprocalSymmetryGrid.get_epsilon()ReciprocalSymmetryGrid.forward()ReciprocalSymmetryGrid.__call__()ReciprocalSymmetryGrid.get_symmetry_info()
expand_hkl()complete_hkl()expand_reflections()reduce_hkl()canonicalize_hkl()expand_reciprocal_grid()
- torchref.symmetry.spacegroup module
spacegroup_to_str()get_symmetry_operations()get_operations_as_tensors()is_same_spacegroup()get_point_group()get_crystal_system()is_centrosymmetric()n_operations()is_fft_friendly()find_fft_friendly_size()get_grid_requirements()check_grid_compatibility()suggest_grid_size()SpaceGroupSpaceGroup.matricesSpaceGroup.translationsSpaceGroup.n_opsSpaceGroup.__init__()SpaceGroup.n_opsSpaceGroup.nameSpaceGroup.hmSpaceGroup.xhmSpaceGroup.numberSpaceGroup.gemmiSpaceGroup.point_groupSpaceGroup.crystal_systemSpaceGroup.centrosymmetricSpaceGroup.dtypeSpaceGroup.deviceSpaceGroup.spacegroupSpaceGroup.space_groupSpaceGroup.space_group_nameSpaceGroup.space_group_numberSpaceGroup.short_name()SpaceGroup.operations()SpaceGroup.apply()SpaceGroup.apply_to_hkl()SpaceGroup.expand_coords_to_P1()SpaceGroup.forward()SpaceGroup.get_grid_requirements()SpaceGroup.check_grid_compatibility()SpaceGroup.suggest_grid_size()SpaceGroup.__hash__()SpaceGroup.__eq__()SpaceGroup.copy()
- torchref.symmetry.symmetry module