FFTAnalysis¶
Overview¶
The rheojax.transforms.FFTAnalysis transform converts time-domain stress/strain
records into frequency-domain storage and loss moduli by performing a carefully conditioned
Fast Fourier Transform (FFT). The implementation emphasizes reproducible preprocessing,
windowing, and leakage control for arbitrary-waveform SAOS experiments.
Key Capabilities:
Time-frequency interconversion: \(G(t) \leftrightarrow G^*(\omega)\) transformations
LAOS harmonic extraction: Nonlinear moduli (\(G_3'\), \(G_3''\), \(I_3/I_1\))
Kramers-Kronig verification: Validate \(G'\), \(G''\) data consistency
Noise-robust processing: Windowing, detrending, zero-padding
Mathematical Theory¶
Fourier Transform Fundamentals¶
The continuous Fourier transform relates time-domain and frequency-domain representations:
For discrete sampled data at sampling rate \(f_s\) with \(N\) points, the discrete Fourier transform (DFT) is:
The Fast Fourier Transform (FFT) is an efficient \(O(N \log N)\) algorithm for computing the DFT, compared to \(O(N^2)\) for direct evaluation.
Frequency grid: The discrete frequencies corresponding to each FFT bin are:
Only the positive frequencies (\(k \le N/2\)) are retained due to signal symmetry for real-valued data.
Nyquist Sampling Criterion¶
To avoid aliasing (high-frequency content masquerading as low frequency), the sampling rate must satisfy the Nyquist criterion:
where \(f_{\max}\) is the highest frequency present in the signal. For rheological measurements:
SAOS at 1 Hz: \(f_s \ge 50\) Hz (\(25\times\) oversampling recommended)
Chirp 0.1-100 Hz: \(f_s \ge 500\) Hz (accounts for harmonics)
LAOS: \(f_s \ge 10 \times n \times f_{\max}\) (n harmonics)
Window Functions and Spectral Leakage¶
Spectral leakage occurs when the signal does not contain an integer number of periods within the sampling window. Energy “leaks” from the true frequency into adjacent bins.
Apodization windows \(w[n]\) suppress leakage by smoothly tapering signal edges:
Common window functions:
Window |
Leakage Suppression |
Frequency Resolution |
Application |
|---|---|---|---|
Rectangular |
None (worst) |
Best (narrowest) |
Integer cycles only |
Hann |
Good (-31 dB) |
Moderate (1.5× wider) |
General purpose |
Blackman-Harris |
Excellent (-92 dB) |
Poor (2× wider) |
High dynamic range |
Tukey (\(\alpha = 0.2\)) |
Moderate (-20 dB) |
Good (1.2× wider) |
Low distortion |
Flat-top |
Very good (-70 dB) |
Poor (3.8× wider) |
Amplitude accuracy |
Trade-off: Reduced leakage leads to wider main lobe and poorer frequency resolution.
Zero Padding and Frequency Resolution¶
Zero padding appends zeros to the signal before FFT:
Effect on frequency grid:
Original spacing: \(\Delta f = f_s / N\)
Padded spacing: \(\Delta f = f_s / N_{\text{fft}}\)
Important: Zero padding does NOT increase information content—it only interpolates the spectrum for smoother visualization and peak identification.
Rheological Applications: Time-Domain Interconversion¶
1. Stress Relaxation to Dynamic Moduli
From the Boltzmann superposition principle, the dynamic moduli can be computed from the relaxation modulus \(G(t)\) via:
Discrete approximation (numerical integration):
FFT approach: Faster for large datasets, computes all frequencies simultaneously.
2. Creep Compliance to Dynamic Compliance
Similarly, from creep compliance \(J(t)\):
3. Kramers-Kronig Relations
For causal linear systems, \(G'\) and \(G''\) are not independent—they satisfy the Kramers-Kronig integral relations:
where \(G_\infty = \lim_{\omega \to \infty} G'(\omega)\).
Applications:
Data validation: Check if experimental \(G'\), \(G''\) satisfy causality
Extrapolation: Estimate \(G'\) from measured \(G''\) (or vice versa)
Model verification: Ensure fitted models obey physical principles
LAOS: Higher Harmonic Extraction¶
In Large Amplitude Oscillatory Shear (LAOS), the stress response to sinusoidal strain \(\gamma(t) = \gamma_0 \sin(\omega t)\) becomes nonlinear:
FFT extracts odd harmonics:
n = 1: Linear response (\(G_1'\), \(G_1'' \approx\) SAOS moduli for small \(\gamma_0\))
n = 3, 5, 7, …: Nonlinear contributions
Nonlinearity quantification:
\(I_{3/1} < 0.01\): Linear viscoelastic region (LVR)
\(I_{3/1} > 0.1\): Significant nonlinearity (yielding, strain-stiffening)
Processing Pipeline¶
Acquire synchronized stress \(\sigma(t)\) and strain \(\gamma(t)\) at sampling rate \(f_s\) with consistent calibration.
Detrend the traces (constant, linear, or median) to remove DC offsets that cause leakage.
Window each record with \(w[n]\) and optionally zero-pad to \(N_{\text{fft}}\) samples (typically next power of two) to refine frequency spacing.
Compute FFTs
\[\hat{x}[k] = \sum_{n=0}^{N_{\text{fft}}-1} x[n] w[n] e^{-i 2\pi kn / N_{\text{fft}}}\]for both stress and strain.
Form complex modulus
\[G^*(\omega_k) = \frac{\hat{\sigma}[k]}{\hat{\gamma}[k]} = G'(\omega_k) + i G''(\omega_k),\]then project onto the positive-frequency half-plane.
Annotate diagnostics (leakage ratio, signal-to-noise, characteristic time \(\tau_c = 1/\omega_c\) where \(G' = G''\)).
Algorithm Details¶
Step-by-Step Procedure¶
Input: Time-domain data \(t, \sigma(t), \gamma(t)\) with \(N\) samples at \(f_s\) Hz.
Output: Frequency-domain moduli \(\omega_k, G'(\omega_k), G''(\omega_k)\).
Detrending:
Constant (DC removal): \(x'[n] = x[n] - \bar{x}\)
Linear: Subtract best-fit line \(x'[n] = x[n] - (a + b n)\)
Median: \(x'[n] = x[n] - \text{median}(x)\)
Windowing: Multiply by window function \(w[n]\):
\[x_w[n] = x'[n] \cdot w[n]\]Hann window:
\[w[n] = 0.5 \left[1 - \cos\left(\frac{2\pi n}{N-1}\right)\right]\]Zero padding: Extend to \(N_{\text{fft}} = 2^{\lceil \log_2(N \cdot \text{zero\_padding}) \rceil}\).
FFT computation: Compute \(\hat{\sigma}[k]\) and \(\hat{\gamma}[k]\) using FFT algorithm.
Complex modulus:
\[G^*[k] = \frac{\hat{\sigma}[k]}{\hat{\gamma}[k]}, \quad k = 1, \ldots, N_{\text{fft}}/2\]Storage modulus: \(G'[k] = \text{Re}(G^*[k])\)
Loss modulus: \(G''[k] = \text{Im}(G^*[k])\)
Frequency grid:
\[\omega_k = \frac{2\pi k f_s}{N_{\text{fft}}}, \quad f_k = \frac{\omega_k}{2\pi}\]
Computational Complexity¶
FFT: \(O(N \log N)\)
Windowing, detrending: \(O(N)\)
Total: \(O(N \log N)\)
Speedup over DFT: \(\sim 100\times\) for \(N = 1024\), \(\sim 1000\times\) for \(N = 8192\).
Edge Effects and Artifacts¶
End-point discontinuities: Signal jumps at boundaries cause high-frequency ringing.
Mitigation:
Use tapering windows (Hann, Tukey) to smoothly reduce signal to zero at edges
Apply reflection padding before windowing (mirror signal at boundaries)
Ensure integer number of periods if possible
Gibbs phenomenon: Sharp transitions (step functions) exhibit oscillatory overshoots in frequency domain.
Mitigation: Use higher-order windows (Blackman-Harris) for sharper roll-off.
Parameters¶
Parameter |
Type |
Description |
Default |
|---|---|---|---|
|
str | callable |
Apodization window ( |
|
|
int | None |
FFT length after padding; controls frequency spacing. |
|
|
str |
|
|
|
float |
Factor (>1) applied to extend the record before FFT. |
|
Parameter Selection Guidelines¶
Window function:
Hann: Default, good balance of leakage/resolution
Tukey(0.1-0.2): Low distortion, minimal main-lobe widening
Blackman-Harris: Maximum leakage suppression (-92 dB)
Flat-top: Amplitude accuracy (±0.01 dB)
Zero padding factor:
1.0: No padding (coarse frequency grid)
2.0: Default (doubles frequency resolution, smooth visualization)
4.0-8.0: Very fine interpolation for peak detection
Detrending:
Linear: Remove linear drift (recommended for most rheology data)
Median: Robust to outliers (e.g., instrument glitches)
Constant: Simple DC removal (for AC-coupled data)
Input / Output Specifications¶
Input:
rheojax.core.data.RheoDatain the time domain with fieldsdata.x= time (s) anddata.ycontaining stress/strain channels (Pa, dimensionless). Metadata must provide sampling ratefs(Hz) or timestamps must be uniformly spaced.Output:
RheoDatain frequency domain withx= angular frequency array (rad/s),metadatakeysfrequency_hz,G_prime,G_double_prime,G_complex, and diagnostics (leakage, SNR, characteristic time).
Applications and Use Cases¶
When to Use FFT Transform¶
1. Time-domain to Frequency-domain conversion
Convert \(G(t)\) from stress relaxation to \(G'(\omega)\), \(G''(\omega)\) for model fitting
Convert \(J(t)\) from creep to \(J'(\omega)\), \(J''(\omega)\) for compliance analysis
Advantage: Single measurement spans decades in frequency
2. LAOS higher harmonics extraction
Quantify nonlinearity via \(I_3/I_1\), \(I_5/I_1\) intensity ratios
Identify yield point, strain-stiffening, strain-softening
Advantage: Full harmonic decomposition from single waveform
3. Mastercurve construction
FFT-based interpolation and smoothing of multi-temperature data
Reduce noise before time-temperature superposition
Advantage: Consistent frequency grids for overlaying datasets
4. Kramers-Kronig validation
Verify experimental \(G'\), \(G''\) data consistency
Detect measurement artifacts (inertia, compliance, slip)
Advantage: Independent check of data quality
Input Data Requirements¶
Minimum requirements:
Uniform sampling: Timestamps equally spaced (±1%)
Sufficient duration: \(T \ge 10/f_{\min}\) (at least 10 periods of lowest frequency)
Adequate sampling rate: \(f_s \ge 50 f_{\max}\) (Nyquist + safety margin)
Stable baseline: No drift, offset, or low-frequency noise
Recommended:
Integer number of periods: Minimizes spectral leakage
High SNR: Signal-to-noise ratio > 30 dB (1000:1)
Synchronized channels: Stress and strain aligned (< 0.01 sample lag)
Output Interpretation¶
Frequency grid spacing:
Characteristic frequency (crossover):
Loss tangent:
\(\tan \delta < 1\): Solid-like (\(G' > G''\))
\(\tan \delta = 1\): Balanced viscoelastic (crossover point)
\(\tan \delta > 1\): Liquid-like (\(G'' > G'\))
Integration with RheoJAX Models¶
Pre-Processing Recommendations¶
Before model fitting:
FFT \(G(t) \to G^*(\omega)\) to obtain broadband frequency-domain data
Select frequency range where SNR > 10 dB
Apply Kramers-Kronig check to validate data consistency
Fit fractional models (FMG, FML, FZSS) to \(G'(\omega)\), \(G''(\omega)\)
Workflow:
from rheojax.transforms import FFTAnalysis
from rheojax.models import FractionalMaxwellGel
# 1. Transform time-domain relaxation data
fft = FFTAnalysis(window="hann", n_fft=2048, detrend="linear")
freq_data = fft.transform(relaxation_data)
omega = freq_data.x
G_prime = freq_data.metadata["G_prime"]
G_double_prime = freq_data.metadata["G_double_prime"]
# 2. Fit fractional gel model to frequency-domain data
model = FractionalMaxwellGel()
model.fit(omega, np.column_stack([G_prime, G_double_prime]), test_mode='oscillation')
print(f"Fitted α = {model.parameters.get_value('alpha'):.3f}")
Models That Benefit from FFT¶
Fractional models:
Fractional Maxwell Gel (Fractional) — Power-law relaxation with parallel \(G'\), \(G''\) scaling
Fractional Maxwell Liquid (Fractional) — Broad relaxation spectrum
Fractional Zener Solid-Solid (Fractional) — Solid-like with finite \(G_e\)
Classical models:
Maxwell (Classical) — Exponential relaxation with Lorentzian spectrum
Zener (Standard Linear Solid) — Relaxation to equilibrium modulus
Post-Transform Fitting Strategies¶
1. Frequency-domain fitting (recommended):
Fit \(G'(\omega)\), \(G''(\omega)\) directly from FFT output
Advantage: Models naturally expressed in frequency domain
Disadvantage: Frequency bins may be unevenly weighted
2. Time-domain fitting with FFT verification:
Fit \(G(t)\) in time domain
FFT-transform fitted model and compare with experimental FFT
Advantage: Direct comparison with raw relaxation data
Disadvantage: Requires inverse FFT for model predictions
3. Hybrid approach:
Use FFT to initialize parameters (identify \(\alpha\), \(\tau\) from slopes)
Refine via time-domain fitting for better noise robustness
Common Workflows¶
Workflow 1: Relaxation to Oscillation interconversion
# Measure G(t) via stress relaxation (10s duration, 100 Hz sampling)
# Use FFT to predict G'(ω), G''(ω) for oscillatory equivalent
fft = FFTAnalysis(window="tukey(0.15)", n_fft=4096)
oscillation_data = fft.transform(relaxation_data)
# Now fit oscillatory models without running frequency sweep
Workflow 2: LAOS harmonics + yield stress identification
# Extract I₃/I₁ from LAOS data
fft = FFTAnalysis(window="hann", n_fft=8192)
result = fft.transform(laos_data)
G1_pp = result.metadata["harmonics"][1]["G_double_prime"]
G3_pp = result.metadata["harmonics"][3]["G_double_prime"]
I31 = G3_pp / G1_pp
# Fit Herschel-Bulkley model to flow curve derived from I₃/I₁
Workflow 3: Mastercurve + FFT smoothing
# Use FFT-based interpolation for smooth frequency grids
fft = FFTAnalysis(zero_padding=4.0)
smoothed_datasets = [fft.transform(data) for data in raw_datasets]
# Apply time-temperature superposition
from rheojax.transforms import Mastercurve
mc = Mastercurve(reference_temp=298.15, shift_model="wlf")
master = mc.create_mastercurve(smoothed_datasets, temps)
Validation and Quality Control¶
Diagnostic Checks¶
1. Leakage ratio: Ratio of out-of-bin energy to total energy
Leakage < 1%: Excellent (integer periods, good windowing)
Leakage 1-5%: Acceptable (typical for experimental data)
Leakage > 10%: Poor (non-integer periods, insufficient windowing)
2. Signal-to-noise ratio (SNR):
SNR > 30 dB: Excellent (clean harmonics, reliable \(G_3'\), \(G_5'\))
SNR 20-30 dB: Good (\(G_1'\), \(G_1''\) reliable)
SNR < 20 dB: Poor (high-frequency noise dominates)
3. Kramers-Kronig compliance:
Compute \(G'_{\text{KK}}\) from \(G''\) using Kramers-Kronig transform, compare with measured \(G'\):
Common Failure Modes¶
1. Excessive leakage:
Symptom: Spurious peaks between harmonics, broad spectral smearing
Cause: Non-integer number of periods, rectangular window
Fix: Use Hann/Blackman-Harris window, ensure integer cycles
2. Aliasing:
Symptom: High-frequency noise folded into low frequencies
Cause: \(f_s < 2 f_{\max}\) (Nyquist violation)
Fix: Increase sampling rate, apply anti-aliasing filter
3. Noise amplification:
Symptom: \(G'(\omega)\), \(G''(\omega)\) noisy at high frequencies
Cause: Inherent FFT sensitivity to high-frequency noise
Fix: Apply low-pass filter, average multiple acquisitions, reduce frequency range
4. DC offset / drift:
Symptom: Large zero-frequency bin, distorted low-frequency moduli
Cause: Baseline drift, instrument offset
Fix: Apply linear or median detrending before FFT
Parameter Sensitivity¶
Window function sensitivity:
Hann vs Rectangular: -31 dB leakage suppression, \(1.5\times\) main-lobe widening
Blackman-Harris: -92 dB suppression but \(2\times\) wider main lobe (poorer resolution)
Zero padding sensitivity:
1× vs 2×: Doubled frequency bins (smoother visualization)
4× vs 8×: Marginal improvement (diminishing returns)
Detrending sensitivity:
Constant vs Linear: Linear removes drift (recommended for long acquisitions)
Median vs Linear: Median robust to outliers (spikes, glitches)
Cross-Validation Techniques¶
1. Kramers-Kronig validation:
G_prime_reconstructed = kramers_kronig_transform(G_double_prime, omega)
error = np.linalg.norm(G_prime - G_prime_reconstructed) / np.linalg.norm(G_prime)
assert error < 0.05, "Kramers-Kronig violation detected"
2. Multi-window comparison:
# Compare results with different windows
windows = ["hann", "blackman", "tukey(0.2)"]
results = [FFTAnalysis(window=w).transform(data) for w in windows]
# Verify consistent G'(ω), G''(ω) within 5%
assert np.std([r.metadata["G_prime"] for r in results]) < 0.05 * np.mean(...)
3. Repeated acquisitions:
# Average multiple FFTs for noise reduction
ffts = [FFTAnalysis().transform(data_i) for data_i in repeated_measurements]
G_prime_avg = np.mean([f.metadata["G_prime"] for f in ffts], axis=0)
G_prime_std = np.std([f.metadata["G_prime"] for f in ffts], axis=0)
# SNR estimate
SNR_dB = 20 * np.log10(G_prime_avg / G_prime_std)
Usage¶
from rheojax.transforms import FFTAnalysis
fft = FFTAnalysis(window="tukey(0.15)", n_fft=8192, detrend="median")
freq_data = fft.transform(time_domain_data)
omega = freq_data.x
Gp = freq_data.metadata["G_prime"]
Gpp = freq_data.metadata["G_double_prime"]
Worked Example¶
Scenario: Convert stress relaxation \(G(t)\) to frequency-domain \(G'(\omega)\), \(G''(\omega)\) for fractional model fitting.
Input Data:
Relaxation modulus: \(G(t) = 10^5\) Pa at \(t = 0.01\) s to \(5 \times 10^3\) Pa at \(t = 10\) s
Sampling: 1000 points over 10 s (\(f_s = 100\) Hz)
Noise: \(\sim 1\%\) RMS
Step-by-step transformation:
import numpy as np
from rheojax.transforms import FFTAnalysis
from rheojax.core.data import RheoData
# 1. Generate synthetic relaxation data (power-law decay)
t = np.logspace(-2, 1, 1000) # 0.01s to 10s, logarithmic
G_t = 1e5 * (t / 0.01)**(-0.3) # Power-law: G(t) ~ t^(-α)
G_t += np.random.normal(0, 1e3, size=t.shape) # Add 1% noise
relaxation_data = RheoData(x=t, y=G_t, metadata={'test_mode': 'relaxation'})
# 2. Configure FFT transform
fft = FFTAnalysis(
window="hann", # Good balance leakage/resolution
n_fft=4096, # Zero-pad to 4096 (from 1000 samples)
detrend="linear", # Remove linear drift
zero_padding=4.0 # 4× zero padding for smooth spectrum
)
# 3. Transform to frequency domain
freq_data = fft.transform(relaxation_data)
omega = freq_data.x # rad/s
G_prime = freq_data.metadata["G_prime"]
G_double_prime = freq_data.metadata["G_double_prime"]
# 4. Diagnostics
print(f"Frequency range: {omega.min():.3f} to {omega.max():.1f} rad/s")
print(f"Leakage ratio: {freq_data.metadata['leakage']:.2%}")
print(f"SNR: {freq_data.metadata['SNR_dB']:.1f} dB")
# 5. Identify crossover frequency
tan_delta = G_double_prime / G_prime
idx_crossover = np.argmin(np.abs(tan_delta - 1.0))
omega_c = omega[idx_crossover]
tau_c = 1.0 / omega_c
print(f"Crossover: ω_c = {omega_c:.2f} rad/s → τ_c = {tau_c:.3f} s")
Expected Output:
Frequency range: 0.628 to 314.2 rad/s (0.1 to 50 Hz)
Leakage ratio: 2.3%
SNR: 38.5 dB
Crossover: ω_c = 6.28 rad/s → τ_c = 0.159 s
Interpretation:
Power-law scaling: \(G'(\omega) \sim G''(\omega) \sim \omega^\alpha\) (parallel in log-log plot), indicating a fractional model
\(\alpha \approx 0.3\): Gel-like behavior (\(0 < \alpha < 0.5\))
Recommended model: FractionalMaxwellGel with \(\alpha \approx 0.3\)
Troubleshooting¶
Excessive leakage – ensure each record contains an integer number of cycles or choose a higher-taper window (Tukey, Blackman-Harris).
Aliasing – verify the sampling rate satisfies Nyquist (
fs >= 2 * f_max) or resample.Phase jumps between stress/strain – enable cross-correlation alignment or median detrending to remove offsets before FFT.
Noisy \(G'\) at low frequency – widen
n_fft(heavier padding) and average multiple acquisitions viastack_mode="average".Kramers-Kronig violations – check for instrument artifacts (inertia, compliance, slip).
Spurious peaks – verify window function applied correctly, check for electrical noise.
References¶
Ferry, Viscoelastic Properties of Polymers, 3rd ed. Wiley, 1980.
M. Wilhelm, D. Maring, & H. W. Spiess, “Fourier-transform rheology.” Rheol. Acta 37, 399-405 (1998). https://doi.org/10.1007/s003970050126
M. Wilhelm, P. Reinheimer, & M. Ortseifer, “High sensitivity Fourier-transform rheology.” Rheol. Acta 38, 349-356 (1999). https://doi.org/10.1007/s003970050185
Booij, H.C., Thoone, G.P.J.M. (1982). “Generalization of Kramers-Kronig Transforms and Some Approximations of Relations between Viscoelastic Quantities.” Rheologica Acta, 21(1), 15-24. https://doi.org/10.1007/BF01520701
Hyun, K., Wilhelm, M., Klein, C.O., et al. (2011). “A Review of Nonlinear Oscillatory Shear Tests: Analysis and Application of LAOS.” Progress in Polymer Science, 36(12), 1697-1753. https://doi.org/10.1016/j.progpolymsci.2011.02.002
See also¶
Maxwell (Classical) and Zener (Standard Linear Solid) — FFTAnalysis provides the \(G^*(\omega)\) input required for these fits.
Generalized Fractional Maxwell (Two-Order) — broadband spectra obtained via FFT enable fractional-order identification.
Mastercurve (Time-Temperature Superposition) — combine FFT-derived spectra across temperature before fitting.
OWChirp — chirp experiments are often FFT processed using the same pipeline.
../../examples/transforms/01-fft-time-to-frequency — notebook showing a full FFT preprocessing workflow.