DMTA Workflows

This page presents end-to-end workflows for common DMTA analysis tasks.

Workflow 1: Direct Fit with Modulus Conversion

The simplest workflow: load E* data, fit with any model.

import numpy as np
from rheojax.models import Maxwell

# Synthetic E* data (rubber, nu=0.5 -> E = 3G)
omega = np.logspace(-2, 3, 100)
G0, eta = 1e6, 1e4
tau = eta / G0
G_star = G0 * (1j * omega * tau) / (1 + 1j * omega * tau)
E_star = 3.0 * G_star  # E = 2(1+0.5)*G = 3G

# Fit directly in tension mode
model = Maxwell()
model.fit(
    omega, E_star,
    test_mode='oscillation',
    deformation_mode='tension',
    poisson_ratio=0.5,
)

# Parameters are in G-space
print(f"G0 = {model.parameters.get_value('G0'):.0f} Pa")
print(f"eta = {model.parameters.get_value('eta'):.0f} Pa.s")

# predict() returns E*
E_pred = model.predict(omega, test_mode='oscillation')

Workflow 2: Master Curve from Multi-T DMTA

Time–temperature superposition collapses multi-temperature frequency sweeps into a single master curve.

from rheojax.io import load_csv
from rheojax.models import GeneralizedMaxwell
from rheojax.transforms import Mastercurve

# Load datasets at multiple temperatures
datasets = []
for T in [20, 40, 60, 80, 100, 120]:
    data = load_csv(
        f"dmta_T{T}C.csv",
        x_col="frequency (Hz)",
        y_cols=["E' (Pa)", "E'' (Pa)"],
        temperature=T + 273.15,
        deformation_mode='tension',
    )
    datasets.append(data)

# Create master curve
mc = Mastercurve(reference_temp=60 + 273.15, method='wlf')
master, shifts = mc.transform(datasets)

# Fit Prony series (native tensile mode)
gmm = GeneralizedMaxwell(n_modes=10, modulus_type='tensile')
gmm.fit(master.x, master.y, test_mode='oscillation')

# Extract WLF parameters
C1, C2 = shifts['wlf_C1'], shifts['wlf_C2']
print(f"WLF: C1={C1:.1f}, C2={C2:.1f} K")

Workflow 3: NLSQ -> NUTS for Uncertainty

Bayesian inference quantifies parameter uncertainty from DMTA data.

from rheojax.models import FractionalZenerSolidSolid

model = FractionalZenerSolidSolid()

# Step 1: NLSQ point estimate (with auto E->G conversion)
model.fit(
    omega, E_star,
    test_mode='oscillation',
    deformation_mode='tension',
    poisson_ratio=0.5,
)

# Step 2: NUTS with warm-start
result = model.fit_bayesian(
    omega, E_star,
    test_mode='oscillation',
    num_warmup=1000,
    num_samples=2000,
)

# Diagnostics
print(f"R-hat max: {result.diagnostics['r_hat_max']:.3f}")
intervals = model.get_credible_intervals(
    result.posterior_samples, credibility=0.95
)

Workflow 4: Loading DMTA CSV Files

The CSV reader auto-detects E’/E’’ columns and sets deformation_mode in the RheoData metadata.

from rheojax.io import load_csv

# Auto-detected from column names
data = load_csv(
    "dmta_sweep.csv",
    x_col="omega (rad/s)",
    y_cols=["E' (Pa)", "E'' (Pa)"],
)
print(data.deformation_mode)  # "tension"

# pyvisco format (f, E_stor, E_loss, T, Set)
data = load_csv(
    "pyvisco_data.csv",
    x_col="f",
    y_cols=["E_stor", "E_loss"],
    deformation_mode='tension',  # explicit
)

Workflow 5: Vitrimer DMTA with HVM

The HVM model has built-in Arrhenius kinetics, ideal for vitrimers showing a topology-freezing transition \(T_v\) in DMTA.

from rheojax.models import HVMLocal

model = HVMLocal(kinetics="stress")
model.parameters.set_value("E_a", 80e3)  # Activation energy (J/mol)

# Fit isothermal SAOS at each temperature
model.fit(
    omega, G_star,  # Already converted to G-space
    test_mode='oscillation',
)

# Or fit E* directly
model.fit(
    omega, E_star,
    test_mode='oscillation',
    deformation_mode='tension',
    poisson_ratio=0.5,
)

Workflow 6: Model Selection Query

Use the registry to find all DMTA-compatible models:

from rheojax.core.registry import ModelRegistry
from rheojax.core.test_modes import DeformationMode
from rheojax.core.inventory import Protocol

# All models supporting oscillation + tension
tension_models = ModelRegistry.find(
    protocol=Protocol.OSCILLATION,
    deformation_mode=DeformationMode.TENSION,
)
print(f"{len(tension_models)} models support tension DMTA")

# Check if a specific model supports tension
info = ModelRegistry.get_info("fractional_zener_ss")
print(DeformationMode.TENSION in info.deformation_modes)  # True

Workflow 7: Bounds Handling for Real DMTA Data

Real polymer DMTA data can reach ~10 GPa in the glassy plateau. GeneralizedMaxwell with modulus_type='tensile' uses wider default bounds (\(E_i \leq 10^{12}\) Pa) that accommodate this range.

When fitting real data, disable element minimisation to avoid internal sub-models reverting to default bounds:

from rheojax.models import GeneralizedMaxwell

gmm = GeneralizedMaxwell(n_modes=10, modulus_type='tensile')
gmm.fit(
    omega, E_star,
    test_mode='oscillation',
    optimization_factor=None,  # CRITICAL for real tensile data
)

For other models, widen bounds manually if needed — see bounds widening in Numerical Implementation for the code pattern and a complete discussion of parameter bounds.

Workflow 8: Cross-Domain Validation

Validate fitted models by predicting across domains (frequency ↔ time):

from rheojax.models import GeneralizedMaxwell
import numpy as np

# Fit to frequency-domain E*(omega)
gmm_freq = GeneralizedMaxwell(n_modes=15, modulus_type='tensile')
gmm_freq.fit(omega, E_star_freq, test_mode='oscillation',
             optimization_factor=None)

# Predict E(t) relaxation from the frequency-domain Prony
E_pred_relax = gmm_freq.predict(t, test_mode='relaxation')

# Compare to measured E(t)
R2 = 1 - np.sum((E_relax - E_pred_relax)**2) / np.sum((E_relax - np.mean(E_relax))**2)
print(f"Cross-domain R² = {R2:.4f}")

Warning

Cross-domain prediction requires sufficient Prony modes to represent the full relaxation spectrum. For a master curve spanning 20+ decades, use n_modes >= 15. With n_modes=5 (FAST_MODE), cross-domain R² can be pathologically low or even negative.

See also