SPP Analysis API¶
The Sequence of Physical Processes (SPP) tooling in RheoJAX provides time-domain LAOS analysis without relying on Fourier/Chebyshev expansions. It is built from three primary objects:
rheojax.transforms.spp_decomposer.SPPDecomposer— per-cycle LAOS decomposition into cage modulus, static/dynamic yield stresses, power-law flow, and nonlinearity metrics.rheojax.models.spp.spp_yield_stress.SPPYieldStress— a fit-ready model (NLSQ and NumPyro NUTS) that parameterizes SPP yield behavior across amplitudes or steady shear.rheojax.pipeline.workflows.SPPAmplitudeSweepPipeline— convenience pipeline for amplitude-sweep LAOS workflows.
For conceptual background see Sequence of Physical Processes (SPP).
SPPDecomposer¶
- class rheojax.transforms.spp_decomposer.SPPDecomposer(omega, gamma_0, n_harmonics=39, yield_tolerance=0.02, start_cycle=0, end_cycle=None, use_numerical_method=False, step_size=8, num_mode=2, wrap_strain_rate=True)[source]
Bases:
BaseTransformSPP decomposition transform for LAOS stress analysis.
Applies the Sequence of Physical Processes (SPP) framework to decompose LAOS stress signals and extract nonlinear viscoelastic parameters.
The transform requires oscillatory shear data with known frequency and strain amplitude. It computes:
Elastic/viscous stress decomposition
Yield stress extraction (static and dynamic)
Power-law flow parameters
Lissajous-Bowditch metrics
Harmonic decomposition
- Parameters:
omega (
float) – Angular frequency ω (rad/s) of the oscillationgamma_0 (
float) – Strain amplitude γ_0 (dimensionless)n_harmonics (
int) – Number of odd harmonics to extract for stress (default: 39 per MATLAB SPPplus)yield_tolerance (
float) – Fractional tolerance for yield point detection (default: 0.02)start_cycle (
int) – First cycle to analyze (0-indexed). Use to skip startup transients. Default: 0 (start from beginning).end_cycle (
int|None) – Last cycle to analyze (exclusive). None means use all available cycles. Default: None.use_numerical_method (
bool) – If True, use MATLAB-compatible numerical differentiation for raw data. If False (default), use Fourier-based decomposition.step_size (
int) – Step size k for numerical differentiation (only used if use_numerical_method=True). Default: 8 to mirror SPPplus v2.1.num_mode (
int) – Numerical differentiation mode (1 = edge-aware, 2 = periodic/looped), matching SPPplus num_mode. Only used when use_numerical_method=True.
- omega
Angular frequency
- Type:
- gamma_0
Strain amplitude
- Type:
- gamma_dot_0
Strain rate amplitude (ω * γ_0)
- Type:
- n_harmonics
Number of harmonics for decomposition
- Type:
- start_cycle
First cycle to analyze
- Type:
- end_cycle
Last cycle to analyze
- Type:
int or None
- use_numerical_method
Whether using numerical differentiation
- Type:
- results_
Dictionary of computed SPP metrics (after transform)
- Type:
Examples
Basic usage with RheoData:
>>> from rheojax.core.data import RheoData >>> from rheojax.transforms.spp_decomposer import SPPDecomposer >>> >>> # LAOS stress-strain data >>> omega = 1.0 # rad/s >>> gamma_0 = 1.0 # strain amplitude >>> t = jnp.linspace(0, 2*jnp.pi, 1000) >>> strain = gamma_0 * jnp.sin(omega * t) >>> stress = 100.0 * strain + 20.0 * jnp.sin(3 * omega * t) # With 3rd harmonic >>> >>> data = RheoData( ... x=t, ... y=stress, ... domain='time', ... metadata={ ... 'test_mode': 'oscillation', ... 'omega': omega, ... 'gamma_0': gamma_0, ... 'strain': strain, ... } ... ) >>> >>> # Apply SPP decomposition >>> decomposer = SPPDecomposer(omega=omega, gamma_0=gamma_0) >>> result = decomposer.transform(data) >>> >>> # Access metrics >>> print(f"Static yield stress: {decomposer.results_['sigma_sy']:.2f} Pa") >>> print(f"Dynamic yield stress: {decomposer.results_['sigma_dy']:.2f} Pa")
Notes
Input data must be time-domain stress signal
Strain data must be available in metadata[‘strain’] or computed from ω, γ_0
Output includes both decomposed waveforms and extracted parameters
- __init__(omega, gamma_0, n_harmonics=39, yield_tolerance=0.02, start_cycle=0, end_cycle=None, use_numerical_method=False, step_size=8, num_mode=2, wrap_strain_rate=True)[source]
Initialize SPP decomposer transform.
- Parameters:
omega (
float) – Angular frequency (rad/s)gamma_0 (
float) – Strain amplitude (dimensionless)n_harmonics (
int) – Number of odd harmonics to extract (default: 39)yield_tolerance (
float) – Tolerance for yield point detection (default: 0.02)start_cycle (
int) – First cycle to analyze, 0-indexed (default: 0)end_cycle (
int|None) – Last cycle to analyze, exclusive (default: None, use all)use_numerical_method (
bool) – Use MATLAB-compatible numerical differentiation (default: False)step_size (
int) – Step size k for numerical differentiation (default: 8)num_mode (
int) – Numerical differentiation mode (1=edge-aware, 2=periodic). Default: 2.wrap_strain_rate (
bool) – If True, infer strain rate with periodic wrapping when missing (default: True)
- get_results()[source]
Get computed SPP analysis results.
- Returns:
Dictionary containing all SPP metrics: - sigma_sy: Static yield stress (Pa) - sigma_dy: Dynamic yield stress (Pa) - K: Consistency index (Pa·s^n) - n_power_law: Power-law exponent - harmonic_amplitudes: Array of harmonic amplitudes - harmonic_phases: Array of harmonic phases - I3_I1_ratio: Third harmonic nonlinearity ratio - G_L, G_M: Large and minimum strain moduli (Pa) - eta_L, eta_M: Large and minimum rate viscosities (Pa·s) - S_factor: Stiffening ratio - T_factor: Thickening ratio - G_cage: Time-resolved cage modulus (array) - sigma_elastic: Elastic stress contribution (array) - sigma_viscous: Viscous stress contribution (array)
- Return type:
- Raises:
RuntimeError – If transform has not been applied yet
Examples
>>> decomposer = SPPDecomposer(omega=1.0, gamma_0=1.0) >>> _ = decomposer.transform(data) >>> results = decomposer.get_results() >>> print(f"I3/I1 = {results['I3_I1_ratio']:.4f}")
- get_yield_stresses()[source]
Get static and dynamic yield stresses.
- Returns:
(sigma_sy, sigma_dy) in Pa
- Return type:
- Raises:
RuntimeError – If transform has not been applied yet
- get_nonlinearity_metrics()[source]
Get nonlinearity quantification metrics.
- Returns:
Dictionary with: - I3_I1_ratio: Third harmonic ratio (FT-rheology) - S_factor: Strain stiffening ratio - T_factor: Shear thickening ratio
- Return type:
- Raises:
RuntimeError – If transform has not been applied yet
SPPYieldStress Model¶
- class rheojax.models.spp.spp_yield_stress.SPPYieldStress[source]
Bases:
BaseModelSPP-based yield stress model for LAOS analysis.
This model extracts yield stress parameters from amplitude-sweep LAOS data using the SPP framework. It parameterizes the nonlinear response in terms of physically meaningful quantities: cage modulus, static/dynamic yield stresses, and post-yield flow parameters.
The model supports both OSCILLATION mode (LAOS amplitude sweeps) and ROTATION mode (steady shear flow curves) for comprehensive yield stress characterization.
- Parameters:
G_cage (float) – Apparent cage modulus (Pa), elastic stiffness of the cage structure
sigma_sy_scale (float) – Static yield stress scale factor (Pa)
sigma_sy_exp (float) – Static yield stress exponent for amplitude scaling
sigma_dy_scale (float) – Dynamic yield stress scale factor (Pa)
sigma_dy_exp (float) – Dynamic yield stress exponent for amplitude scaling
eta_inf (float) – Infinite-shear viscosity (Pa·s)
n_power_law (float) – Power-law flow index (dimensionless)
noise (float) – Observation noise scale (Pa), for Bayesian inference
Equations (Constitutive)
----------------------
γ_0) (For OSCILLATION (LAOS at amplitude) – σ_sy(γ_0) = sigma_sy_scale * γ_0^sigma_sy_exp σ_dy(γ_0) = sigma_dy_scale * γ_0^sigma_dy_exp σ_max(γ_0) = G_cage * γ_0 + η_inf * ω * γ_0
γ̇) (For ROTATION (steady shear at rate) – σ(γ̇) = σ_dy + η_inf * γ̇^n_power_law (Herschel-Bulkley-like)
Modes (Test)
----------
OSCILLATION (-)
ROTATION (-)
Examples
Fit to amplitude sweep data:
>>> from rheojax.models import SPPYieldStress >>> from rheojax.transforms import SPPDecomposer >>> >>> # Amplitude sweep data (multiple γ_0 values) >>> gamma_0_values = jnp.array([0.01, 0.1, 0.5, 1.0, 2.0, 5.0]) >>> sigma_sy_data = jnp.array([1.0, 10.0, 40.0, 80.0, 140.0, 280.0]) >>> >>> model = SPPYieldStress() >>> model.fit(gamma_0_values, sigma_sy_data, test_mode='oscillation') >>> print(model)
Bayesian inference:
>>> result = model.fit_bayesian(gamma_0_values, sigma_sy_data, ... test_mode='oscillation') >>> print(result.summary)
- __init__()[source]
Initialize SPP yield stress model.
- model_function(X, params, test_mode=None, **kwargs)[source]
Model function for predictions and Bayesian inference.
This is the NumPyro-facing model used by BayesianMixin. It maps inputs and parameter vectors to stress predictions.
- Parameters:
X (
Array) – Independent variable - OSCILLATION: strain amplitudes (gamma_0) - ROTATION: shear rates (gamma_dot)params (
Array) –- [G_cage, sigma_sy_scale, sigma_sy_exp, sigma_dy_scale,
sigma_dy_exp, eta_inf, n_power_law, noise]
test_mode (
TestModeEnum|None) – Mode selector; defaults to the model’s stored test_mode.
- Returns:
Predicted stress values.
- Return type:
- bayesian_prior_factory(name, lower, upper)[source]
Create NumPyro-friendly priors for model parameters.
Uses physically-motivated prior distributions: - LogNormal for scale parameters (positive, often log-distributed) - Beta for bounded exponents - HalfCauchy for noise scale
- predict_amplitude_sweep(gamma_0_array, omega=1.0, yield_type='static')[source]
Predict full amplitude sweep response.
Computes both static and dynamic yield stresses across amplitudes.
- Parameters:
- Returns:
Dictionary with: - gamma_0: strain amplitudes - sigma_sy: static yield stresses (if requested) - sigma_dy: dynamic yield stresses (if requested)
- Return type:
- predict_flow_curve(gamma_dot_array)[source]
Predict steady shear flow curve.
- predict_rheo(rheo_data, test_mode=None)[source]
Predict rheological response for RheoData.
- Parameters:
rheo_data (
RheoData) – Input rheological datatest_mode (
TestModeEnum|None) – Test mode override
- Returns:
Predicted response
- Return type:
SPP Amplitude Sweep Pipeline¶
- class rheojax.pipeline.workflows.SPPAmplitudeSweepPipeline(omega=1.0, n_harmonics=39, step_size=8, num_mode=2, wrap_strain_rate=True, use_numerical_method=None)[source]¶
Bases:
PipelinePipeline for SPP analysis of amplitude sweep LAOS data.
This pipeline performs SPP (Sequence of Physical Processes) analysis on amplitude sweep LAOS data to extract yield stress parameters and nonlinear viscoelastic metrics.
- Workflow:
Load amplitude sweep data (multiple γ_0 values)
Apply SPP decomposition at each amplitude
Extract yield stresses (static and dynamic)
Fit power-law scaling to yield stress vs amplitude
Optionally fit Bayesian SPPYieldStress model
- omega¶
Angular frequency of oscillation (rad/s)
- results¶
Dictionary of SPP metrics per amplitude
- model¶
Fitted SPPYieldStress model (after fit_model)
Example
>>> pipeline = SPPAmplitudeSweepPipeline(omega=1.0) >>> pipeline.run(amplitude_data_list) >>> pipeline.fit_model(bayesian=True) >>> print(pipeline.get_yield_stresses())
- __init__(omega=1.0, n_harmonics=39, step_size=8, num_mode=2, wrap_strain_rate=True, use_numerical_method=None)[source]¶
Initialize SPP amplitude sweep pipeline.
- Parameters:
omega (
float) – Angular frequency in rad/s (default: 1.0)n_harmonics (
int) – Number of harmonics for SPP decomposition (default: 39)step_size (
int) – Differentiation step size k (default: 8, Rogers parity)num_mode (
int) – Numerical differentiation mode (default: 2 periodic)wrap_strain_rate (
bool) – Whether to use wrapped differentiation when rate missinguse_numerical_method (
bool|None) – Force numerical path; None keeps default from transform
- run(stress_data, gamma_0_values=None)[source]¶
Execute SPP analysis on amplitude sweep data.
- Parameters:
- Return type:
- Returns:
self for method chaining
- Raises:
ValueError – If gamma_0_values not provided and not in metadata
- fit_model(bayesian=False, yield_type='static', **fit_kwargs)[source]¶
Fit SPPYieldStress model to extracted yield stresses.
- Parameters:
- Return type:
- Returns:
self for method chaining
Helper Functions¶
These JAX-first kernels are re-exported for completeness; see
rheojax.utils.spp_kernels for details.
JAX-compatible SPP (Sequence of Physical Processes) kernel functions.
This module provides efficient, JAX-compatible implementations of SPP analysis kernel functions for LAOS (Large Amplitude Oscillatory Shear) rheology. SPP analysis enables cycle-by-cycle decomposition of nonlinear stress responses into elastic and viscous contributions, extracting physically meaningful yield parameters.
The SPP framework was developed by Rogers (2012, 2017) and provides: - Time-resolved apparent cage modulus G’_cage(t) - Static and dynamic yield stress extraction - Phase reconstruction from harmonic decomposition - Lissajous-Bowditch plot metrics - Frenet-Serret frame analysis (T, N, B vectors) - Moduli rate calculations (Ġ’, Ġ’’, G_speed)
Key Functions¶
apparent_cage_modulus: Time-resolved elastic modulus from stress/strain
static_yield_stress: Yield stress at strain reversal (strain = ±gamma0)
dynamic_yield_stress: Yield stress at rate reversal (strain rate = 0)
harmonic_reconstruction: Stress reconstruction from Fourier components
harmonic_reconstruction_full: Full Fourier with phase alignment (MATLAB-compatible)
spp_fourier_analysis: Complete Fourier-based SPP analysis with analytical derivatives
lissajous_metrics: Bowditch diagram derived quantities (S, T ratios)
zero_crossing_detection: Robust strain/rate zero-crossing finder
frenet_serret_frame: Compute T, N, B trajectory vectors
moduli_rates: Compute Ġ’(t), Ġ’’(t), G_speed, δ̇(t)
yield_from_displacement_stress: SPP-based yield stress extraction
Physical Interpretation¶
SPP analysis provides a phenomenological interpretation of LAOS behavior: - G’_cage(t): Instantaneous elastic modulus reflecting cage structure - static_yield: Static yield stress (stress at max strain, cage breakage threshold) - dynamic_yield: Dynamic yield stress (stress at zero rate, flow cessation threshold) - Power-law regime: Post-yield flow characterized by sigma ~ strain_rate^n - Frenet-Serret frame: Geometric analysis of (γ, γ̇/ω, σ) trajectory
References
S.A. Rogers et al., “A sequence of physical processes determined and quantified in large-amplitude oscillatory shear (LAOS): Application to theoretical nonlinear models”, J. Rheol. 56(1), 2012
S.A. Rogers, “In search of physical meaning: defining transient parameters for nonlinear viscoelasticity”, Rheol. Acta 56, 2017
G.J. Donley et al., “Time-resolved dynamics of the yielding transition in soft materials”, J. Non-Newton. Fluid Mech. 264, 2019
- rheojax.utils.spp_kernels.apparent_cage_modulus(stress, strain, strain_amplitude)[source]¶
Compute time-resolved apparent cage modulus.
Apparent cage modulus is the instantaneous elastic response, normalized by strain amplitude: G_cage(t) = stress(t) / gamma0 * sign(strain(t)).
Reference: Rogers et al. (2012) J. Rheol. 56(1). Eq. (1): G’_cage(t) = sigma(t) / gamma_0 * sign(gamma(t))
- Parameters:
- Returns:
Apparent cage modulus (Pa)
- Return type:
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import apparent_cage_modulus >>> >>> # Sinusoidal LAOS data >>> t = jnp.linspace(0, 2*jnp.pi, 1000) >>> gamma_0 = 1.0 >>> gamma = gamma_0 * jnp.sin(t) >>> sigma = 100.0 * jnp.sin(t) + 10.0 * jnp.sin(3*t) # With 3rd harmonic >>> >>> G_cage = apparent_cage_modulus(sigma, gamma, gamma_0)
Notes
G_cage is constant for a purely linear sinusoidal material
Deviations from constant indicate nonlinearity
Sign(γ) ensures correct sign during negative strain half-cycle
- rheojax.utils.spp_kernels.static_yield_stress(stress, strain, strain_amplitude, tolerance=0.02)[source]¶
Approximate static yield stress near strain reversal.
Samples near the strain extrema (abs(strain) close to strain_amplitude) and returns the average absolute stress.
- Return type:
- rheojax.utils.spp_kernels.dynamic_yield_stress(stress, strain_rate, rate_amplitude, tolerance=0.02)[source]¶
Approximate dynamic yield stress near zero strain rate.
Selects samples where abs(strain_rate) is small, averages abs(stress), and returns that average as the dynamic yield estimate.
- Return type:
- rheojax.utils.spp_kernels.harmonic_reconstruction(stress, omega, n_harmonics=39, dt=None)[source]¶
Reconstruct stress signal from harmonic components (Fourier decomposition).
Extracts odd harmonic amplitudes and phases from LAOS stress signal, enabling reconstruction and harmonic ratio analysis.
- Parameters:
- Return type:
- Returns:
amplitudes (Array) – Harmonic amplitudes [A_1, A_3, A_5, …] (Pa)
phases (Array) – Harmonic phases [φ_1, φ_3, φ_5, …] (radians)
reconstructed (Array) – Reconstructed stress from harmonics (Pa)
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import harmonic_reconstruction >>> >>> omega = 1.0 >>> t = jnp.linspace(0, 2*jnp.pi, 1000) >>> sigma = 100.0 * jnp.sin(t) + 20.0 * jnp.sin(3*t + 0.1) >>> >>> amps, phases, reconstructed = harmonic_reconstruction(sigma, omega) >>> # amps[0] ≈ 100.0, amps[1] ≈ 20.0 >>> # phases[0] ≈ 0, phases[1] ≈ 0.1
Notes
Only odd harmonics (1, 3, 5, …) are physically relevant in LAOS
Even harmonics indicate asymmetric response (wall slip, etc.)
I_n/I_1 ratio quantifies nonlinearity strength
- rheojax.utils.spp_kernels.power_law_fit(stress, strain_rate, threshold_fraction=0.1)[source]¶
Log-log fit of sigma = K * abs(strain_rate) ** n over the flowing region.
Returns (K, n, r_squared).
- rheojax.utils.spp_kernels.lissajous_metrics(stress, strain, strain_rate, strain_amplitude, rate_amplitude)[source]¶
Compute Lissajous-Bowditch diagram derived metrics.
Extracts nonlinearity measures from Lissajous plots including S-factor (stiffening ratio) and T-factor (thickening ratio).
- Parameters:
stress (
Array) – Time-resolved stress signal σ(t) (Pa)strain (
Array) – Time-resolved strain signal γ(t) (dimensionless)strain_rate (
Array) – Time-resolved strain rate signal γ̇(t) (1/s)strain_amplitude (
float) – Maximum strain amplitude γ_0 (dimensionless)rate_amplitude (
float) – Maximum strain rate amplitude γ̇_0 = ω * γ_0 (1/s)
- Returns:
Dictionary containing: - G_L: Large-strain modulus (tangent at γ = γ_0) - G_M: Minimum-strain modulus (tangent at γ = 0) - eta_L: Large-rate viscosity (tangent at γ̇ = γ̇_0) - eta_M: Minimum-rate viscosity (tangent at γ̇ = 0) - S_factor: Stiffening ratio (G_L - G_M) / G_L - T_factor: Thickening ratio (η_L - η_M) / η_L
- Return type:
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import lissajous_metrics >>> >>> omega = 1.0 >>> t = jnp.linspace(0, 2*jnp.pi, 1000) >>> gamma_0 = 1.0 >>> gamma = gamma_0 * jnp.sin(omega * t) >>> gamma_dot = gamma_0 * omega * jnp.cos(omega * t) >>> sigma = 100.0 * gamma + 10.0 * gamma_dot # Linear viscoelastic >>> >>> metrics = lissajous_metrics(sigma, gamma, gamma_dot, gamma_0, gamma_0 * omega) >>> # S_factor ≈ 0 (linear), T_factor ≈ 0 (linear)
Notes
S > 0: strain stiffening, S < 0: strain softening
T > 0: shear thickening, T < 0: shear thinning
For linear viscoelastic: S = T = 0
- rheojax.utils.spp_kernels.zero_crossing_indices(signal)[source]¶
Find indices of zero-crossings in a signal (robust implementation).
Uses linear interpolation to find precise crossing locations, handling noise-induced multiple crossings via hysteresis filtering.
- Parameters:
signal (
Array) – Input signal to analyze for zero-crossings- Returns:
Boolean mask of zero-crossing locations (True at crossings)
- Return type:
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import zero_crossing_indices >>> >>> signal = jnp.sin(jnp.linspace(0, 4*jnp.pi, 100)) >>> crossings = zero_crossing_indices(signal) >>> # crossings is True at indices where sin crosses zero
Notes
Returns mask of same length as signal
Crossing detected when sign(s[i]) != sign(s[i+1])
Edge cases (exact zeros) handled properly
- rheojax.utils.spp_kernels.harmonic_truncation_robustness(amplitudes, n_harmonics_original, n_harmonics_truncated)[source]¶
Compute truncation error metric for harmonic decomposition.
Quantifies how much signal energy is lost when truncating to fewer harmonics, enabling assessment of reconstruction quality.
- Parameters:
- Returns:
Fraction of total energy retained after truncation (0 to 1)
- Return type:
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import harmonic_truncation_robustness >>> >>> # Fundamental dominant with small 3rd harmonic >>> amps = jnp.array([100.0, 10.0, 2.0, 0.5, 0.1]) >>> robustness = harmonic_truncation_robustness(amps, 5, 2) >>> # robustness ≈ 0.99 (most energy in first 2 harmonics)
Notes
Value near 1.0 indicates safe truncation
Value < 0.95 suggests significant information loss
Useful for adaptive harmonic selection
- rheojax.utils.spp_kernels.spp_stress_decomposition(stress, strain, strain_rate, strain_amplitude, rate_amplitude)[source]¶
Decompose total stress into elastic and viscous contributions via linear projection.
This is a Cho-style orthogonal projection (Cho et al. 2005), NOT the full SPP Frenet-Serret decomposition. It projects stress onto normalized strain and strain-rate directions and distributes the residual symmetrically. For the full SPP decomposition (which includes the displacement stress sigma_d via the osculating plane), use
spp_fourier_analysis()instead.Separates σ(t) = σ’(t) + σ’’(t) where: - σ’(t): Elastic (in-phase with strain) component - σ’’(t): Viscous (in-phase with strain rate) component
- Parameters:
stress (
Array) – Time-resolved stress signal σ(t) (Pa)strain (
Array) – Time-resolved strain signal γ(t) (dimensionless)strain_rate (
Array) – Time-resolved strain rate signal γ̇(t) (1/s)strain_amplitude (
float) – Maximum strain amplitude γ_0 (dimensionless)rate_amplitude (
float) – Maximum strain rate amplitude γ̇_0 = ω * γ_0 (1/s)
- Return type:
- Returns:
sigma_elastic (Array) – Elastic stress contribution σ’(t) (Pa)
sigma_viscous (Array) – Viscous stress contribution σ’’(t) (Pa)
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import spp_stress_decomposition >>> >>> omega = 1.0 >>> t = jnp.linspace(0, 2*jnp.pi, 1000) >>> gamma_0 = 1.0 >>> gamma = gamma_0 * jnp.sin(omega * t) >>> gamma_dot = gamma_0 * omega * jnp.cos(omega * t) >>> # G' = 100 Pa, G'' = 50 Pa >>> sigma = 100.0 * gamma + 50.0 * gamma_dot / omega >>> >>> sigma_e, sigma_v = spp_stress_decomposition( ... sigma, gamma, gamma_dot, gamma_0, gamma_0 * omega ... ) >>> # sigma_e ≈ 100 * gamma (elastic) >>> # sigma_v ≈ 50 * gamma_dot / omega (viscous)
Notes
This is a linear projection decomposition, exact for sinusoidal signals. For nonlinear responses, the residual (displacement stress) is split equally between elastic and viscous components.
For the full SPP decomposition that separately tracks the displacement stress, use
spp_fourier_analysis().For linear viscoelastic: σ_e = G’ * γ, σ_v = G’’ * γ / ω
Decomposition satisfies σ = σ_e + σ_v at all times
- rheojax.utils.spp_kernels.numerical_derivative(signal, dt, order=1, step_size=1)¶
Alias for
numerical_derivative_4th_order(). Kept for backwards compatibility and MATLAB SPPplus naming parity.- Return type:
- rheojax.utils.spp_kernels.numerical_derivative_4th_order(signal, dt, order=1, step_size=1)[source]¶
Compute numerical derivatives using 4th-order finite differences (MATLAB SPPplus compatible).
Implements the EXACT finite-difference schemes from SPPplus_numerical_v2.m: - 4th-order centered differences in the interior - Forward differences at the beginning boundary - Backward differences at the ending boundary
This matches MATLAB’s “standard” differentiation mode (num_mode=1).
- Parameters:
- Returns:
Numerical derivative of same length as input (4th-order accurate in interior)
- Return type:
Notes
MATLAB SPPplus_numerical_v2.m stencils (mode 1):
- First derivative (interior, 4th order):
rd = (-f[p+2k] + 8*f[p+k] - 8*f[p-k] + f[p-2k]) / (12*k*dt)
- Second derivative (interior, 4th order):
rdd = (-f[p+2k] + 16*f[p+k] - 30*f[p] + 16*f[p-k] - f[p-2k]) / (12*(k*dt)^2)
- Third derivative (interior, 4th order):
rddd = (-f[p+3k] + 8*f[p+2k] - 13*f[p+k] + 13*f[p-k] - 8*f[p-2k] + f[p-3k]) / (8*(k*dt)^3)
Forward/backward stencils at boundaries use 2nd-order accurate formulas.
- rheojax.utils.spp_kernels.numerical_derivative_periodic(signal, dt, step_size=1)[source]¶
Compute 1st, 2nd, and 3rd derivatives assuming periodic signal (MATLAB “looped” mode).
For LAOS data where the signal is periodic (steady-state oscillation), this uses centered differences everywhere by wrapping around at boundaries. Matches MATLAB SPPplus_numerical_v2.m “looped” differentiation mode.
- Parameters:
- Return type:
- Returns:
d1 (Array) – First derivative
d2 (Array) – Second derivative
d3 (Array) – Third derivative
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import numerical_derivative_periodic >>> >>> # One complete period of sine wave >>> omega = 1.0 >>> t = jnp.linspace(0, 2*jnp.pi/omega, 1000, endpoint=False) >>> dt = t[1] - t[0] >>> signal = jnp.sin(omega * t) >>> >>> d1, d2, d3 = numerical_derivative_periodic(signal, dt) >>> # d1 ≈ omega * cos(omega*t) >>> # d2 ≈ -omega^2 * sin(omega*t) >>> # d3 ≈ -omega^3 * cos(omega*t)
Notes
Assumes signal represents complete periods (periodic boundary)
Uses higher-order centered differences for accuracy
More accurate than standard differentiation for periodic LAOS data
- rheojax.utils.spp_kernels.spp_numerical_analysis(strain, stress, omega, dt, step_size=8, num_mode=2)[source]¶
Perform full SPP analysis using numerical differentiation (MATLAB-compatible).
Implements the numerical SPP workflow from SPPplus_numerical_v2.m: 1. Compute strain rate from strain (or use provided) 2. Compute derivatives of [strain, rate, stress] trajectory 3. Calculate instantaneous
G'_tandG''_tvia cross-product formula 4. Extracttan(δ)_t, phase angle, and displacement stress- Parameters:
strain (
Array) – Strain signal γ(t) (dimensionless)stress (
Array) – Stress signal σ(t) (Pa)omega (
float|Array) – Angular frequency ω (rad/s). Can be scalar or per-sample array.dt (
float) – Time step between samples (s)step_size (
int) – Finite difference step size k (default: 8 for Rogers parity)num_mode (
int) – 1 = edge-aware (forward/backward + centered); 2 = periodic/looped (default).
- Returns:
Dictionary containing: - Gp_t: Instantaneous storage modulus G’(t) (Pa) - Gpp_t: Instantaneous loss modulus G’’(t) (Pa) - G_star_t: Instantaneous complex modulus
|G*(t)|(Pa) - tan_delta_t: Instantaneous tan(δ)(t) - delta_t: Instantaneous phase angle δ(t) (radians) - disp_stress: Displacement stress (Pa) - eq_strain_est: Equivalent strain estimate- Return type:
Examples
>>> import jax.numpy as jnp >>> from rheojax.utils.spp_kernels import spp_numerical_analysis >>> >>> omega = 1.0 >>> t = jnp.linspace(0, 2*jnp.pi, 1000) >>> dt = t[1] - t[0] >>> gamma_0 = 1.0 >>> strain = gamma_0 * jnp.sin(omega * t) >>> # Linear viscoelastic response >>> stress = 100.0 * strain + 50.0 * gamma_0 * omega * jnp.cos(omega * t) >>> >>> result = spp_numerical_analysis(strain, stress, omega, dt) >>> # result['Gp_t'] ≈ 100.0 (constant for linear material)
Notes
Matches MATLAB SPPplus cross-product formulation
G'_t = -rd_x_rdd[:,0] / rd_x_rdd[:,2]G''_t = -rd_x_rdd[:,1] / rd_x_rdd[:,2]Works directly with raw experimental data (no Fourier decomposition)
- rheojax.utils.spp_kernels.spp_fourier_analysis(strain, stress, omega, dt, n_harmonics=39, n_cycles=1)[source]¶
Complete SPP analysis using Fourier-based analytical derivatives (MATLAB-compatible).
Implements the full workflow from SPPplus_fourier_v2.m: 1. FFT strain and stress signals 2. Compute phase offset and rotate coefficients 3. Compute derivatives ANALYTICALLY from Fourier coefficients 4. Calculate G’(t), G’’(t) via cross-product formula 5. Extract all SPP metrics including moduli rates and Frenet-Serret frame
This is more accurate than numerical differentiation for noisy data.
- Parameters:
strain (
Array) – Strain signal γ(t) (dimensionless)stress (
Array) – Stress signal σ(t) (Pa)omega (
float) – Angular frequency ω (rad/s)dt (
float) – Time step (s)n_harmonics (
int) – Number of odd harmonics for reconstruction (default: 15)n_cycles (
int) – Number of complete cycles in data (default: 1)
- Returns:
Dictionary containing all SPP metrics: - Gp_t: Instantaneous G’(t) (Pa) - Gpp_t: Instantaneous G’’(t) (Pa) - G_star_t: Complex modulus
|G*(t)|(Pa) - tan_delta_t: Loss tangent tan(δ)(t) - delta_t: Phase angle δ(t) (radians) - disp_stress: Displacement stress (Pa) - eq_strain_est: Equivalent strain estimate - Gp_t_dot: Time derivative of G’(t) (Pa/s) - Gpp_t_dot: Time derivative of G’’(t) (Pa/s) - G_speed: Moduli rate magnitude (Pa/s) - delta_t_dot: Phase angle rate (rad/s) - T_vec, N_vec, B_vec: Frenet-Serret frame vectors - strain_recon, stress_recon: Reconstructed waveforms - Delta: Phase offset used- Return type:
Notes
- ANALYTICAL derivatives from Fourier series:
f(t) = Σ [An*cos(nωt) + Bn*sin(nωt)] f’(t) = Σ [-nω*An*sin(nωt) + nω*Bn*cos(nωt)] f’’(t) = Σ [-n²ω²*An*cos(nωt) - n²ω²*Bn*sin(nωt)] f’’’(t) = Σ [n³ω³*An*sin(nωt) - n³ω³*Bn*cos(nωt)]
- rheojax.utils.spp_kernels.compute_phase_offset(strain, omega, dt, n_cycles=1)[source]¶
Compute phase offset Delta for aligning strain to start at zero crossing.
- This matches MATLAB SPPplus_fourier_v2.m phase offset calculation:
Delta = atan(An1_n(p+1)/Bn1_n(p+1)) if Bn1_n(p+1) < 0: Delta = Delta + pi
- rheojax.utils.spp_kernels.harmonic_reconstruction_full(strain, strain_rate, stress, omega, n_harmonics=39, n_cycles=1, W_int=None)[source]¶
Full Fourier-based harmonic reconstruction with phase alignment (MATLAB-compatible).
Implements the complete workflow from SPPplus_fourier_v2.m: 1. FFT all three waveforms (strain, rate, stress) 2. Compute phase offset Delta from strain fundamental 3. Rotate all Fourier coefficients to align with phase reference 4. Reconstruct aligned waveforms
- Parameters:
strain (
Array) – Strain signal γ(t) (dimensionless)strain_rate (
Array) – Strain rate signal γ̇(t) (1/s) - will be normalized by omegastress (
Array) – Stress signal σ(t) (Pa)omega (
float) – Angular frequency ω (rad/s)n_harmonics (
int) – Number of odd harmonics for stress reconstruction (default: 15)n_cycles (
int) – Number of complete cycles in data (default: 1)
- Returns:
Dictionary containing: - Delta: Phase offset (radians) - An_strain, Bn_strain: Aligned strain Fourier coefficients - An_rate, Bn_rate: Aligned rate Fourier coefficients - An_stress, Bn_stress: Aligned stress Fourier coefficients - strain_recon: Reconstructed strain - rate_recon: Reconstructed rate/omega - stress_recon: Reconstructed stress - time_new: Phase-aligned time array
- Return type:
Notes
This function matches MATLAB SPPplus_fourier_v2.m coefficient rotation:
An_n[nn+1] = An1_n[nn+1]*cos(Delta/p*nn) - Bn1_n[nn+1]*sin(Delta/p*nn) Bn_n[nn+1] = Bn1_n[nn+1]*cos(Delta/p*nn) + An1_n[nn+1]*sin(Delta/p*nn)
- rheojax.utils.spp_kernels.frenet_serret_frame(rd, rdd)[source]¶
Compute the Frenet-Serret frame (T, N, B) for a 3D trajectory.
The Frenet-Serret frame provides a local coordinate system along the (γ, γ̇/ω, σ) trajectory, useful for understanding the geometry of the nonlinear response.
- Parameters:
- Return type:
- Returns:
T_vec (Array) – Tangent vector (unit vector in direction of motion)
N_vec (Array) – Principal normal vector (direction of curvature)
B_vec (Array) – Binormal vector (
T × N)curvature (Array) – Local curvature
κ = |rd × rdd| / |rd|³torsion (Array) – Local torsion
τ(requires third derivative, returns zeros)
Notes
Formulas (matching MATLAB SPPplus):
T = rd / |rd| N = -(rd × (rd × rdd)) / (|rd| × |rd × rdd|) B = (rd × rdd) / |rd × rdd| κ = |rd × rdd| / |rd|³
- rheojax.utils.spp_kernels.yield_from_displacement_stress(disp_stress, strain, strain_rate, Gp_t, delta_t, strain_amplitude, rate_amplitude)[source]¶
Extract yield stresses from displacement stress curve (SPP methodology).
This implements the Donley et al. (2019) framework for yield stress extraction: - σ_sy (static yield): From displacement stress at G’(t) → 0 transition - σ_dy (dynamic yield): From displacement stress at δ(t) → π/2 transition
This is more physically meaningful than simple geometric extraction.
- Parameters:
disp_stress (
Array) – Displacement stress σ_disp = σ - (G’·γ + G’’·γ̇/ω) (Pa)strain (
Array) – Strain signal γ(t) (dimensionless)strain_rate (
Array) – Strain rate signal γ̇(t)/ω (normalized, dimensionless)Gp_t (
Array) – Instantaneous storage modulus G’(t) (Pa)delta_t (
Array) – Instantaneous phase angle δ(t) (radians)strain_amplitude (
float) – Maximum strain amplitude γ_0 (dimensionless)rate_amplitude (
float) – Maximum strain rate amplitude γ̇_0 = ω * γ_0 (1/s)
- Returns:
Dictionary containing: - sigma_sy: Static yield stress (Pa) - from G’(t) minima - sigma_dy: Dynamic yield stress (Pa) - from δ(t) → π/2 - yield_strain_sy: Strain at static yield - yield_strain_dy: Strain at dynamic yield - yield_indices_sy: Indices of static yield points - yield_indices_dy: Indices of dynamic yield points - sigma_sy_disp: Static yield from displacement stress peak - sigma_dy_disp: Dynamic yield from displacement stress at zero rate
- Return type:
Notes
The SPP framework defines yield stresses based on the displacement stress: - Static yield occurs when the cage structure breaks (G’(t) → 0) - Dynamic yield occurs when flow ceases (δ(t) → π/2)
This differs from simple geometric extraction (stress at extrema) and provides a more physically meaningful interpretation of the yielding transition.
References
G.J. Donley et al., “Time-resolved dynamics of the yielding transition in soft materials”, J. Non-Newton. Fluid Mech. 264, 2019
- rheojax.utils.spp_kernels.calculate_loop_energy(stress, strain)[source]¶
Calculate energy metrics from stress-strain loops via integration.
Computes the dissipated energy (area of the hysteresis loop) and elastic energy metrics, ensuring parity with MATLAB SPP analysis.
- Parameters:
- Returns:
Dictionary containing: - dissipated_energy: Energy dissipated per cycle (Pa) [Loop Area] - dissipated_energy_density: Dissipated energy / π (Pa) - elastic_energy: Maximum stored elastic energy (Pa) [Estimated]
- Return type:
Notes
Dissipated Energy (E_d): Defined as the area enclosed by the Lissajous loop of stress vs strain: E_d = ∮ σ dγ. This represents the viscous energy loss per unit volume per cycle.
Elastic Energy (E_s): Estimated as the energy stored at maximum strain, approximated by 0.5 * σ(γ_max) * γ_max. This corresponds to the potential energy stored in the elastic structure at peak deformation.
Dissipated Energy Density: E_d / π. This is a normalized metric often used to compare dissipation across different amplitudes.
The integration uses the trapezoidal rule on the time-ordered data points, which correctly computes the signed area of the closed loop.
- rheojax.utils.spp_kernels.differentiate_rate_from_strain(strain, dt, step_size=8, looped=True)[source]¶
Compute strain rate from strain via numerical differentiation.
Provides a wrapped (periodic) 8-point stencil path to mirror the MATLAB/Rogers SPPplus implementation, while keeping the prior finite difference fallback for non-periodic data.
- Parameters:
- Returns:
Strain rate γ̇(t) (1/s)
- Return type:
Notes
looped=True + step_size=8 matches SPPplus v2.1 wrapped 8-point rate inference when the rate column is absent.
looped=False preserves the previous 4th-order finite-difference path.
- rheojax.utils.spp_kernels.convert_units(data, from_unit, to_unit)[source]¶
Convert data between common rheological units.
- Parameters:
- Returns:
Converted data
- Return type:
Examples
>>> strain_fraction = convert_units(strain_percent, 'percent', 'fraction') >>> stress_Pa = convert_units(stress_mPa, 'mPa', 'Pa')