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?

  1. Extend measurement ranges: TTS/SRFS create mastercurves spanning 8-12 decades

  2. Extract hidden physics: SPP reveals yield stresses and cage moduli from LAOS waveforms

  3. Quantify nonlinearity: FFT harmonics and Mutation Number measure departure from linearity

  4. Improve signal quality: Smooth Derivative handles noisy experimental data

  5. Accelerate measurements: OWChirp processes chirp data 10-100× faster than discrete sweeps

When to Use Transforms

Transform Decision Guide

I Have…

I Want…

Use This Transform

LAOS time-domain data

Harmonic amplitudes \(I_1, I_3, I_5, \ldots\)

FFTAnalysis

Freq sweeps at multiple T

Single master curve

Mastercurve (TTS)

Flow curves at multiple \(\dot{\gamma}\)

Flow curve master + noise temp

SRFS

LAOS waveform data

Yield stress, cage modulus

SPPDecomposer

Noisy experimental data

Smooth derivative \(dG/dt\)

SmoothDerivative

Any rheological data

Linearity check (\(\Delta\))

MutationNumber

Chirp oscillatory data

\(G'(\omega)\), \(G''(\omega)\) from single test

OWChirp

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:

\[\sigma(t) = \sum_{n=1,3,5,...}^{\infty} \left[ \sigma'_n \sin(n\omega t) + \sigma''_n \cos(n\omega t) \right]\]

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:

\[ \begin{align}\begin{aligned}\text{Nonlinearity ratio} = \frac{I_3}{I_1} = \frac{|\sigma_3|}{|\sigma_1|}\\\text{Total harmonic distortion} = \frac{\sqrt{\sum_{n=3,5,...} I_n^2}}{I_1}\end{aligned}\end{align} \]

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:

FFT Window Selection

Window

Use Case

Trade-off

'hann'

General-purpose (default)

Balanced resolution/leakage

'blackman'

Minimize spectral leakage

Wider main lobe (lower resolution)

'bartlett'

Fast decay, simple

Moderate leakage

'none'

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:

\[ \begin{align}\begin{aligned}G'(\omega, T) = G'(\omega \cdot a_T, T_{\text{ref}})\\G''(\omega, T) = G''(\omega \cdot a_T, T_{\text{ref}})\end{aligned}\end{align} \]

Williams-Landel-Ferry (WLF) Equation (for polymers near \(T_g\)):

\[\log_{10}(a_T) = \frac{-C_1 (T - T_{\text{ref}})}{C_2 + (T - T_{\text{ref}})}\]

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\)):

\[\log(a_T) = \frac{E_a}{R} \left( \frac{1}{T} - \frac{1}{T_{\text{ref}}} \right)\]

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:

\[a(\dot{\gamma}) = \left( \frac{\dot{\gamma}}{\dot{\gamma}_{\text{ref}}} \right)^{2-x}\]

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:

\[ \begin{align}\begin{aligned}G'_t(t) = \frac{\partial \sigma(t)}{\partial \gamma(t)}\\\eta'_t(t) = \frac{\partial \sigma(t)}{\partial \dot{\gamma}(t)}\end{aligned}\end{align} \]

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:

Savitzky-Golay Parameter Guide

Parameter

Typical Range

Effect

window

5-21 (odd)

Larger means smoother, less detail

order

2-5

Higher preserves peaks

method

‘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:

\[\Delta = \frac{\int_0^{t_{\text{obs}}} G(t) \, dt}{G(0) \cdot t_{\text{obs}}}\]

For oscillatory tests:

\[\Delta_{\text{osc}} = \frac{1}{\omega \cdot \gamma_0} \int_0^{2\pi/\omega} |\dot{\gamma}(t)| \, dt\]

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:

\[ \begin{align}\begin{aligned}\gamma(t) = \gamma_0 \sin\left( 2\pi \int_0^t f(\tau) \, d\tau \right)\\f(t) = f_{\text{start}} \left( \frac{f_{\text{end}}}{f_{\text{start}}} \right)^{t/T}\end{aligned}\end{align} \]

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:

Chirp vs Discrete Frequency Sweep

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

  1. 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)

  2. 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

  3. 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

Common Transform Pitfalls

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:

  1. 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

  2. Time-Temperature Superposition: Ferry, J.D. (1980). Viscoelastic Properties of Polymers, 3rd ed. Wiley. ISBN: 978-0-471-04894-7

  3. 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

  4. 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

  5. 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

  6. 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

  7. 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:

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 fitting

  • examples/advanced/09-sgr-soft-glassy-rheology.ipynb — SRFS + SGR workflow

  • examples/advanced/10-spp-laos-tutorial.ipynb — SPP detailed examples

  • examples/basic/01-maxwell-fitting.ipynb — Simple transform usage

Next Steps

After mastering data transforms, you can:

  1. Apply transforms in production workflowsPipeline API Tutorial

  2. Fit models to transformed dataFitting Strategies and Troubleshooting

  3. Perform Bayesian inferenceBayesian Inference

  4. Analyze soft glassy materialsSoft Glassy Rheology (SGR) Analysis

  5. Extract yield stressesSequence 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!")