Skip to main content

Triple-Axis Spectrometer Resolution Calculator with Numba/PyTorch acceleration

Project description

Resolution Calculator for Triple-Axis Spectrometers

A Python library for computing resolution matrices and performing S(Q,w) convolutions for Triple-Axis Spectrometer (TAS) experiments. This package provides both NumPy (CPU) and PyTorch backends for flexible deployment.

Based on ResLib by Andrei Zheludev.

Features

  • Cooper-Nathans resolution formalism with optional Popovici method
  • Dual backend support: NumPy for CPU, PyTorch for GPU acceleration
  • Automatic device detection: CUDA, MPS (Apple Silicon), or CPU
  • S(Q,w) convolution: Integrate scattering cross-sections with resolution function
  • Batched operations: Efficient computation for thousands of Q-points simultaneously

Installation

Requirements

  • Python >= 3.9
  • NumPy
  • SciPy
  • PyTorch (optional, for GPU acceleration)
  • lattice-calculator

Install from PyPI

pip install rescalculator

Install from source

git clone https://github.com/scattering/rescalculator.git
cd rescalculator
pip install -e .

Setting up a conda environment

conda create -n resolution python=3.11 pytorch numpy scipy matplotlib -c pytorch
conda activate resolution
pip install lattice-calculator
pip install -e .

Quick Start

Basic Resolution Calculation

import numpy as np
from rescalculator import TASResolution
from lattice_calculator import Lattice, modvec

# Define crystal lattice
lattice = Lattice(
    a=5.0, b=5.0, c=5.0,
    alpha=90, beta=90, gamma=90,
    orient1=np.array([[1, 0, 0]]),
    orient2=np.array([[0, 1, 0]])
)

# Define experiment configuration
EXP = {
    'efixed': 14.7,              # Fixed energy (meV)
    'infin': -1,                 # -1 for fixed Ef, +1 for fixed Ei
    'dir1': 1,                   # Monochromator scattering direction
    'dir2': 1,                   # Analyzer scattering direction
    'hcol': np.array([40, 40, 40, 40]),  # Horizontal collimations (arcmin)
    'vcol': np.array([120, 120, 120, 120]),  # Vertical collimations (arcmin)
    'mono': {'tau': 'pg(002)', 'mosaic': 30},  # Monochromator
    'ana': {'tau': 'pg(002)', 'mosaic': 30},   # Analyzer
    'sample': {'mosaic': 30},                   # Sample mosaic
    'arms': [200, 200, 150, 150, 100],         # Spectrometer arms (cm)
    'horifoc': -1,               # Horizontal focusing (-1 = no focusing)
    'moncor': 1,                 # Monitor correction
}

# Create resolution calculator
res = TASResolution(lattice, backend='numpy')

# Define scan points
H = np.array([1.0, 1.05, 1.1])
K = np.zeros(3)
L = np.zeros(3)
W = np.array([5.0, 5.0, 5.0])  # Energy transfer (meV)
lattice.npts = len(H)

# Calculate resolution matrices
R0, RMS = res.ResMatS(H, K, L, W, [EXP] * len(H))

# R0: Prefactors (normalization)
# RMS: Resolution matrices in sample coordinates, shape (4, 4, npts)
print(f"Resolution matrix at first point:\n{RMS[:,:,0]}")

S(Q,w) Convolution with Spin Waves

import numpy as np
from rescalculator import TASResolution, ConvolutionCalculator
from lattice_calculator import Lattice
from pyspinw import SpinW

# Set up spin wave model using pyspinw
model = SpinW()
model.genlattice(lat_const=[3.0, 3.0, 3.0], angled=[90, 90, 90])
model.addatom(r=[0, 0, 0], S=1, label='Fe')
model.gencoupling(max_distance=4.0)
model.addmatrix(label='J', value=1.0)
model.addcoupling(bond=0, mat_labels='J')
model.genmagstr(mode='direct', S=[[0], [0], [1]])

# Create S(Q,w) function
def sqw_function(h, k, l, w):
    """Spin wave S(Q,w) at given (h,k,l,w)."""
    spec = model.spinwave([[h], [k], [l]], use_surface_integrals=True)
    # ... process spectrum ...
    return intensity

# Set up convolution
lattice = Lattice(a=3.0, b=3.0, c=3.0, alpha=90, beta=90, gamma=90,
                  orient1=np.array([[1,0,0]]), orient2=np.array([[0,1,0]]))

conv = ConvolutionCalculator(
    resolution=TASResolution(lattice, backend='numpy'),
    sqw_function=sqw_function,
    lattice=lattice
)

# Calculate convoluted intensity
H_scan = np.linspace(0.1, 0.9, 50)
K_scan = np.zeros(50)
L_scan = np.zeros(50)
W_scan = np.full(50, 5.0)

intensity = conv.convolve(H_scan, K_scan, L_scan, W_scan, EXP_list)

Performance Notes

Backend Comparison

This library supports multiple backends with dramatically different performance:

Backend 1,000 pts 10,000 pts 100,000 pts Speedup
Numba (CPU) 4 ms 39 ms 395 ms 26x
NumPy (CPU) 105 ms 998 ms 10,288 ms 1x
PyTorch (MPS) 1,580 ms 13,552 ms OOM 0.08x

Key findings:

  1. Numba is fastest - JIT compilation with parallel execution gives ~26x speedup
  2. Small matrices don't benefit from GPU: Resolution calculations involve thousands of small (4x4 to 8x8) matrix operations, which don't parallelize efficiently on GPU
  3. GPU overhead dominates: Data transfer and kernel launch overhead exceeds computation time for small matrices
  4. More points don't help GPU: GPU performance actually degrades with more points due to memory pressure

Recommendation: Install with pip install rescalculator[fast] to get Numba acceleration. The default backend='auto' will automatically use Numba if available.

When to use each backend

  • Numba (recommended): Best performance for all use cases
  • NumPy: Fallback when Numba not available, always works
  • PyTorch: Only for gradient computation or integration with PyTorch models

Memory Efficiency

The library handles large numbers of points efficiently:

  • Numba/NumPy: Tested with 100,000+ points
  • Memory scales linearly with point count
  • Each point requires approximately 1-2 KB of memory

API Reference

TASResolution

class TASResolution(lattice, backend='auto')

Main resolution calculator class.

Parameters:

  • lattice: Lattice instance from lattice-calculator
  • backend: 'auto', 'numba', 'numpy', or 'pytorch'

Methods:

  • ResMatS(H, K, L, W, EXP): Compute resolution matrices in sample coordinates
  • ResMat_vectorized(Q, W, EXP): Compute resolution matrices for Q magnitude array
  • set_backend(backend_type): Change computational backend

BatchedTASResolution

class BatchedTASResolution(lattice, device='auto')

Fully batched resolution calculator using PyTorch tensor operations.

Parameters:

  • lattice: Lattice instance
  • device: 'auto', 'cuda', 'mps', or 'cpu'

Methods:

  • ResMat_batched(Q, W, EXP): Batched resolution calculation returning tensors
  • ResMatS(H, K, L, W, EXP): Resolution matrices in sample coordinates

ConvolutionCalculator

class ConvolutionCalculator(resolution, sqw_function, lattice)

Convolves S(Q,w) with resolution function.

Parameters:

  • resolution: TASResolution instance
  • sqw_function: Function f(h, k, l, w) -> intensity
  • lattice: Lattice instance

Experiment Configuration (EXP)

The EXP dictionary contains all spectrometer parameters:

EXP = {
    'efixed': 14.7,       # Fixed energy (meV)
    'infin': -1,          # -1 = fixed Ef, +1 = fixed Ei
    'dir1': 1,            # Monochromator scattering direction (+1 or -1)
    'dir2': 1,            # Analyzer scattering direction (+1 or -1)
    'hcol': [40, 40, 40, 40],      # Horizontal collimations (arcmin)
    'vcol': [120, 120, 120, 120],  # Vertical collimations (arcmin)
    'mono': {
        'tau': 'pg(002)',   # Monochromator crystal
        'mosaic': 30,       # Mosaic spread (arcmin)
        'rh': 1e6,          # Horizontal radius (cm), 1e6 = flat
        'rv': 1e6,          # Vertical radius (cm)
    },
    'ana': {
        'tau': 'pg(002)',   # Analyzer crystal
        'mosaic': 30,
        'rh': 1e6,
        'rv': 1e6,
    },
    'sample': {
        'mosaic': 30,       # Sample mosaic (arcmin)
        'vmosaic': 30,      # Vertical sample mosaic (arcmin)
    },
    'arms': [200, 200, 150, 150, 100],  # L0, L1p, L1, L2, L3 (cm)
    'horifoc': -1,          # Horizontal focusing (-1 = off)
    'moncor': 1,            # Monitor correction (0 or 1)
    'method': 0,            # 0 = Cooper-Nathans, 1 = Popovici
}

Available monochromator/analyzer crystals:

  • 'pg(002)': PG(002), tau = 1.873 A^-1
  • 'pg(004)': PG(004), tau = 3.746 A^-1
  • 'ge(111)': Ge(111), tau = 1.924 A^-1
  • 'ge(220)': Ge(220), tau = 3.141 A^-1
  • 'ge(311)': Ge(311), tau = 3.684 A^-1
  • 'be(002)': Be(002), tau = 3.507 A^-1

Examples

See the examples/ directory for complete working examples:

  • spinwave_convolution.py: Ferromagnetic chain convolution with pyspinw
  • benchmark.py: Performance comparison between backends

Dependencies

References

  1. Cooper, M. J. & Nathans, R. (1967). Acta Cryst. 23, 357-367.
  2. Popovici, M. (1975). Acta Cryst. A31, 507-513.
  3. Chesser, N. J. & Axe, J. D. (1973). Acta Cryst. A29, 160-169.
  4. Zheludev, A. ResLib (MATLAB version)

License

MIT License

Authors

William Ratcliff (NIST Center for Neutron Research)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rescalculator-2.0.0.tar.gz (102.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rescalculator-2.0.0-py3-none-any.whl (121.0 kB view details)

Uploaded Python 3

File details

Details for the file rescalculator-2.0.0.tar.gz.

File metadata

  • Download URL: rescalculator-2.0.0.tar.gz
  • Upload date:
  • Size: 102.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for rescalculator-2.0.0.tar.gz
Algorithm Hash digest
SHA256 30fbf50712f71406e2809fc565135cf1025aae05d020994241e0c6f3bb4d44f3
MD5 98e5760cf306ee85326c0f26b70b22ab
BLAKE2b-256 4ea1ec5eaf0d99a10080c2c372c1e297450710eeeb174d64ea3a9ee1ef9abb81

See more details on using hashes here.

File details

Details for the file rescalculator-2.0.0-py3-none-any.whl.

File metadata

  • Download URL: rescalculator-2.0.0-py3-none-any.whl
  • Upload date:
  • Size: 121.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.14

File hashes

Hashes for rescalculator-2.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 678edd738586ed2b2690f2d313441b95bb151a145c56016b18564be0fbdfc031
MD5 f61f970fe06cb2ef353622222d19c85d
BLAKE2b-256 ea492c81a9420944cf467c6c0b40f8a0d409644809067941863d8ce868550c5c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page