User Guide: CFFT Acceleration
Polyorder.jl supports CFFT (Crystallographic FFT) Acceleration for SCFT simulations of ordered phases with crystallographic symmetry. Unlike DCT acceleration (limited to 12 mirror-symmetric space groups), CFFT works for all 230 space groups, including Gyroid ($Ia\bar{3}d$, SG 230). For technical details, see CFFT Acceleration.
Quick Start
Prerequisites
Install CrystallographicFFT.jl:
using Pkg
Pkg.add("CrystallographicFFT")Enabling CFFT
Option A — High-level (recommended)
Pass use_cfft=true to the NoncyclicChainSCFT constructor:
using Polyorder, Polymer, Scattering, CrystallographicFFT
# 1. Define system
sys = AB_system(χN=20.0, fA=0.5)
# 2. Create lattice WITH space group
uc = UnitCell(Cubic(), 6.3)
lat = BravaisLattice(uc, 221) # Pm-3m
# 3. Create SCFT: CFFT field is created automatically
scft = NoncyclicChainSCFT(sys, lat; ds=0.01, mde=OSF,
use_cfft=true, profile=:balanced)
# 4. Solve as usual
Polyorder.solve!(scft)Option B — Lower-level (for more control)
Construct a CFFT field manually and pass it to the constructor:
using Polyorder, Polymer, Scattering, CrystallographicFFT
sys = AB_system(χN=20.0, fA=0.5)
uc = UnitCell(Cubic(), 6.3)
lat = BravaisLattice(uc, 221)
# Create CFFT field — sg_num is read from lattice automatically
w_cfft = cfft_auxiliaryfield(lat; spacing=0.15)
# automatically use the CFFT solver when passing a CFFT field
scft = NoncyclicChainSCFT(sys, w_cfft; ds=0.01, mde=OSF, profile=:balanced)
Polyorder.solve!(scft)CrystallographicFFT.jl must be loaded (using CrystallographicFFT) before calling cfft_auxiliaryfield or using use_cfft=true. Otherwise, a clear error message is shown.
No code changes are needed in the solve loop — the solver automatically uses CFFT internally.
Verifying CFFT is Active
# Check field type
println(typeof(scft.wfields[1]))
# → CFFTAuxiliaryField{Float64, 3, ...}
# Check subgrid vs full grid
w = scft.wfields[1]
println("Subgrid: ", size(w.data)) # e.g., (12, 12, 12)
println("Full grid: ", w.N_full) # e.g., (24, 24, 24)Performance
Memory & Speed
CFFT operates on a subgrid $M^3 = (N/L)^3$, reducing both memory and computation:
| Component | FFT Mode (Full Grid) | CFFT Mode (Subgrid) | Savings |
|---|---|---|---|
| Solver Buffers | $N^3$ | $M^3 = N^3/L^3$ | $L^3\times$ (8× for $L=2$) |
| Spectral Buffer | $N^3$ complex | $n_\text{spec}$ complex | ~58× for Pm-3m |
| MDE Step Speed | $O(N^3 \log N)$ | $O(M^3 \log M)$ | ~8× for $L=2$ |
With SymmetricStorage (:balanced profile)
Combining CFFT with SymmetricStorage gives maximum compression:
| Space Group | $L$ | Stars | Total Compression | Memory Saved |
|---|---|---|---|---|
| Pm-3m (221) | 2 | subgrid-compressed | ~40× | ~97% |
| Ia-3d (230) | 2 | 1,156 (for 48³) | ~96× | ~99% |
Supported Space Groups
CFFT supports all 230 space groups. Key examples:
| Phase | Space Group | SG # | Supported? |
|---|---|---|---|
| Lamellar | Pmmm | 47 | ✅ (CFFT and DCT) |
| Hexagonal Cylinder | P6/mmm | 191 | ✅ (CFFT only) |
| Simple Cubic | Pm-3m | 221 | ✅ (CFFT and DCT) |
| FCC Sphere | Fm-3m | 225 | ✅ (CFFT and DCT) |
| BCC Sphere | Im-3m | 229 | ✅ (CFFT and DCT) |
| Gyroid | Ia-3d | 230 | ✅ (CFFT only) |
| σ-Phase | P4₂/mnm | 136 | ✅ (CFFT only) |
For Gyroid (SG 230), CFFT is the only available spectral acceleration. DCT cannot handle glide planes.
GPU Support
CFFT fields work on GPU with no code changes beyond loading CUDA:
using CUDA
# Option A: High-level
scft = NoncyclicChainSCFT(sys, lat; ds=0.01, mde=OSF,
use_cfft=true, array_type=CuArray{Float64},
profile=:balanced)
# Option B: Manual field
w = cfft_auxiliaryfield(lat; spacing=0.15, array_type=CuArray{Float64})
scft = NoncyclicChainSCFT(sys, w; ds=0.01, mde=OSF, profile=:balanced)GPU performance has been validated on NVIDIA RTX 2080 Ti with Ia-3d (SG 230):
- CPU vs GPU free energy: $|\Delta F| < 10^{-11}$
- GPU iteration: ~311 ms vs CPU ~378 ms (for $N=48^3$)
- VariableCell: Full GPU support including stress tensor and grid resize
Spectral Updaters with CFFT
All SpectralSCFTAlgorithm updaters work with CFFT fields — both on CPU and GPU:
# SIS (semi-implicit, most commonly used as preconditioner)
solve!(scft, SIS(0.5))
# ETD (exponential time differencing)
solve!(scft, ETD(1.0))
# Anderson with SIS preconditioner (recommended)
solve!(scft, Anderson(SIS(0.5)))
# Full-matrix variants
solve!(scft, SISF(0.5))
solve!(scft, ETDF(1.0))
# Predictor-corrector variants
solve!(scft, PO(0.5))
solve!(scft, ETDPEC(1.0))The CFFT path is automatically selected when scft.wfields[1] is a CFFTAuxiliaryField. No additional configuration is needed.
For production runs, use Anderson(SIS(0.5)) — the Anderson acceleration operates in real space on the subgrid, while the SIS preconditioner uses the CFFT spectral path for optimal convergence.
Cell Optimization
CFFT is fully compatible with variable-cell SCFT (cell_solve!). The stress tensor is computed natively in the spectral ASU — no full-grid expansion needed. When the unit cell changes:
- The lattice is updated in-place
cfft_kk_orbsumis recomputed with the new lattice matrix- MDE diffusion kernels (
k²) are recalculated
This happens automatically inside the reset! function.
Resize Hysteresis
During variable-cell optimization, the grid size $N$ may oscillate if the cell edge is near a rounding threshold of best_N_fft. CFFT includes automatic hysteresis to prevent this:
- Grid growth is always accepted
- Grid shrinkage is only accepted if $N$ drops by more than $4L$ per dimension (four subgrid points)
This prevents expensive plan rebuilds from being triggered by rounding-level fluctuations in the cell edge. For example, with SG 221 ($L=2$), a shrinkage of $N$ from 56 to 54 (subgrid 28→27) is suppressed, while a shrinkage from 56 to 48 is accepted.
CFFT vs DCT: When to Use Which?
| Criterion | Choose DCT | Choose CFFT |
|---|---|---|
| Space group has only mirror planes | ✅ Prefer DCT | ✅ Also works |
| Space group has glide/screw | ❌ Cannot use | ✅ Required |
| Gyroid (SG 230) | ❌ | ✅ Only option |
| Custom VkDCT GPU kernel available | ✅ Fastest | ✅ Good |
| Non-orthogonal lattice | ❌ | ✅ Supported |
In general: choose CFFT if possible, especially non-symmorphic groups. DCT + VkDCT may have some speed advantage for low symmetry groups (but it uses full grid to compute density field and stresses, etc. which is slower than CFFT and consumes more memory).
Troubleshooting
"DimensionMismatch" during construction
Ensure your N dimensions are compatible with the space group. Use CrystallographicFFT.recommended_N:
using CrystallographicFFT
N_rec = recommended_N(sg_num, 64) # Suggest compatible N near 64SCFT diverges with CFFT
- Check symmetrization: Initial fields must be symmetrized. Use
init=:randn(default) which auto-symmetrizes, or manually callsymmetrize_field!. - Check Anderson norm: If using Anderson acceleration, ensure the
LinearAlgebra.normoverride is active (loaded via the CFFT extension automatically). - Reduce
ds: Tryds=0.005or smaller to check time-step stability.
Comparing CFFT with FFT results
To verify CFFT correctness against FFT:
# Create both solvers with identical initial fields
scft_fft = NoncyclicChainSCFT(sys, lat; ds=0.01, mde=OSF, init=:none)
scft_cfft = NoncyclicChainSCFT(sys, lat; ds=0.01, mde=OSF, init=:none, use_cfft=true)
# Set identical wfields (symmetrized)
# ... see test/cfft_vs_fft_onestep.jl for full procedureSpectral updater diverges with CFFT on GPU
If a spectral updater (SIS, ETD, etc.) crashes on GPU with KernelError: passing non-bitstype argument, ensure you are using the latest version of PolyorderCFFTExt — earlier versions allocated CPU buffers for spectral operations. The current implementation uses KernelAbstractions.allocate for all spectral work buffers.
Grid size oscillation during VariableCell
If you see repeated "Space resolution changed" messages alternating between two sizes (e.g., (27,27,27) ↔ (28,28,28)), the hysteresis threshold may need adjustment. The default threshold requires shrinkage > $4L$ per dimension. You can verify by checking the actual $N$ vs $M$ sizes:
w = scft.wfields[1]
println("N_full = ", w.N_full, ", M = ", size(w.data), ", L = ", stride_factors(w.cfft_pair))