Complete Guide to Data Transforms¶
Processing Rheological Data for Advanced Analysis
Overview¶
RheoJAX provides 7 specialized data transforms that process raw rheological measurements
into derived quantities. These transforms operate on RheoData objects or raw NumPy/JAX
arrays and extract physically meaningful information for material characterization, nonlinearity
quantification, and data quality assessment.
Key Insight
Transforms are pre-processing and analysis tools—not constitutive models. They convert raw experimental data (stress, strain, time, frequency) into derived physical quantities (harmonics, shift factors, yield stresses, moduli distributions) that reveal material behavior or prepare data for model fitting.
Why Use Transforms?
Extend measurement ranges: TTS/SRFS create mastercurves spanning 8-12 decades
Extract hidden physics: SPP reveals yield stresses and cage moduli from LAOS waveforms
Quantify nonlinearity: FFT harmonics and Mutation Number measure departure from linearity
Improve signal quality: Smooth Derivative handles noisy experimental data
Accelerate measurements: OWChirp processes chirp data 10-100× faster than discrete sweeps
When to Use Transforms¶
I Have… |
I Want… |
Use This Transform |
|---|---|---|
LAOS time-domain data |
Harmonic amplitudes \(I_1, I_3, I_5, \ldots\) |
|
Freq sweeps at multiple T |
Single master curve |
Mastercurve (TTS) |
Flow curves at multiple \(\dot{\gamma}\) |
Flow curve master + noise temp |
|
LAOS waveform data |
Yield stress, cage modulus |
|
Noisy experimental data |
Smooth derivative \(dG/dt\) |
|
Any rheological data |
Linearity check (\(\Delta\)) |
|
Chirp oscillatory data |
\(G'(\omega)\), \(G''(\omega)\) from single test |
1. FFT (Fast Fourier Transform)¶
Frequency Decomposition of Oscillatory Signals
Theory¶
The Fast Fourier Transform (FFT) decomposes a periodic time-domain signal into its constituent frequency components. For Large Amplitude Oscillatory Shear (LAOS), the stress response becomes nonsinusoidal and contains higher harmonics:
where: - \(\omega\): Fundamental frequency (rad/s) - \(n\): Harmonic number (odd integers for symmetric strain) - \(\sigma'_n, \sigma''_n\): Elastic and viscous stress components at harmonic \(n\)
Key Nonlinearity Metrics:
where \(I_n = \sqrt{(\sigma'_n)^2 + (\sigma''_n)^2}\) is the intensity of harmonic \(n\).
Physical Interpretation: - \(I_3/I_1 < 0.01\): Linear viscoelastic regime (SAOS) - \(I_3/I_1 \sim 0.1\): Weakly nonlinear (MAOS) - \(I_3/I_1 > 0.5\): Strongly nonlinear (LAOS) — yield, flow, cage breaking
Usage¶
from rheojax.transforms import FFTAnalysis
from rheojax.core.data import RheoData
import numpy as np
# Generate LAOS data (10 cycles, omega = 1 rad/s)
t = np.linspace(0, 10*2*np.pi, 2000)
omega = 1.0
gamma_0 = 1.0 # Large strain amplitude
# Nonlinear stress response (contains 3rd harmonic)
stress = 100*np.sin(omega*t) + 20*np.sin(3*omega*t)
# Create RheoData
data = RheoData(x=t, y=stress, test_mode='oscillation')
# Apply FFT with Hann window to reduce spectral leakage
fft = FFTAnalysis(window='hann', detrend=True)
result = fft.transform(data)
# Extract harmonics (result is dict: {1: {...}, 3: {...}, ...})
harmonics = result['harmonics']
I1 = harmonics[1]['amplitude']
I3 = harmonics[3]['amplitude']
I5 = harmonics[5]['amplitude']
print(f"Fundamental amplitude I₁ = {I1:.1f} Pa")
print(f"Third harmonic I₃ = {I3:.1f} Pa")
print(f"Nonlinearity ratio I₃/I₁ = {I3/I1:.4f}")
Window Functions:
Window |
Use Case |
Trade-off |
|---|---|---|
|
General-purpose (default) |
Balanced resolution/leakage |
|
Minimize spectral leakage |
Wider main lobe (lower resolution) |
|
Fast decay, simple |
Moderate leakage |
|
Perfect periodic data |
High leakage if not periodic |
Note
Data Requirements: FFT requires integer number of cycles for accurate harmonic extraction. For LAOS, use at least 5-10 cycles in steady state. Discard initial transient cycles to avoid spectral artifacts.
2. Mastercurve (Time-Temperature Superposition)¶
Extending Frequency Range via Temperature Sweeps
Theory¶
Time-Temperature Superposition (TTS) exploits the separability of temperature effects in thermorheologically simple materials. All relaxation processes accelerate uniformly with temperature, allowing horizontal shifting of frequency sweeps to create a mastercurve:
Williams-Landel-Ferry (WLF) Equation (for polymers near \(T_g\)):
Universal constants (\(T_{\text{ref}} = T_g\)): \(C_1 = 17.44\), \(C_2 = 51.6\) K
Arrhenius Equation (for high-temperature melts, \(T \gg T_g\)):
where \(E_a\) is activation energy (J/mol), \(R = 8.314\) J/(mol·K).
Applicability: - ✓ Amorphous polymers, polymer melts, viscoelastic liquids - ✗ Crystalline polymers, phase-separated blends, chemical reactions during heating
Usage¶
from rheojax.transforms import Mastercurve
from rheojax.core.data import RheoData
# Multi-temperature frequency sweep data
datasets = []
temperatures = [40, 60, 80, 100, 120] # Celsius
for T in temperatures:
# Load experimental data at each temperature
omega, G_prime, G_double_prime = load_saos_data(T)
# Create RheoData with temperature metadata
data = RheoData(
x=omega,
y=G_prime + 1j*G_double_prime, # Complex modulus
test_mode='oscillation',
metadata={'temperature': T}
)
datasets.append(data)
# Automatic shift factor determination
mc = Mastercurve(
reference_temp=80.0, # Reference temperature (Celsius)
method='wlf', # or 'arrhenius'
auto_shift=True # Optimize shift factors
)
# Generate mastercurve
master_curve, shift_factors = mc.transform(datasets)
# Access WLF parameters (if fitted)
wlf_params = mc.get_wlf_parameters()
print(f"C₁ = {wlf_params['C1']:.2f}")
print(f"C₂ = {wlf_params['C2']:.1f} K")
# Plot mastercurve (now spans 8-12 decades instead of 2-3)
import matplotlib.pyplot as plt
plt.loglog(master_curve.x, master_curve.y.real, 'o-', label="G'")
plt.loglog(master_curve.x, master_curve.y.imag, 's-', label="G''")
plt.xlabel("$\\omega \\cdot a_T$ (rad/s)")
plt.ylabel("G', G'' (Pa)")
plt.legend()
plt.show()
Manual WLF Shifting:
# Use known WLF parameters instead of auto-fitting
mc = Mastercurve(
reference_temp=373.15, # 100°C in Kelvin
method='wlf',
C1=17.44, # Universal value
C2=51.6, # Universal value
auto_shift=False
)
master_curve, shift_factors = mc.transform(datasets)
Warning
Temperature Units: reference_temp expects Celsius if metadata temperatures are in Celsius,
or Kelvin if metadata uses Kelvin. Be consistent to avoid incorrect shift factors.
3. SRFS (Strain-Rate Frequency Superposition)¶
Flow Curve Mastercurves via Shear Rate Shifting
Theory¶
Strain-Rate Frequency Superposition (SRFS) is the flow curve analog of Time-Temperature Superposition. Instead of shifting frequency data at multiple temperatures, SRFS shifts flow curves at multiple shear rates to reveal universal scaling behavior.
Connection to Soft Glassy Rheology (SGR):
For SGR materials, the shift factor follows a power-law:
where \(x\) is the effective noise temperature (SGR parameter): - \(x < 1\): Glass (aging, yield stress) - \(1 < x < 2\): Power-law fluid - \(x \ge 2\): Newtonian liquid
Physical Interpretation: The exponent \(2 - x\) reveals the material’s proximity to the glass transition. Materials near \(x = 1\) exhibit strong shear thinning and SRFS collapse.
Shear Banding Detection: SRFS automatically detects coexistence plateaus in flow curves — signatures of spatial heterogeneity (banding) where two flow states coexist at the same stress.
Usage¶
from rheojax.transforms import SRFS, detect_shear_banding
from rheojax.core.data import RheoData
# Multi-rate flow curve data (e.g., yield stress fluid)
datasets = []
shear_rates = [0.1, 1.0, 10.0, 100.0] # s^-1
for gamma_dot in shear_rates:
# Load steady-shear data at each rate
stress = measure_flow_curve(gamma_dot)
data = RheoData(
x=np.array([gamma_dot]), # Single point per rate
y=stress,
test_mode='flow_curve',
metadata={'shear_rate': gamma_dot}
)
datasets.append(data)
# SRFS mastercurve with auto shift
srfs = SRFS(reference_gamma_dot=1.0, auto_shift=True)
master_flow, shift_factors = srfs.transform(datasets)
# Extract SGR noise temperature from power-law fit
exponent = srfs.get_shift_exponent() # 2 - x
x_sgr = 2 - exponent
print(f"SGR noise temperature x = {x_sgr:.3f}")
if x_sgr < 1.0:
print("Material is a glass (aging, yield stress)")
elif x_sgr < 2.0:
print("Material is a power-law fluid")
else:
print("Material is Newtonian")
# Detect shear banding (plateau in stress vs gamma_dot)
banding_info = detect_shear_banding(datasets, threshold=0.05)
if banding_info['is_banding']:
print(f"Shear banding detected!")
print(f"Coexistence region: {banding_info['plateau_range']}")
Shear Banding Analysis:
from rheojax.transforms import compute_shear_band_coexistence
# Full steady-shear data (stress vs gamma_dot)
gamma_dot = np.logspace(-2, 2, 100)
stress = measure_full_flow_curve(gamma_dot)
# Detect plateau (constant stress over range of gamma_dot)
coexist = compute_shear_band_coexistence(
gamma_dot, stress,
threshold=0.05 # 5% stress variation defines plateau
)
if coexist['is_coexistence']:
print(f"Coexistence stress: {coexist['plateau_stress']:.1f} Pa")
print(f"Rate range: {coexist['rate_range']}")
Note
Shear Banding occurs in wormlike micelles, colloidal glasses, and other soft materials under shear. The flow curve develops a stress plateau where high-shear and low-shear bands coexist in the gap, violating the monotonic stress–rate relationship.
4. SPP Decomposer (Sequence of Physical Processes)¶
Time-Domain LAOS Analysis Without Fourier Limitations
Theory¶
The Sequence of Physical Processes (SPP) framework (Rogers et al., 2011) analyzes Large Amplitude Oscillatory Shear (LAOS) data directly in the time domain, extracting transient moduli and yield stresses without harmonic decomposition.
Key Innovation: SPP views the stress waveform as representing a sequence of physical events within each cycle: 1. Elastic extension (cage deformation): \(G_{\text{cage}}\) 2. Yielding (cage breaking): \(\sigma_{sy}\) (static), \(\sigma_{dy}\) (dynamic) 3. Flow (viscous dissipation): \(\eta'(t)\) 4. Reformation (cage rebuilding)
Transient Moduli:
These time-dependent moduli vary within each cycle, revealing when the material transitions from elastic to viscous behavior.
Extracted Parameters: - \(G_{\text{cage}}\): Maximum elastic modulus (cage stiffness) - \(\sigma_{sy}\): Static yield stress (onset of flow from rest) - \(\sigma_{dy}\): Dynamic yield stress (flow cessation during oscillation) - \(\eta_{\text{min}}\): Minimum viscosity (maximum fluidization)
Usage¶
from rheojax.transforms import SPPDecomposer, spp_analyze
from rheojax.core.data import RheoData
import numpy as np
# LAOS experimental data (10 cycles in steady state)
t = np.linspace(0, 10*2*np.pi, 2000)
omega = 1.0 # rad/s
gamma_0 = 1.0 # Large strain amplitude
# Measured stress (nonlinear waveform)
stress = load_laos_stress(t)
strain = gamma_0 * np.sin(omega*t)
strain_rate = gamma_0 * omega * np.cos(omega*t)
# Create RheoData with full LAOS information
data = RheoData(
x=t,
y=stress,
test_mode='oscillation',
metadata={
'strain': strain,
'strain_rate': strain_rate,
'omega': omega,
'gamma_0': gamma_0
}
)
# SPP decomposition with Rogers-parity defaults
decomposer = SPPDecomposer(
omega=omega,
gamma_0=gamma_0,
n_harmonics=39, # Match SPPplus MATLAB (odd harmonics)
step_size=8, # 8-point 4th-order stencil
num_mode=2 # Periodic differentiation
)
result = decomposer.transform(data)
# Extract physical parameters
spp_results = decomposer.get_results()
print(f"Cage modulus: {spp_results['G_cage']:.1f} Pa")
print(f"Static yield stress: {spp_results['sigma_sy']:.1f} Pa")
print(f"Dynamic yield stress: {spp_results['sigma_dy']:.1f} Pa")
print(f"Minimum viscosity: {spp_results['eta_min']:.2f} Pa·s")
# Plot transient moduli within a single cycle
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(8, 8))
# Elastic modulus vs strain (Lissajous-Bowditch curve)
ax1.plot(result['strain'], result['G_prime_t'], 'b-')
ax1.axhline(spp_results['G_cage'], color='r', linestyle='--',
label=f"$G_{{cage}}$ = {spp_results['G_cage']:.0f} Pa")
ax1.set_xlabel("Strain γ")
ax1.set_ylabel("G'(t) (Pa)")
ax1.legend()
# Viscosity vs strain rate
ax2.plot(result['strain_rate'], result['eta_prime_t'], 'g-')
ax2.axhline(spp_results['eta_min'], color='r', linestyle='--',
label=f"$\\eta_{{min}}$ = {spp_results['eta_min']:.2f} Pa·s")
ax2.set_xlabel("Strain rate γ̇ (s⁻¹)")
ax2.set_ylabel("η'(t) (Pa·s)")
ax2.legend()
plt.tight_layout()
plt.show()
Convenience Function for Quick Analysis:
# All-in-one SPP analysis with automatic plotting
from rheojax.transforms import spp_analyze
results = spp_analyze(
time=t,
stress=stress,
strain=strain,
omega=omega,
gamma_0=gamma_0,
plot=True # Auto-generate standard SPP plots
)
SPP vs Fourier: When to Use Each
Use SPP when: - Material has yield stress (foam, paste, gel) - You need transient moduli \(G'(t)\), \(\eta'(t)\) within cycle - Physical interpretation is priority (cage, yield, flow)
Use FFT when: - Broadband frequency characterization needed - Comparing to literature (harmonic ratios \(I_3/I_1\)) - Material is thermorheologically simple
5. Smooth Derivative¶
Noise-Robust Differentiation of Experimental Data
Theory¶
Numerical differentiation amplifies noise. For noisy rheological data (common in stress relaxation,
creep, startup), direct differentiation via np.gradient() produces unusable derivatives.
Solution: Apply smoothing before differentiation using: 1. Savitzky-Golay filter: Polynomial least-squares fit in sliding window 2. Spline interpolation: Smooth cubic/quintic spline, then differentiate analytically
Savitzky-Golay: Preserves peak shapes better than moving average. Order and window size control smoothness vs accuracy trade-off.
Usage¶
from rheojax.transforms import SmoothDerivative
import numpy as np
# Noisy relaxation modulus data
t = np.linspace(0.01, 100, 500)
G_t = 1000 * np.exp(-t/10.0) + 5*np.random.randn(500) # Signal + noise
# Direct differentiation (bad - amplifies noise)
dG_dt_noisy = np.gradient(G_t, t)
# Smooth differentiation with Savitzky-Golay
sd_savgol = SmoothDerivative(
method='savgol',
window=11, # Odd integer (larger = smoother, less responsive)
order=3 # Polynomial order (2-5 typical)
)
dG_dt_smooth = sd_savgol.transform(t, G_t)
# Alternative: Spline method for very noisy data
sd_spline = SmoothDerivative(
method='spline',
smoothing=0.1 # Smoothing parameter (0 = interpolate, >0 = smooth)
)
dG_dt_spline = sd_spline.transform(t, G_t)
# Plot comparison
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 5))
plt.plot(t, dG_dt_noisy, 'r-', alpha=0.3, label='Direct (noisy)')
plt.plot(t, dG_dt_smooth, 'b-', linewidth=2, label='Savitzky-Golay')
plt.plot(t, dG_dt_spline, 'g--', label='Spline')
plt.xlabel('Time (s)')
plt.ylabel('dG/dt (Pa/s)')
plt.legend()
plt.show()
Parameter Selection:
Parameter |
Typical Range |
Effect |
|---|---|---|
|
5-21 (odd) |
Larger means smoother, less detail |
|
2-5 |
Higher preserves peaks |
|
‘savgol’, ‘spline’ |
savgol for peaks, spline for trends |
# Application: Power-law exponent from flow curve
gamma_dot = np.logspace(-2, 2, 100)
stress = 10 * gamma_dot**0.5 # Power-law fluid: σ = K·γ̇^n
# Differentiate log-log to find n
sd = SmoothDerivative(method='savgol', window=11, order=3)
d_log_stress = sd.transform(np.log10(gamma_dot), np.log10(stress))
n_avg = np.mean(d_log_stress) # Should be ≈ 0.5
print(f"Power-law exponent n = {n_avg:.3f}")
6. Mutation Number¶
Quantifying Time-Dependence in Viscoelastic Materials
Theory¶
The Mutation Number (Rogers & Vlassopoulos, 2010) is a dimensionless parameter quantifying how much a material “mutates” (changes structure) during a rheological measurement.
Definition:
For oscillatory tests:
Physical Interpretation: - \(\Delta \to 0\): Material remains elastic (solid-like) - \(\Delta \to 1\): Material fully relaxes (liquid-like) - \(\Delta \approx 0.1\): Transition regime (critical for accurate measurements)
Practical Use: Mutation number assesses measurement validity. If \(\Delta > 0.1\), the material structure evolves significantly during the test, potentially invalidating linear viscoelastic assumptions.
Usage¶
from rheojax.transforms import MutationNumber
from rheojax.core.data import RheoData
import numpy as np
# Relaxation modulus data
t = np.linspace(0.01, 100, 500)
G_t = 1000 * np.exp(-t/10.0)
data = RheoData(x=t, y=G_t, test_mode='relaxation')
# Calculate mutation number
mutation = MutationNumber(integration_method='trapz')
delta = mutation.transform(data)
print(f"Mutation number Δ = {delta:.4f}")
if delta < 0.01:
print("Material is essentially elastic (no structural change)")
elif delta < 0.1:
print("Material shows time-dependence but measurement is valid")
else:
print("WARNING: Significant structural evolution during measurement!")
Application: Assessing Linear Regime:
# Oscillatory strain sweep (amplitude sweep)
strains = np.logspace(-3, 0, 20) # 0.1% to 100%
omega = 1.0
mutation_numbers = []
for gamma_0 in strains:
# Measure at each strain amplitude
G_prime, G_double_prime = measure_saos(omega, gamma_0)
# Calculate mutation number (simplified for SAOS)
delta_osc = (gamma_0 * omega) / (2 * np.pi) # Rough estimate
mutation_numbers.append(delta_osc)
# Find linear regime (Δ < 0.1)
linear_strains = strains[np.array(mutation_numbers) < 0.1]
print(f"Linear regime: γ₀ < {linear_strains[-1]:.3f}")
Note
Mutation number is particularly useful for soft glassy materials (foams, gels, pastes) that age or evolve during measurement. For Newtonian liquids, \(\Delta \approx 1\) always.
7. OWChirp (Optimally Windowed Chirp)¶
Rapid Frequency Sweeps via Chirp Oscillation
Theory¶
Optimally Windowed Chirp (OWChirp) processes frequency-swept oscillatory data where the oscillation frequency varies continuously (chirp signal) rather than stepping discretely.
Advantages over discrete frequency sweeps: - 10-100× faster: Single chirp vs 20-50 discrete frequencies - Better time resolution: Captures transient behavior - Lower sample requirement: One loading vs multiple frequency steps
Challenge: Extracting frequency-dependent moduli \(G'(\omega)\), \(G''(\omega)\) from time-domain chirp data requires specialized time-frequency analysis (short-time Fourier transform, wavelets).
Mathematical Formulation:
where \(f(t)\) is the instantaneous frequency (Hz) varying logarithmically from \(f_{\text{start}}\) to \(f_{\text{end}}\) over time \(T\).
Usage¶
from rheojax.transforms import OWChirp
from rheojax.core.data import RheoData
import numpy as np
# Chirp oscillatory data
T_total = 300.0 # 5 minutes
t = np.linspace(0, T_total, 10000)
# Frequency sweep: 0.01 Hz to 100 Hz
f_start = 0.01 # Hz
f_end = 100.0 # Hz
# Generate chirp strain (logarithmic sweep)
phase = 2 * np.pi * f_start * T_total / np.log(f_end/f_start) * \
(np.exp(t * np.log(f_end/f_start) / T_total) - 1)
strain = 0.01 * np.sin(phase) # 1% strain amplitude
# Measured stress (complex response)
stress = load_chirp_stress(t)
# OWChirp analysis
owc = OWChirp(
f_start=f_start,
f_end=f_end,
window='hann',
n_windows=50 # Frequency resolution
)
data = RheoData(
x=t,
y=stress,
test_mode='oscillation',
metadata={'strain': strain}
)
result = owc.transform(data)
# Extract moduli vs frequency
freq = result['frequency'] # Hz
G_prime = result['G_prime']
G_double_prime = result['G_double_prime']
# Plot frequency-dependent moduli
import matplotlib.pyplot as plt
plt.loglog(freq, G_prime, 'o-', label="G'")
plt.loglog(freq, G_double_prime, 's-', label="G''")
plt.xlabel("Frequency (Hz)")
plt.ylabel("G', G'' (Pa)")
plt.legend()
plt.show()
When to Use OWChirp:
Criterion |
Discrete Sweep |
Chirp (OWChirp) |
|---|---|---|
Speed |
10-60 minutes |
1-5 minutes |
Sample Requirement |
High (long test) |
Low (short test) |
Transient Materials |
Poor (ages during test) |
Good (fast) |
Frequency Resolution |
Excellent (arbitrary points) |
Moderate (window size) |
Data Processing |
Simple (direct) |
Complex (requires OWChirp) |
Warning
Nonlinearity: Chirp data analysis assumes linear viscoelasticity (SAOS regime). For LAOS, use discrete frequency sweeps with FFT/SPP instead.
Combining Transforms¶
Multi-Transform Workflows for Comprehensive Analysis
Example 1: Polymer Mastercurve with Quality Control¶
from rheojax.transforms import Mastercurve, MutationNumber
from rheojax.core.data import RheoData
# Load multi-temperature SAOS data
datasets = load_multi_temp_saos([40, 60, 80, 100, 120])
# Step 1: Check linearity at each temperature
for i, data in enumerate(datasets):
mutation = MutationNumber()
delta = mutation.transform(data)
print(f"T = {data.metadata['temperature']}°C: Δ = {delta:.4f}")
if delta > 0.1:
print(f"WARNING: Nonlinear behavior at T = {data.metadata['temperature']}°C")
# Step 2: Generate mastercurve (TTS)
mc = Mastercurve(reference_temp=80.0, method='wlf', auto_shift=True)
master_curve, shift_factors = mc.transform(datasets)
# Step 3: Fit model to mastercurve (not shown)
# model.fit(master_curve.x, master_curve.y, test_mode='oscillation')
Example 2: LAOS Characterization with Yield Stress¶
from rheojax.transforms import FFTAnalysis, SPPDecomposer
# LAOS time-domain data
t, stress, strain = load_laos_data()
omega = 1.0
gamma_0 = 1.0
# Step 1: FFT for nonlinearity quantification
fft = FFTAnalysis(window='hann', detrend=True)
data = RheoData(x=t, y=stress, test_mode='oscillation')
fft_result = fft.transform(data)
harmonics = fft_result['harmonics']
I3_I1 = harmonics[3]['amplitude'] / harmonics[1]['amplitude']
print(f"Nonlinearity ratio I₃/I₁ = {I3_I1:.4f}")
# Step 2: SPP for physical parameters
decomposer = SPPDecomposer(omega=omega, gamma_0=gamma_0)
decomposer_result = decomposer.transform(data)
spp_results = decomposer.get_results()
print(f"Static yield stress: {spp_results['sigma_sy']:.1f} Pa")
print(f"Cage modulus: {spp_results['G_cage']:.1f} Pa")
Example 3: SGR Material Classification via SRFS¶
from rheojax.transforms import SRFS, detect_shear_banding
from rheojax.models import SGRConventional
# Multi-rate flow curves
datasets = load_multi_rate_flow([0.01, 0.1, 1.0, 10.0, 100.0])
# Step 1: SRFS mastercurve
srfs = SRFS(reference_gamma_dot=1.0, auto_shift=True)
master_flow, shifts = srfs.transform(datasets)
# Extract SGR noise temperature from shift exponent
exponent = srfs.get_shift_exponent()
x_sgr = 2 - exponent
print(f"SGR noise temperature x = {x_sgr:.3f}")
# Step 2: Fit SGR model
model = SGRConventional()
model.fit(master_flow.x, master_flow.y, test_mode='flow_curve')
x_fitted = model.parameters.get_value('x')
print(f"SGR fitted x = {x_fitted:.3f} (compare to SRFS x = {x_sgr:.3f})")
# Step 3: Check for shear banding
banding = detect_shear_banding(datasets, threshold=0.05)
if banding['is_banding']:
print("Material shows shear banding (heterogeneous flow)")
Transform Limitations¶
Understanding When Transforms Fail
General Limitations¶
Garbage In, Garbage Out: All transforms require clean, properly calibrated experimental data. Pre-process data to remove:
Instrument artifacts (inertia, compliance)
Temperature drift
Sample edge effects
Initial transients (use steady-state data only)
Sampling Requirements: Adequate data density needed:
FFT: Nyquist criterion (sample rate \(\ge 2 \times\) highest frequency)
TTS/SRFS: At least 3-5 temperatures/rates for robust shifting
SPP: 5-10 steady-state cycles minimum
Linearity Assumptions: Several transforms assume linear viscoelasticity:
Mastercurve (TTS): Valid only in SAOS regime
Mutation Number: Assumes no permanent structural change
OWChirp: Small-amplitude assumption
Transform-Specific Issues¶
Transform |
Common Failure Mode |
Solution |
|---|---|---|
FFT |
Spectral leakage (non-integer cycles) |
Use windowing + integer cycles |
Mastercurve |
Non-thermorheologically simple material |
Check WLF residuals, test alternative models |
SRFS |
Thixotropic transients |
Ensure steady state before measurement |
SPP |
Noisy strain rate differentiation |
Increase smoothing (step_size) |
Smooth Derivative |
Over-smoothing (loses features) |
Reduce window size or smoothing parameter |
Mutation Number |
Short observation time |
Extrapolate to infinite time |
OWChirp |
Nonlinear response |
Use smaller strain amplitude |
FFT Artifacts:
# BAD: Non-integer cycles cause spectral leakage
t = np.linspace(0, 9.5*2*np.pi, 1000) # 9.5 cycles
# GOOD: Exactly 10 cycles
t = np.linspace(0, 10*2*np.pi, 1000) # 10 cycles
Mastercurve Validation:
# Check if material is thermorheologically simple
mc = Mastercurve(reference_temp=80.0, auto_shift=True)
master_curve, shift_factors = mc.transform(datasets)
# Compute shift quality metric (residuals)
quality = mc.get_shift_quality()
if quality['mean_squared_error'] > 0.1:
print("WARNING: Poor mastercurve quality. Material may not be thermorheologically simple.")
Troubleshooting Guide¶
Issue: FFT shows unexpected harmonics
Check for integer number of cycles
Verify steady-state (discard initial cycles)
Apply appropriate window function (try ‘blackman’)
Issue: Mastercurve has discontinuities
Ensure consistent units (Pa vs kPa)
Verify temperature metadata is correct
Check for phase transitions (crystallization)
Try manual WLF parameters
Issue: SPP yields unrealistic values (G_cage < 0)
Increase smoothing (step_size=16 instead of 8)
Check strain rate data quality
Verify steady-state oscillation
Issue: SRFS shift factors don’t follow power law
Material may not be SGR-type
Check for thixotropic effects (aging)
Verify truly steady-state measurements
References¶
Foundational Papers:
FFT & LAOS: Hyun et al. (2002). “Large amplitude oscillatory shear as a way to classify the complex fluids.” J. Non-Newt. Fluid Mech. 107, 51-65. https://doi.org/10.1016/S0377-0257(02)00141-6
Time-Temperature Superposition: Ferry, J.D. (1980). Viscoelastic Properties of Polymers, 3rd ed. Wiley. ISBN: 978-0-471-04894-7
SPP Framework: Rogers, S.A. et al. (2011). “Sequence of physical processes determined by the medium amplitude oscillatory shear.” J. Rheol. 55, 435-458. https://doi.org/10.1122/1.3544591
Mutation Number: Rogers, S.A. & Vlassopoulos, D. (2010). “Frieze group analysis of asymmetric response to large amplitude oscillatory shear.” J. Rheol. 54, 859-880. https://doi.org/10.1122/1.3445064
SRFS & SGR: Divoux, T., Barentin, C. & Manneville, S. (2011). “Stress overshoot in a simple yield stress fluid: An extensive study combining rheology and velocimetry.” Soft Matter, 7, 9335-9349. https://doi.org/10.1039/C1SM05740E
OWChirp: Ghiringhelli, E., Roux, D., Bleses, D., Galliard, H. & Caton, F. (2012). “Optimal Fourier rheometry: Application to the gelation of an alginate.” Rheologica Acta, 51, 413-420. https://doi.org/10.1007/s00397-012-0616-z
Soft Glassy Rheology: Sollich, P. et al. (1997). “Rheology of soft glassy materials.” Phys. Rev. Lett. 78, 2020-2023. https://doi.org/10.1103/PhysRevLett.78.2020
See Also¶
Related Documentation:
Time-Temperature Superposition (TTS) — In-depth TTS guide with WLF/Arrhenius theory
Soft Glassy Rheology (SGR) Analysis — Soft Glassy Rheology framework and SRFS applications
Sequence of Physical Processes (SPP) — Detailed SPP tutorial with physical interpretation
Model Selection Guide — Choosing models for transformed data
Visualization Guide — Plotting transform results
Transform API Reference:
../../api_reference/transforms/fft_analysis — FFTAnalysis class
../../api_reference/transforms/mastercurve — Mastercurve class
../../api_reference/transforms/srfs — SRFS class
../../api_reference/transforms/spp_decomposer — SPPDecomposer class
../../api_reference/transforms/smooth_derivative — SmoothDerivative class
../../api_reference/transforms/mutation_number — MutationNumber class
../../api_reference/transforms/owchirp — OWChirp class
Example Notebooks:
examples/advanced/02-multi-technique-fitting.ipynb— Combining transforms with fittingexamples/advanced/09-sgr-soft-glassy-rheology.ipynb— SRFS + SGR workflowexamples/advanced/10-spp-laos-tutorial.ipynb— SPP detailed examplesexamples/basic/01-maxwell-fitting.ipynb— Simple transform usage
Next Steps¶
After mastering data transforms, you can:
Apply transforms in production workflows — Pipeline API Tutorial
Fit models to transformed data — Fitting Strategies and Troubleshooting
Perform Bayesian inference — Bayesian Inference
Analyze soft glassy materials — Soft Glassy Rheology (SGR) Analysis
Extract yield stresses — Sequence of Physical Processes (SPP)
Practical Exercise:
Try the complete workflow:
from rheojax.transforms import Mastercurve, MutationNumber
from rheojax.models import FractionalMaxwellLiquid
from rheojax.pipeline import Pipeline
# Load multi-temperature data
pipeline = Pipeline()
pipeline.load_multi_temperature('data_*.csv', temps=[40, 60, 80, 100])
# Quality check
for data in pipeline.datasets:
mu = MutationNumber().transform(data)
print(f"T = {data.metadata['temperature']}°C: Δ = {mu:.4f}")
# Generate mastercurve
mc = Mastercurve(reference_temp=80.0, method='wlf', auto_shift=True)
master, shifts = mc.transform(pipeline.datasets)
# Fit fractional model
model = FractionalMaxwellLiquid()
model.fit(master.x, master.y, test_mode='oscillation')
# Bayesian inference
result = model.fit_bayesian(master.x, master.y, num_samples=2000)
print("Analysis complete!")