Transforms

This section documents every transform in hermes_rheo.transforms. Transforms consume and return Measurement or MeasurementSet objects; they compose with the + operator into pipelines.

For a general introduction see Background; for full end-to-end examples see the Tutorials and the tutorial_notebooks/ folder.

RheoAnalysis (deprecated alias)

Retained for backwards compatibility. New code should use OWChirpAnalysis directly; per-method default coordinates for non-chirp measurements (Creep, Frequency Sweep, Temperature Ramp, …) are now assigned by the reader, so no post-read transform is required.

Deprecated alias of OWChirpAnalysis.

RheoAnalysis formerly combined OWCh Fourier analysis with per-method switch_coordinates() calls. Since hermes-rheo 2.0 the file readers set the default coordinates for each method (see _METHOD_DEFAULTS in hermes_rheo.file_readers.trios_rheo_txt), and the OWCh analysis lives in OWChirpAnalysis. This module keeps a subclass that warns on construction and otherwise behaves identically. It will be removed in hermes-rheo 3.0.

class hermes_rheo.transforms.rheo_analysis.RheoAnalysis(*args: Any, **kwargs: Any)[source]

Bases: OWChirpAnalysis

Deprecated alias for OWChirpAnalysis.

Emits a DeprecationWarning on construction. Measurements that are not Arbitrary Wave or Frequency Chirp steps are left unchanged, because the readers already set their default coordinates.

Examples

Old (still works, emits a warning):

>>> from hermes_rheo.transforms.rheo_analysis import RheoAnalysis
>>> RheoAnalysis()

New:

>>> from hermes_rheo.transforms.owchirp_analysis import OWChirpAnalysis
>>> OWChirpAnalysis()

OWChirp Signal Generation

Helper for authoring TRIOS Arbitrary Wave macros implementing an OWChirp signal.

class hermes_rheo.transforms.owchirp_generation.OWChirpGeneration(output_file_name: str, waiting_time: float, signal_duration: float, signal_amplitude: float, initial_frequency: float, final_frequency: float, tapering_parameter: float, initial_phase_shift: float)[source]

Bases: object

Generate and visualize an optimally windowed chirp (OWCh) waveform.

The waveform is an exponential chirp with a cosine-squared taper, as described by Geri, Keshavarz and McKinley (see References). It is meant to be entered piecewise into a rheometer’s arbitrary-waveform editor.

Parameters:
  • output_file_name (str) – Name of the output file.

  • waiting_time (float) – Delay before the chirp starts, in s.

  • signal_duration (float) – Chirp length, in s.

  • signal_amplitude (float) – Amplitude of the imposed strain or stress, in rheometer units.

  • initial_frequency (float) – Initial frequency, in rad/s.

  • final_frequency (float) – Final frequency, in rad/s.

  • tapering_parameter (float) – Taper fraction r of the signal duration (0 disables tapering).

  • initial_phase_shift (float) – Initial phase, in degrees.

References

Geri, M., Keshavarz, B., Divoux, T., Clasen, C., Curtis, D. J. and McKinley, G. H., Time-resolved mechanical spectroscopy of soft materials via optimally windowed chirps, Phys. Rev. X 8, 041042 (2018). DOI: 10.1103/PhysRevX.8.041042. Perego, A., Vadillo, D. C., Mills, M. J. L., Das, M. and McKinley, G. H., Evaluation of optimally windowed chirp signals in industrial rheological measurements: method development and data processing, Rheologica Acta 64, 391–406 (2025). DOI: 10.1007/s00397-025-01511-0.

print_parameters()[source]

Print the chirp parameters and waveform expressions.

to_txt(filepath)[source]

Write the chirp parameters and waveform expressions to owchirp_parameters.txt in filepath.

visualize(display_r0=False, filepath=None, y_axis='strain')[source]

Plot the waveform and save its data and figure.

Parameters:
  • display_r0 (bool) – Overlay the untapered (r=0) chirp.

  • filepath (str) – Folder where the waveform data and plot are saved.

  • y_axis ({"strain", "stress"}) – Imposed quantity; sets only the axis label and data-file header.

Time–Temperature Superposition

Automated master-curve construction via the data-driven Gaussian-process method of Lennon, McKinley and Swan (2023) [6].

class hermes_rheo.transforms.automated_mastercurve.AutomatedMasterCurve(state='temperature', state_mode='average', x='angular frequency', y='storage modulus', vertical_shift=False, reverse_data=False, measurements=None, sort_states=True, ref_state_idx=None, *args, **kwargs)[source]

Bases: MeasurementSetTransform

Superpose a measurement set into a master curve (Gaussian-process method).

Wraps the mastercurves package, which learns a Gaussian-process model of the data and finds shift factors by maximum a posteriori estimation [1]_. Data are log-transformed; horizontal shifts (and optionally vertical shifts) are multiplicative.

Parameters:
  • state (str, default "temperature") – Name of the data array used as the shift variable. Its value per measurement is summarized with summarize_state() and stored as a condition.

  • state_mode ({"average", "first point", "last point"}, default "average") – How each measurement’s state value is computed from its array.

  • x (str, default "angular frequency") – Name of the abscissa array.

  • y (str, default "storage modulus") – Name of the ordinate array.

  • vertical_shift (bool, default False) – Also fit multiplicative vertical shift factors.

  • reverse_data (bool, default False) – Reverse the (sorted) data order before superposition.

  • measurements (list of int, optional) – Indices of measurements to use. All are used if None.

  • sort_states (bool, default True) – Sort the measurements by ascending state value.

  • ref_state_idx (int, optional) – Index into the ordered states of the reference state; if given, the shift factors are re-referenced to it.

Returns:

From apply_to: a piblin measurement with the master-curve dataset (shifted x, y and state) and a shift-factor dataset (state against horizontal/vertical shift factors). Details hold the method, reference state, shift factors and uncertainties, and software. The mastercurves object is result.master_curve.

Return type:

MasterCurveMeasurement

References

class hermes_rheo.transforms.automated_mastercurve.CompositeMasterCurve(n_curves=2, y_quantities=None, quantities=None, x='angular frequency', state='temperature', state_mode='average', vertical_shift=False, reverse_data=False, measurements=None, sort_states=True, ref_temp_idx=None, *args, **kwargs)[source]

Bases: MeasurementSetTransform

Build one master curve per y-quantity.

Returns a MasterCurveSet (a piblin measurement set) holding one MasterCurveMeasurement per quantity, usable by downstream steps such as FitModel through MasterCurveSet.available_quantities and MasterCurveSet.get_mastercurve().

Parameters:

References

class hermes_rheo.transforms.automated_mastercurve.MasterCurveMeasurement(master_curve, state, x, y, units, conditions=None, details=None, reference_state=None)[source]

Bases: Measurement

Piblin measurement holding a superposed master curve.

Datasets

  1. Master curve: one-dimensional composite dataset with the shifted abscissa (x), shifted ordinate (y) and the state of each point, in linear scale and sorted by x.

  2. Shift factors: horizontal shift factor (and vertical shift factor if fitted) against the state.

Details hold the method, reference state, shift parameters and uncertainties, and software. The fitted mastercurves.MasterCurve is available as master_curve.

param master_curve:

The fitted master curve (log-transformed data).

type master_curve:

MasterCurveExtended

param state:

Names of the shift variable, abscissa and ordinate arrays.

type state:

str

param x:

Names of the shift variable, abscissa and ordinate arrays.

type x:

str

param y:

Names of the shift variable, abscissa and ordinate arrays.

type y:

str

param units:

Units by array name, as found on the source datasets.

type units:

dict of str to str

param conditions:

Metadata copied from the source measurements.

type conditions:

dict, optional

param details:

Metadata copied from the source measurements.

type details:

dict, optional

param reference_state:

Reference state. Defaults to the state whose shift factor is closest to one.

type reference_state:

float, optional

change_ref(state)[source]

Change the reference state and refresh the piblin datasets and details.

property hparams

Horizontal shift parameters (hparams[0] holds the shift factors).

plot(*args, **kwargs)[source]

Plot the master curve; see mastercurves.MasterCurve.plot().

property states

States of the superposed data, in superposition order.

to_excel(filename)[source]

Export original and shifted data; see MasterCurveExtended.to_excel().

property vparams

Vertical shift parameters.

property xtransformed

Log-scale shifted abscissa blocks, one per state.

property ytransformed

Log-scale shifted ordinate blocks, one per state.

class hermes_rheo.transforms.automated_mastercurve.MasterCurveSet(measurements=None, quantities=None)[source]

Bases: MeasurementSet

Piblin measurement set of master curves, one per quantity.

Provides get_mastercurve() and available_quantities for downstream steps such as FitModel.

property available_quantities

Lower-cased names of the quantities with a master curve.

get_mastercurve(quantity)[source]

Return the master-curve measurement for quantity (case-insensitive), or None.

Rheological Model Fitting

Fits rheological models to master-curve data using pyRheo (Miranda-Valdez et al.) or a built-in multi-mode Maxwell (Prony series) implementation [14]. Background on fractional models and non-Maxwellian relaxation: [12], [13]. pyRheo is an optional dependency (GPL-3.0, not on PyPI) and is imported only when one of its models is requested:

pip install git+https://github.com/mirandi1/pyRheo.git

FitModel supports both oscillation-domain fitting (\(G'(\omega), G''(\omega)\)) and time-domain relaxation fitting (\(G(t)\)) via domain="relaxation".

Calling FitModel returns a piblin Measurement holding a one-dimensional composite dataset with the measured and fitted arrays (angular frequency, storage modulus, loss modulus, fitted storage modulus, fitted loss modulus; or time, relaxation modulus, fitted relaxation modulus). Its details record the model, fitted parameters, goodness of fit, method, references and software version. The transform instance keeps fitted_model, fit_params, plot() and print_parameters() for convenience.

Model fitting of linear viscoelastic moduli.

  • MultiModeMaxwellModel: generalized Maxwell (Prony series) fit of \(G'(\omega)\) and \(G''(\omega)\) by non-negative least squares (default) or multistart nonlinear optimisation. This class fits data; to simulate a multimode Maxwell material use hermes_rheo.lve_models.MultiModeMaxwellModel.

  • FitModel: piblin transform that fits the above or a classical or fractional model from pyRheo to one frequency sweep, a master curve, or (domain="relaxation") a relaxation curve \(G(t)\). It returns a piblin Measurement holding measured and fitted data, with the fitted parameters and goodness of fit in details.

Classical models (Maxwell, Kelvin-Voigt, Zener) combine springs and dashpots. Fractional models replace the dashpot by a spring-pot, whose stress is proportional to a fractional derivative of the strain of order \(\alpha\) (0 = elastic, 1 = viscous). They describe the power-law response of soft materials with few parameters.

pyRheo is an optional dependency (GPL-3.0, not distributed on PyPI) and is imported only when a pyRheo model is requested. Install it with:

pip install git+https://github.com/mirandi1/pyRheo.git

References

[Bonfanti2020]

Bonfanti, A., Kaplan, J. L., Charras, G., Kabla, A., Fractional viscoelastic models for power-law materials, Soft Matter 16(26), 6002-6020 (2020). DOI: 10.1039/d0sm00354a.

[Song2023]

Song, J., Holten-Andersen, N., McKinley, G. H., Non-Maxwellian viscoelastic stress relaxations in soft matter, Soft Matter 19(41), 7885-7906 (2023). DOI: 10.1039/d3sm00736g.

[pyRheo]

Miranda-Valdez, I. Y., Niinisto, J., Makinen, T., Lejon, J., Koivisto, J., Alava, M. J., pyRheo: an open-source Python package for complex rheology, Digital Discovery 4(4), 1075-1082 (2025). DOI: 10.1039/d5dd00021a.

class hermes_rheo.transforms.fit_model.FitModel(model=None, model_name='FractionalZener', n_modes=8, mode='nnls', equilibrium_modulus=False, initial_guesses='random', cost_function='RSS', bounds='auto', num_initial_guesses=500, minimization_algorithm='L-BFGS-B', measurement_index=0, dataset_index=0, x_name='angular frequency', storage_name='storage modulus', loss_name='loss modulus', domain='oscillation', time_name='time', relaxation_name='G_from_eta1', n_interp=100, *args, **kwargs)[source]

Bases: MeasurementSetTransform

Fit a rheological model to linear viscoelastic data.

Fits \(G'(\omega)\) and \(G''(\omega)\) (or, with domain="relaxation", \(G(t)\)) with a classical or fractional model from pyRheo, or with the built-in MultiModeMaxwellModel (a Prony series, \(G(t) = G_e + \sum_i G_i e^{-t/\tau_i}\)). Fractional models use spring-pots of order \(\alpha\), so a few parameters describe power-law relaxation. Parameters are found by minimising a cost (RSS, MSE or BIC) of the model against the data from many random starts. The simulator of the same name, hermes_rheo.lve_models.MultiModeMaxwellModel, generates data instead.

The input may be a Measurement, a MeasurementSet or ExperimentSet (measurement_index picks one measurement), a dataset, or a CompositeMasterCurve whose storage and loss curves are interpolated onto a common log grid. domain="relaxation" fits one dataset, such as the output of FrequencyToTime.

Returns:

  • piblin.data.data_collections.measurement.Measurement – One OneDimensionalCompositeDataset. Oscillation arrays: angular frequency (rad/s), storage modulus, loss modulus, fitted storage modulus, fitted loss modulus (Pa). Relaxation arrays: time (s), relaxation modulus, fitted relaxation modulus (Pa). conditions hold fit_model and fitted. details hold model, parameters, goodness_of_fit (cost, metric, RSS and R-squared; parameter uncertainties are not estimated), method, domain, references and software.

  • The fitted model stays available as fitted_model,

  • fit_params and fit_measurement, and the fitted data as

  • omega_common, G_prime, G_double_prime or t_common,

  • G_relax.

Parameters:
  • model (callable, {"custom"} or None, optional) – pyRheo model class, or "custom" with model_name="MultiModeMaxwell" for the built-in fit. None (default) selects pyRheo.oscillation_model.SAOSModel or, for domain="relaxation", pyRheo.relaxation_model.RelaxationModel.

  • model_name (str, optional) – pyRheo model name (e.g. "FractionalZener") or "MultiModeMaxwell". Default "FractionalZener".

  • n_modes (optional) – Passed to MultiModeMaxwellModel for model="custom".

  • mode (optional) – Passed to MultiModeMaxwellModel for model="custom".

  • equilibrium_modulus (optional) – Passed to MultiModeMaxwellModel for model="custom".

  • initial_guesses (optional) – Optimiser options passed to the fitted model.

  • cost_function (optional) – Optimiser options passed to the fitted model.

  • bounds (optional) – Optimiser options passed to the fitted model.

  • num_initial_guesses (optional) – Optimiser options passed to the fitted model.

  • minimization_algorithm (optional) – Optimiser options passed to the fitted model.

  • measurement_index (int, optional) – Measurement to fit in a set. Default 0.

  • dataset_index (int, optional) – Dataset to fit within the measurement. Default 0.

  • x_name (str, optional) – Names of the angular frequency, storage and loss modulus arrays.

  • storage_name (str, optional) – Names of the angular frequency, storage and loss modulus arrays.

  • loss_name (str, optional) – Names of the angular frequency, storage and loss modulus arrays.

  • n_interp (int, optional) – Points of the common grid for master curves. Default 100.

  • domain ({"oscillation", "relaxation"}, optional) – Fit \(G'(\omega), G''(\omega)\) (default) or \(G(t)\).

  • time_name (str, optional) – Array names for domain="relaxation". Default "time" and "G_from_eta1" (the output of FrequencyToTime).

  • relaxation_name (str, optional) – Array names for domain="relaxation". Default "time" and "G_from_eta1" (the output of FrequencyToTime).

References

[Bonfanti2020]

Bonfanti, A., Kaplan, J. L., Charras, G., Kabla, A., Fractional viscoelastic models for power-law materials, Soft Matter 16(26), 6002-6020 (2020). DOI: 10.1039/d0sm00354a.

[Song2023]

Song, J., Holten-Andersen, N., McKinley, G. H., Non-Maxwellian viscoelastic stress relaxations in soft matter, Soft Matter 19(41), 7885-7906 (2023). DOI: 10.1039/d3sm00736g.

[pyRheo]

Miranda-Valdez, I. Y., Niinisto, J., Makinen, T., Lejon, J., Koivisto, J., Alava, M. J., pyRheo: an open-source Python package for complex rheology, Digital Discovery 4(4), 1075-1082 (2025). DOI: 10.1039/d5dd00021a.

apply_to(target, make_copy=False, **kwargs)[source]

Fit target and return a piblin Measurement.

target may also be a CompositeMasterCurve. make_copy is ignored because the input is never modified.

property model_fit_object

Fitted model object (alias of fitted_model).

property model_fit_params

Fitted parameters of the model (alias of fit_params).

property model_fit_plot

Plot the fit; see plot().

plot(*args, **kwargs)[source]

Plot the fitted moduli against the data.

Calls hermes_rheo.visualization.fit.plot_model_fit(), which accepts show_residuals, ax, figsize, xlabel, ylabel, title, xlim and ylim. savefig, format, dpi and show are handled here. Returns (figure, axes).

print_error(*args, **kwargs)[source]

Print the error of the fitted model.

print_parameters(*args, **kwargs)[source]

Print the parameters of the fitted model.

class hermes_rheo.transforms.fit_model.MultiModeMaxwellModel(n_modes=None, mode='nnls', equilibrium_modulus=False, tau_range=None, modes_per_decade=2, weighting='relative', initial_guesses='random', cost_function='RSS', bounds='auto', num_initial_guesses=500, minimization_algorithm='Powell', initial_guess=None)[source]

Bases: object

Fit a generalized Maxwell (Prony series) model to \(G'(\omega)\), \(G''(\omega)\).

This class fits data. To simulate a multimode Maxwell material see hermes_rheo.lve_models.MultiModeMaxwellModel.

The model is a sum of \(N\) Maxwell elements (modulus \(G_i\), relaxation time \(\tau_i\)) in parallel with an optional equilibrium spring \(G_e\):

\[\begin{split}G'(\omega) &= G_e + \sum_{i=1}^{N} G_i \frac{(\omega\tau_i)^2}{1 + (\omega\tau_i)^2}, \\ G''(\omega) &= \sum_{i=1}^{N} G_i \frac{\omega\tau_i}{1 + (\omega\tau_i)^2}.\end{split}\]

Fit modes:

"nnls" (default)

The \(\tau_i\) are fixed on a log-spaced grid and the \(G_i \ge 0\) follow from one non-negative least-squares solve.

"nonlinear"

\(G_i\) and \(\tau_i\) are optimised in log space by multistart local minimisation.

Parameters:
  • n_modes (int, optional) – Number of modes. For mode="nnls" the default is modes_per_decade per decade of the \(\tau\) grid; required for mode="nonlinear".

  • mode ({"nnls", "nonlinear"}, optional) – Fit mode. Default "nnls".

  • equilibrium_modulus (bool, optional) – Fit \(G_e \ge 0\) (mode="nnls" only). Default False.

  • tau_range (tuple of float, optional) – (tau_min, tau_max) of the \(\tau\) grid for "nnls". Default \((1/\omega_{\max}, 1/\omega_{\min})\).

  • modes_per_decade (int, optional) – Grid density when n_modes is None. Default 2.

  • weighting ({"relative", "none"}, optional) – "relative" (default) weights residuals by 1/|data|.

  • cost_function ({"RSS", "MSE", "BIC"}, optional) – Reported fit metric, and the objective for "nonlinear". Default "RSS".

  • initial_guesses – Options for mode="nonlinear": initial_guesses="random" draws num_initial_guesses perturbed starts and keeps the best; bounds="auto" means unbounded; initial_guess is an explicit start vector [log G_1, log eta_1, ...].

  • num_initial_guesses – Options for mode="nonlinear": initial_guesses="random" draws num_initial_guesses perturbed starts and keeps the best; bounds="auto" means unbounded; initial_guess is an explicit start vector [log G_1, log eta_1, ...].

  • minimization_algorithm – Options for mode="nonlinear": initial_guesses="random" draws num_initial_guesses perturbed starts and keeps the best; bounds="auto" means unbounded; initial_guess is an explicit start vector [log G_1, log eta_1, ...].

  • bounds – Options for mode="nonlinear": initial_guesses="random" draws num_initial_guesses perturbed starts and keeps the best; bounds="auto" means unbounded; initial_guess is an explicit start vector [log G_1, log eta_1, ...].

  • initial_guess – Options for mode="nonlinear": initial_guesses="random" draws num_initial_guesses perturbed starts and keeps the best; bounds="auto" means unbounded; initial_guess is an explicit start vector [log G_1, log eta_1, ...].

Variables:
  • G_modes (numpy.ndarray) – Fitted \(G_i\) (Pa).

  • tau_modes (numpy.ndarray) – Fitted \(\tau_i\) (s).

  • equilibrium_modulus_value (float) – Fitted \(G_e\) (Pa); 0 if not fitted.

  • parameters (numpy.ndarray) – Interleaved [G_1, eta_1, G_2, eta_2, ...] with \(\eta_i = G_i \tau_i\) (Pa s).

  • cost (rss, mse, bic,) – Fit metrics on the concatenated \(G'\) and \(G''\) residuals; cost is the one selected by cost_function.

Examples

>>> import numpy as np
>>> omega = np.logspace(-2, 2, 60)
>>> g_prime, g_double_prime = MultiModeMaxwellModel._moduli(omega, [1e3], [1.0])
>>> model = MultiModeMaxwellModel().fit(omega, g_prime, g_double_prime)
fit(omega, Gp_data, Gpp_data)[source]

Fit the model to storage/loss modulus data.

Parameters:
  • omega (array_like) – Angular frequencies (rad/s).

  • Gp_data (array_like) – Storage and loss moduli (Pa).

  • Gpp_data (array_like) – Storage and loss moduli (Pa).

Returns:

self, fitted.

Return type:

MultiModeMaxwellModel

predict(omega)[source]

Predict (G', G'') at omega from the fitted parameters.

print_error()[source]

Print the mean percentage error and the cost; return the cost.

print_parameters()[source]

Print the fitted G_e (if any) and each mode’s G, tau and eta.

Shift-Factor Models

Fit the temperature dependence of the horizontal time-temperature superposition shift factors \(a_T\) (produced by AutomatedMasterCurve) to the Williams-Landel-Ferry [19] or Arrhenius [20] equations.

Temperature models for time-temperature-superposition shift factors.

Fits the horizontal shift factors \(a_T\) of a master curve built by AutomatedMasterCurve (master_curve.hparams[0] at master_curve.states) with

  • the Williams-Landel-Ferry (WLF) equation [WLF1955], and

  • the Arrhenius equation.

ShiftFactorModel is a pipeline-style wrapper around WLFModel or ArrheniusModel, analogous to FitModel.

class hermes_rheo.transforms.shift_factor_models.ArrheniusModel(reference_temperature=None, temperature_unit='C')[source]

Bases: object

Arrhenius equation for the temperature dependence of shift factors.

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

with \(T\) in kelvin, gas constant \(R\) and activation energy \(E_a\) in J/mol. The fit is performed on \(\log_{10} a_T\), like the WLF model.

Parameters:
  • reference_temperature (float, optional) – Reference temperature, in the unit of the input data. If None (default), the temperature whose shift factor is closest to unity is used.

  • temperature_unit ({"C", "K"}, default "C") – Unit of the input temperatures; Celsius is converted to kelvin.

References

[Arrhenius1889]

Arrhenius, S., “Über die Reaktionsgeschwindigkeit bei der Inversion von Rohrzucker durch Säuren”, Z. Phys. Chem. 4, 226-248 (1889).

fit(temperatures, log_shift_factors, reference_temperature=None)[source]

Fit the activation energy to shift-factor data.

Parameters:
  • temperatures (array_like) – Temperatures of each state, in the unit given by temperature_unit.

  • log_shift_factors (array_like) – Base-10 logarithm of the horizontal shift factors, log10(a_T).

  • reference_temperature (float, optional) – Overrides the value supplied at construction.

Returns:

self, with fitted attributes populated.

Return type:

ArrheniusModel

model = 'Arrhenius'
predict(temperatures)[source]

Return log10(a_T) predicted by the fitted Arrhenius equation.

print_parameters()[source]

Print the fitted activation energy.

class hermes_rheo.transforms.shift_factor_models.ShiftFactorModel(model='WLF', reference_temperature=None, temperature_unit='C')[source]

Bases: object

Fit a WLF or Arrhenius model to master-curve shift factors.

Reads \(a_T\) (master_curve.hparams[0]) and the states (master_curve.states) from a master curve, or a (temperatures, shift_factors) pair, and fits \(\log_{10} a_T\).

Parameters:
  • model ({"WLF", "Arrhenius"}, default "WLF") – Temperature model.

  • reference_temperature (float, optional) – Reference temperature where \(a_T = 1\). If None, the state closest to \(a_T = 1\) is used.

  • temperature_unit ({"C", "K"}, default "C") – Unit of the state temperatures (used by the Arrhenius model).

Examples

>>> from hermes_rheo.transforms.automated_mastercurve import AutomatedMasterCurve
>>> from hermes_rheo.transforms.shift_factor_models import ShiftFactorModel
>>> mc = AutomatedMasterCurve(y='storage modulus').apply_to(experiment)
>>> wlf = ShiftFactorModel(model='WLF')(mc)
>>> wlf.print_parameters()
apply_to(target, make_copy=False, **kwargs)[source]

Fit the model to target; make_copy is ignored (API symmetry with FitModel).

plot(*args, **kwargs)[source]

Plot log10(a_T) versus temperature with the fitted curve.

Delegates to hermes_rheo.visualization.fit.plot_shift_factors() and returns (figure, axes). Pass show=False to skip plt.show.

predict(temperatures)[source]

Return log10(a_T) predicted by the fitted model.

print_parameters()[source]

Print the fitted model parameters.

class hermes_rheo.transforms.shift_factor_models.WLFModel(reference_temperature=None)[source]

Bases: object

WLF equation for the temperature dependence of shift factors.

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

\(C_1\) is dimensionless; \(C_2\) has the unit of the input temperatures (conventionally degC). Both are constrained positive.

Parameters:

reference_temperature (float, optional) – Reference temperature where \(a_T = 1\). If None (default), the temperature whose shift factor is closest to unity is used.

References

[WLF1955]

Williams, M. L., Landel, R. F. and Ferry, J. D., “The temperature dependence of relaxation mechanisms in amorphous polymers and other glass-forming liquids”, J. Am. Chem. Soc. 77(14), 3701-3707 (1955). https://doi.org/10.1021/ja01619a008

fit(temperatures, log_shift_factors, reference_temperature=None)[source]

Fit the WLF constants to shift-factor data.

Parameters:
  • temperatures (array_like) – Temperatures of each state (conventionally in Celsius).

  • log_shift_factors (array_like) – Base-10 logarithm of the horizontal shift factors, log10(a_T).

  • reference_temperature (float, optional) – Overrides the value supplied at construction.

Returns:

self, with fitted attributes populated.

Return type:

WLFModel

model = 'WLF'
predict(temperatures)[source]

Return log10(a_T) predicted by the fitted WLF equation.

print_parameters()[source]

Print the fitted WLF constants.

Temperature Ramp Analysis

Extract Tg and interpolated viscoelastic properties from a ExperimentSet containing temperature-ramp or temperature-sweep steps, and report the test frequency read from the data. Tg is taken at the most prominent tan(delta) peak (default; further peaks, e.g. of a second phase, are reported too), at the onset of the storage-modulus drop (intersection of the glassy and inflection tangents of log G’), or both (tg_method), following [8].

Temperature-ramp analysis: merge temperature steps, detect Tg, extract values.

Tg is taken at the most prominent tan(delta) peak (default), at the onset of the storage-modulus drop (TA Instruments application note RH100), or both (tg_method). The test frequency is read from the data.

Works on an ExperimentSet (or measurement set) containing temperature-ramp or temperature-sweep steps, and is meant to be chained after RheoAnalysis:

pipeline = RheoAnalysis() + TemperatureRampAnalysis(
    steps_to_merge=["Temperature Sweep - 1", "Temperature Sweep - 2"],
    use_temps=[25.0, 100.0],
)
result_exp = pipeline(experiment_set)

ramp = result_exp                 # TemperatureRampMeasurement (piblin Measurement)
print(ramp.summary())             # frequency and Tg values
ramp.to_excel("output.xlsx")

The result is a piblin Measurement holding the merged dataset and the dataset interpolated at use_temps; Tg, the parameters and the method are in its details. A TemperatureRampResult view is also available as ramp.result and transform.result.

Step matching

steps_to_merge entries are matched case-insensitively as substrings of each measurement’s conditions["method"]. For TRIOS files this value has the form "Step name\tTemperature Ramp - 1", so both "Temperature Ramp - 1" and "Temperature Ramp" match. When steps_to_merge is empty, measurements whose method contains a temperature keyword ("temperature sweep", "temperature ramp", …) are used.

Variable names and units are taken from the OneDimensionalCompositeDataset objects and are preserved unchanged in the output datasets.

class hermes_rheo.transforms.temperature_ramp_analysis.StorageModulusOnset(temperature: ndarray, log_modulus: ndarray, slope: ndarray, glassy_range: tuple[float, float], glassy_line: tuple[float, float], inflection_line: tuple[float, float], t_inflection: float, tg_onset: float)[source]

Bases: object

Construction of the onset Tg from the drop of the storage modulus.

Following TA Instruments application note RH100, the onset Tg is where the tangent to the glassy plateau of log10 G’ meets the tangent at the inflection point, the steepest decrease of log10 G’ (minimum of d log10 G’/dT).

Variables:
  • temperature (numpy.ndarray) – Uniform temperature grid of the analysis range.

  • slope (log_modulus,) – Smoothed log10 G’ and its derivative with respect to temperature.

  • glassy_range (tuple of float) – Temperatures over which the glassy tangent is fitted.

  • inflection_line (glassy_line,) – (slope, intercept) of the two tangents of log10 G’ against T.

  • tg_onset (t_inflection,) – Inflection temperature and onset Tg (intersection of the tangents).

glassy_line: tuple[float, float]
glassy_range: tuple[float, float]
inflection_line: tuple[float, float]
log_modulus: ndarray
slope: ndarray
t_inflection: float
temperature: ndarray
tg_onset: float
class hermes_rheo.transforms.temperature_ramp_analysis.TanDeltaPeaks(temperatures: ndarray, heights: ndarray, prominences: ndarray, primary: int)[source]

Bases: object

Peaks of tan(delta) against temperature.

Variables:
  • prominences (temperatures, heights,) – Temperature, tan(delta) value and prominence of each peak, in order of increasing temperature.

  • primary (int) – Index of the most prominent peak, taken as Tg.

heights: ndarray
primary: int
prominences: ndarray
temperatures: ndarray
property tg: float

Temperature of the most prominent peak.

class hermes_rheo.transforms.temperature_ramp_analysis.TemperatureRampAnalysis(steps_to_merge: list[str] | None = None, use_temps: list[float] | None = None, properties: list[str] | None = None, *args: Any, tg_method: str = 'tan_delta', tan_delta_prominence: float = 0.03, tan_delta_max_width: float = 1.5, onset_range: tuple[float, float] | None = None, glassy_fraction: float = 0.125, smoothing: float = 5.0, **kwargs: Any)[source]

Bases: MeasurementSetTransform

Merge temperature-ramp steps, detect Tg and interpolate properties.

Steps:

  1. Select measurements whose conditions["method"] contains any steps_to_merge substring (case-insensitive), or a temperature keyword when steps_to_merge is empty.

  2. Concatenate their OneDimensionalCompositeDataset objects and sort by "temperature". Only variables present in all datasets are kept.

  3. Determine Tg with tg_method: the most prominent tan(delta) peak (find_tan_delta_peaks(), all peaks are reported), the onset of the storage-modulus drop (storage_modulus_onset()), or both.

  4. Read the test frequency from the "angular frequency" and "frequency" arrays (median values).

  5. Linearly interpolate temperature and the requested properties at each temperature in use_temps.

Parameters:
  • steps_to_merge (list of str or None, optional) – Substrings matched case-insensitively against the measurement conditions["method"] to identify steps to include. Typical values are step names as they appear in the TRIOS file, e.g. ["Temperature Ramp - 1", "Temperature Ramp - 2"]. When None or [], temperature-ramp/sweep steps are detected automatically via keyword matching.

  • use_temps (list of float or None, optional) – Temperatures at which to interpolate, in the unit of the data (typically °C).

  • properties (list of str or None, optional) – Variables to extract (case-insensitive). Temperature is always included. When None, all variables are extracted.

  • tg_method ({"tan_delta", "storage_modulus", "both"}, default "tan_delta") – How Tg is determined. With "both", tg is the tan(delta) peak and the onset is reported alongside it.

  • tan_delta_prominence (float, default 0.03) – Minimum prominence of a further tan(delta) peak, as a fraction of the tan(delta) range; see find_tan_delta_peaks().

  • tan_delta_max_width (float, default 1.5) – Maximum width of a further tan(delta) peak relative to the Tg peak.

  • onset_range (tuple of float, optional) – Temperature range of the storage-modulus analysis; it must start on the glassy plateau and contain the inflection. Default: from the first point, symmetric about the inflection.

  • glassy_fraction (float, default 0.125) – Fraction of the onset range used for the glassy tangent.

  • smoothing (float, default 5.0) – Smoothing window of tan(delta) and log10 G’, in temperature units.

Variables:

result (TemperatureRampResult or None) – View of the last output; the returned object is a TemperatureRampMeasurement.

Returns:

From apply_to: a piblin measurement with the merged dataset and the dataset interpolated at use_temps; details hold Tg, the parameters, the method and software.

Return type:

TemperatureRampMeasurement

References

Examples

>>> from hermes_rheo.file_readers.trios_rheo_txt import TriosRheoReader
>>> from hermes_rheo.transforms.rheo_analysis import RheoAnalysis
>>> from hermes_rheo.transforms.temperature_ramp_analysis import TemperatureRampAnalysis
>>> from piblin.data import ExperimentSet
>>> reader = TriosRheoReader()
>>> mset = reader.data_from_filepath(
...     "experiment.txt", create_composite_datasets=True
... )
>>> exp = ExperimentSet(mset.measurements)
>>> pipeline = RheoAnalysis() + TemperatureRampAnalysis(
...     steps_to_merge=["Temperature Ramp"],
...     use_temps=[25.0, 100.0],
...     properties=["storage modulus", "loss modulus", "tan(delta)"],
... )
>>> ramp = pipeline(exp)
>>> ramp.tg
>>> ramp.to_excel("sample.xlsx")
properties: list[str] | None
result: TemperatureRampResult | None
steps_to_merge: list[str]
use_temps: list[float]
class hermes_rheo.transforms.temperature_ramp_analysis.TemperatureRampMeasurement(dataset, extracted_dataset, tg, conditions=None, details=None, tan_delta_peaks: TanDeltaPeaks | None = None, onset: StorageModulusOnset | None = None)[source]

Bases: Measurement

Piblin measurement holding the outputs of a temperature-ramp analysis.

Datasets are the merged temperature series and, when temperatures were requested, the values interpolated at those temperatures. Conditions and details shared by the selected source measurements are carried over; additional details hold the analysis parameters (steps_to_merge, use_temps, properties, tg_method), the test frequency (angular_frequency, frequency and their units), the glass transition temperatures (tg, tg_tan_delta, tg_tan_delta_peaks, tg_onset, t_inflection, tg_unit), the method (analysis_method) and the software.

Parameters:
  • dataset (OneDimensionalCompositeDataset or None) – Merged and interpolated datasets.

  • extracted_dataset (OneDimensionalCompositeDataset or None) – Merged and interpolated datasets.

  • tg (float or None) – Glass transition temperature from the selected method.

  • conditions (dict, optional) – Metadata of the source measurements and analysis parameters.

  • details (dict, optional) – Metadata of the source measurements and analysis parameters.

  • tan_delta_peaks (TanDeltaPeaks, optional) – All tan(delta) peaks, when the tan(delta) method was used.

  • onset (StorageModulusOnset, optional) – The storage-modulus onset construction, when that method was used.

plot_onset(**kwargs)[source]

Plot the storage-modulus onset construction.

Thin wrapper around hermes_rheo.visualization.temperature_ramp.plot_tg_onset().

property result: TemperatureRampResult

The outputs as a TemperatureRampResult.

summary() → str[source]

Test frequency and glass transition temperatures as text.

property tg_values: dict[str, float]

Every Tg computed, by label.

to_excel(path: str) → str[source]

Write the outputs to an Excel workbook; see TemperatureRampResult.to_excel().

class hermes_rheo.transforms.temperature_ramp_analysis.TemperatureRampResult(dataset: OneDimensionalCompositeDataset | None, tg: float | None, extracted_dataset: OneDimensionalCompositeDataset | None, tg_values: dict[str, float] | None = None)[source]

Bases: object

Outputs of a temperature-ramp analysis.

Parameters:
  • dataset (OneDimensionalCompositeDataset or None) – Merged dataset sorted by temperature, with all variables common to the selected measurements (names and units unchanged). The default independent variable is "temperature"; the default dependent variable is "storage modulus" or, if absent, the first non-temperature variable. None if no measurements matched.

  • tg (float or None) – Glass transition temperature from the selected method (the tan-delta peak by default). None when it cannot be determined.

  • extracted_dataset (OneDimensionalCompositeDataset or None) – Temperature and the selected properties interpolated at the requested temperatures, with the names and units of dataset. None when no temperatures were requested.

  • tg_values (dict, optional) – Every Tg computed, by label (e.g. "Tg (tan(delta) peak)").

Examples

>>> result.dataset.data_array_names
>>> result.tg
120.3
dataset: OneDimensionalCompositeDataset | None
extracted_dataset: OneDimensionalCompositeDataset | None
tg: float | None
tg_values: dict[str, float] | None = None
to_excel(path: str) → str[source]

Write the result to an Excel workbook.

Sheet Temperature Series holds the merged dataset (names, units, values). Sheet Extracted Values holds one row per Tg (label, value, unit), then, after a blank row, the interpolated dataset.

Parameters:

path (str) – Destination file path (.xlsx).

Returns:

path.

Return type:

str

hermes_rheo.transforms.temperature_ramp_analysis.find_tan_delta_peaks(temperature: ndarray, tan_delta: ndarray, prominence: float = 0.03, max_width_ratio: float = 1.5, smoothing: float = 5.0) → TanDeltaPeaks | None[source]

Find the tan(delta) peaks of a temperature ramp.

tan(delta) is smoothed over smoothing degrees (Savitzky-Golay, quadratic) to remove noise and the overlaps between ramp steps, and peaks are found with scipy.signal.find_peaks(). The most prominent peak is Tg. A further peak, e.g. a second phase or a secondary relaxation, is kept when its prominence is at least prominence times the tan(delta) range and its width at half prominence is at most max_width_ratio times that of the Tg peak; broad rises of tan(delta) towards flow are rejected. Peak temperatures are the maxima of the measured tan(delta) within half a smoothing window of the smoothed peaks.

Parameters:
  • temperature (numpy.ndarray) – Ramp data; non-finite points are ignored.

  • tan_delta (numpy.ndarray) – Ramp data; non-finite points are ignored.

  • prominence (float, default 0.03) – Minimum peak prominence as a fraction of max - min of tan(delta).

  • max_width_ratio (float, default 1.5) – Maximum width of a further peak relative to the Tg peak.

  • smoothing (float, default 5.0) – Smoothing window, in temperature units.

Returns:

None with fewer than five finite points. Without an interior peak, the maximum of tan(delta) is returned with zero prominence.

Return type:

TanDeltaPeaks or None

hermes_rheo.transforms.temperature_ramp_analysis.storage_modulus_onset(temperature: ndarray, storage_modulus: ndarray, temperature_range: tuple[float, float] | None = None, glassy_fraction: float = 0.125, smoothing: float = 5.0) → StorageModulusOnset | None[source]

Onset Tg from the intersection of the glassy and inflection tangents.

log10 G’ is interpolated on a uniform temperature grid and smoothed with a Savitzky-Golay filter (quadratic, window smoothing wide), which also gives the derivative. The glassy tangent is a straight-line fit over the first glassy_fraction of the analysis range (12.5 % is the TRIOS default); the inflection tangent passes through the derivative minimum.

Parameters:
  • temperature (numpy.ndarray) – Ramp data; non-finite and non-positive moduli are ignored.

  • storage_modulus (numpy.ndarray) – Ramp data; non-finite and non-positive moduli are ignored.

  • temperature_range (tuple of float, optional) – Analysis range; it must start on the glassy plateau and contain the inflection. Default: from the first data point to as far above the steepest decrease of G’ as that lies above the first point.

  • glassy_fraction (float, default 0.125) – Fraction of the analysis range used for the glassy tangent.

  • smoothing (float, default 5.0) – Width of the smoothing window, in temperature units.

Returns:

None if fewer than ten points lie in the analysis range.

Return type:

StorageModulusOnset or None

Recovery Rheology

Oscillatory (OCR) and step-strain (creep-recovery) rheology analyses following Lee, Weigandt, Kelley and Rogers (2019) [9].

Recovery rheology transform.

Recovery rheology splits the total shear strain of a viscoelastic material into a recoverable (elastic) part and an unrecoverable (viscous or plastic) part,

\[\gamma_{tot} = \gamma_{rec} + \gamma_{unrec},\]

so that each part defines its own material function: a recoverable modulus \(G_{rec} = \sigma / \gamma_{rec}\) and a flow viscosity \(\eta_{flow} = \sigma / \dot\gamma_{unrec}\) [1]_ [2]_ [3]. The transform supports two protocols and returns piblin datasets: a RecoveryRheologyMeasurementSet whose measurements hold the computed curves and whose details hold the parameters and scalar results.

Symbols

\(t\) time [s]; \(\omega\) angular frequency [rad/s]; \(\gamma\) shear strain [-]; \(\dot\gamma\) shear rate [1/s]; \(\sigma\) shear stress [Pa]; \(G'\), \(G''\) storage and loss moduli [Pa]. Subscripts: tot (total), rec (recoverable), unrec (unrecoverable), FT (Fourier transform of the stress response).

Oscillatory method (oscillatory recovery, OCR)

  1. Consecutive measurements are paired as forward/reverse runs with positive and negative strain amplitude: (1, 2), (3, 4), … An unpaired last measurement is used alone.

  2. Forward (A) and reverse (B) data are averaged as (A - B) / 2. Because the two runs have opposite strain amplitude, this keeps the response that is odd in strain and cancels drift that does not change sign. The result is centred on the mean of the final oscillation interval.

  3. The Fourier component of the averaged stress at harmonic bin n_osc gives \(G'_{FT}\) and \(G''_{FT}\).

  4. A closed half-period loop is built from the paired strain, recoverable strain and stress, and mirrored (\(x(t + \pi/\omega) = -x(t)\)) to complete the period. After periodic differentiation, cycle-averaged energies yield the decomposed moduli. With \(\gamma_0\) the total-strain amplitude, \(G'_x = 4 \langle \gamma_x \sigma / 2 \rangle / \gamma_0^2\) and \(G''_x = 2 \langle \dot\gamma_x \sigma \rangle / (\omega \gamma_0)^2\) for \(x\) in {tot, unrec, rec}. \(G'_{rec}\) is the solid-like storage modulus, \(G''_{rec}\) the solid-like dissipation and \(G''_{unrec}\) the fluid-like dissipation.

Step-function method

  1. Non-zero strain- or stress-controlled blocks are identified in a stepwise measurement and classified as positive or negative steps.

  2. Positive and negative blocks of the same duration are averaged to give total, unrecoverable and recoverable strain versus experimental time; a duration measured in one direction only is used alone. The recoverable strain of a step is \(\gamma_{rec} = \gamma_{tot} - \gamma_{unrec}\), where \(\gamma_{unrec}\) is the strain left after recovery at zero stress.

References

Examples

>>> from hermes_rheo.transforms.recovery_rheology import RecoveryRheology
>>> out = RecoveryRheology(method="oscillatory")(experiment)
>>> out.measurements[0].details["energy_storage_modulus_recoverable"]
class hermes_rheo.transforms.recovery_rheology.RecoveryRheology(method: str = 'oscillatory', n_osc: int = 2, n_use: int | None = None, diff_state: int = 4, k_step: int = 1, strain_shift_state: bool = False, omega: float | None = None, stress_zero_tol: float = 1e-10, *args, **kwargs)[source]

Bases: MeasurementSetTransform

Recovery rheology analysis of oscillatory or step-function data.

The transform returns a RecoveryRheologyMeasurementSet; the input is not modified.

  • Oscillatory: one measurement with the paired forward/reverse data (time [s], total, unrecoverable and recoverable strain [-], stress [Pa]) and one with the closed decomposition loop (time, the three strains, the three strain rates [1/s] and stress).

  • Step: one measurement with the pair-averaged strain and stress versus time, plus one measurement per deformation step holding the deformation and recovery strain curves.

Parameters and scalar results (omega, storage_modulus_fft, loss_modulus_fft, energy_* and raw_* moduli, amplitude_* values, loop-selection diagnostics) are stored in each measurement’s details. The same results are also available as arrays through the result attribute of the output and of the transform (RecoveryRheologyResult), which provides Lissajous/transient plots and Excel export.

Parameters:
  • method ({"oscillatory", "step"}, default "oscillatory") – Analysis type. “step strain” and “creep” are accepted as aliases of “step”.

  • n_osc (int, default 2) – Harmonic bin index of the FFT of the stress response used for \(G'_{FT}\) and \(G''_{FT}\) (oscillatory only).

  • n_use (int, optional) – Initial guess of the 1-based row at which the half-period starts. If None, the start row is chosen by minimising the discontinuity at the joins of the mirrored loop.

  • diff_state (int, default 4) – Stencil width of the periodic centred finite-difference derivative used for strain rates (1-4).

  • k_step (int, default 1) – Sample spacing of the derivative stencil.

  • strain_shift_state (bool, default False) – Shift the strain columns of the half-period by the mean of its first and mirrored last rows, so the loop closes about zero.

  • omega (float, optional) – Angular frequency \(\omega\) [rad/s]. If None, it is estimated from the FFT peak of the oscillation data; supply it when known.

  • stress_zero_tol (float, default 1e-10) – Stress magnitude [Pa] below which stress is treated as zero, which marks the recovery blocks (step only).

References

Lee, J. C.-W., Weigandt, K. M., Kelley, E. G. and Rogers, S. A. (2019). Structure-Property Relationships via Recovery Rheology in Viscoelastic Materials. Physical Review Letters 122, 248003.

Singh, P. K., Lee, J. C.-W., Patankar, K. A. and Rogers, S. A. (2021). Revisiting the basis of transient rheological material functions: Insights from recoverable strain measurements. Journal of Rheology 65(2), 129-144.

class hermes_rheo.transforms.recovery_rheology.RecoveryRheologyMeasurementSet(measurements: List[Measurement] = None, merge_redundant: bool = True)[source]

Bases: MeasurementSet

Measurements produced by RecoveryRheology.

Each measurement holds one or more piblin datasets; the analysis parameters and scalar results (moduli, amplitudes, angular frequency) are stored in the details of every measurement.

Variables:

result (RecoveryRheologyResult or None) – Array-level view of the analysis with plotting and Excel export helpers. It is set by the transform that created this object.

result: RecoveryRheologyResult | None = None
class hermes_rheo.transforms.recovery_rheology.RecoveryRheologyResult(method: str, recov_anal_data: ndarray, pair_indices: ndarray, mod_fft: ndarray | None = None, amplitudes: ndarray | None = None, trad_mod: ndarray | None = None, raw_mod: ndarray | None = None, decomp_waveform: ndarray | None = None, omega: float | None = None, metadata: dict[str, ~typing.Any] | None=<factory>)[source]

Bases: object

Array-level container for the outputs of a recovery-rheology analysis.

Variables:
  • method (str) – “oscillatory” or “step”.

  • recov_anal_data (numpy.ndarray) – Pair-averaged data. Oscillatory: columns [t, gamma_tot, gamma_unrec, gamma_rec, sigma] (s, -, -, -, Pa). Step: columns [t, gamma_tot, sigma_tot, t_rec, gamma_unrec, gamma_rec] (s, -, Pa, s, -, -).

  • pair_indices (numpy.ndarray) – Index of each forward/reverse pair (oscillatory) or step pair (step).

  • mod_fft (numpy.ndarray or None) – [G'_FT, G''_FT] in Pa from the Fourier transform of the paired stress response (oscillatory).

  • amplitudes (numpy.ndarray or None) – Maximum absolute value of total/unrecoverable/recoverable strain [-], their rates [1/s] and the stress [Pa] over the loop (7 values).

  • trad_mod (numpy.ndarray or None) – Energy-based moduli [Pa] normalised by the total-strain amplitude: [G'_tot, G'_unrec, G'_rec, G''_tot, G''_unrec, G''_rec].

  • raw_mod (numpy.ndarray or None) – The same six moduli [Pa], each normalised by the amplitude of its own strain or strain rate.

  • decomp_waveform (numpy.ndarray or None) – Closed decomposition loop with columns [t, gamma_tot, gamma_unrec, gamma_rec, rate_tot, rate_unrec, rate_rec, sigma] (s, -, -, -, 1/s, 1/s, 1/s, Pa).

  • omega (float or None) – Angular frequency [rad/s].

  • metadata (dict or None) – Additional scalars and, for step data, the transient curves.

amplitudes: ndarray | None = None
decomp_waveform: ndarray | None = None
metadata: dict[str, Any] | None
method: str
mod_fft: ndarray | None = None
omega: float | None = None
pair_indices: ndarray
plot_lissajous(figsize=(6, 4))[source]

Plot stress versus total, recoverable, and unrecoverable responses.

Produces three figures with forward and reverse half-periods. The unrecoverable plot uses strain rate instead of strain.

Parameters:

figsize (tuple of float, default (6, 4)) – Size of each figure.

Returns:

  • figs (list of matplotlib.figure.Figure)

  • axes (list of matplotlib.axes.Axes)

Raises:

ValueError – If no decomposition waveform is available (step results).

plot_step_results(figsize=(8, 5))[source]

Plot total, unrecoverable and recoverable strain versus experimental time.

Parameters:

figsize (tuple of float, default (8, 5)) – Figure size.

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes)

plot_transient(figsize=(10, 6), direction='both', logx=True)[source]

Plot transient recoverable strain versus recovery time.

For each step, the recoverable strain at recovery time t is |gamma(t=0) - gamma(t)|, i.e. the strain recovered so far. It starts at 0 and approaches the final recoverable strain. Curves are coloured by deformation duration.

Parameters:
  • figsize (tuple of float, default (10, 6)) – Figure size.

  • direction ({"pos", "neg", "both"}, default "both") – Step directions to include.

  • logx (bool, default True) – Use a logarithmic x-axis.

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes)

Raises:

ValueError – If the result is not a step result or has no transient curves.

raw_mod: ndarray | None = None
recov_anal_data: ndarray
to_dict() → dict[str, Any][source]

Return all fields as a dict (metadata is {} if None).

to_excel(path: str) → str[source]

Write transient data and moduli to an Excel file.

Sheets: “transient data” (oscillatory), “moduli” (oscillatory) and “step_results” (step).

Parameters:

path (str) – Output file path (.xlsx).

Returns:

Path written.

Return type:

str

trad_mod: ndarray | None = None

Mutation Number

Computes the mutation number \(\mathrm{Mu}\) following Jamali & McKinley (2022) [11] and the mutation-number concept of Mours & Winter (1994) [10], from successive single-wave or correlated OWCh (e.g. TORCh) spectra. Each output measurement holds time, mutation number, angular frequency and, for correlated steps, temperature, so the result can be passed to Kymograph; its details store the mean and maximum mutation number and the calculation settings.

Mutation number of a time-evolving material from successive oscillatory spectra.

The mutation number, Mu = T / lambda_Mu, compares the duration T of an observation (a single oscillatory wave or an optimally windowed chirp) with the mutation time lambda_Mu = (d ln g / dt)^-1 of a viscoelastic quantity g (usually the complex modulus |G*|). When |Mu| is small the sample is quasi-static during the measurement and Fourier-based spectra are valid; the usual criterion is |Mu| <= ~0.15.

The transform returns one measurement per angular frequency. Each holds a piblin composite dataset with the columns “time”, “mutation number” and “angular frequency” (and “temperature” for correlated OWCh steps), so the result can be plotted with MutationNumberMeasurementSet.visualize() or mapped over temperature and frequency with Kymograph.

References

class hermes_rheo.transforms.mutation_number.MutationNumber(state: str = 'time', state_sampling: str = 'first point', dependent_variable: str = 'complex modulus', measurements=None, owch_length: float = 10.0, absolute: bool = False, dt_threshold: float | None = None, *args, **kwargs)[source]

Bases: MeasurementSetTransform

Compute the mutation number of a time-evolving material from a measurement set.

The mutation number is the ratio of the observation time to the mutation time of the modulus, Mu = T * d ln|G*| / dt, evaluated at each frequency from successive spectra. Two input layouts are supported: “Arbitrary Wave” steps and “Correlated” optimal-waveform (OWCh) steps, e.g. a TORCh temperature ramp.

Parameters:
  • state (str, default "time") – Name of the state variable stored as a condition (Arbitrary Wave only).

  • state_sampling ({"average", "first point", "last point"}, default "first point") – How state is reduced to one value per measurement.

  • dependent_variable (str, default "complex modulus") – Dataset variable whose logarithmic derivative is evaluated.

  • measurements (list of int, optional) – Indices of the measurements to use. All are used if None.

  • owch_length (float, default 10.0) – Observation time T of each correlated window, in seconds (Correlated steps only).

  • absolute (bool, default False) – Return |Mu| instead of the signed mutation number. The sign only tells whether g grows (e.g. curing) or decays (e.g. heating); the quasi-static criterion applies to |Mu|.

  • dt_threshold (float, optional) – Skip pairs of consecutive spectra more than dt_threshold seconds apart (e.g. across an isothermal dwell between two ramps).

Returns:

One measurement per angular frequency (condition “Angular Frequency”, rad/s). Its composite dataset holds “time” (s), “mutation number” (dimensionless) and “angular frequency” (rad/s), plus “temperature” for correlated steps; details holds mutation_number_mean, mutation_number_max, n_points and the parameters above.

Return type:

MutationNumberMeasurementSet

Notes

The derivative is a finite difference between consecutive spectra, Mu_i = T ln(g_i / g_(i-1)) / (t_i - t_(i-1)), reported at time t_i (and, for correlated steps, the mean temperature of window i). The logarithmic derivative makes Mu independent of the modulus scale.

References

class hermes_rheo.transforms.mutation_number.MutationNumberMeasurementSet(measurements: List[Measurement] = None, merge_redundant: bool = True)[source]

Bases: MeasurementSet

MeasurementSet of mutation-number time series, one measurement per frequency.

visualize(show_all_frequencies: bool = False, y_lim=None, **kwargs)[source]

Plot the mutation number against time.

Thin wrapper around hermes_rheo.visualization.mutation.plot_mutation_number(); see that function for the keyword arguments (color_by, cmap, threshold, …).

Returns:

  • fig (matplotlib.figure.Figure)

  • ax (matplotlib.axes.Axes or tuple of matplotlib.axes.Axes) – (ax_all, ax_mean) if show_all_frequencies, else ax_mean.

Kymograph

Produces a 2-D kymograph (Temperature × Frequency → viscoelastic quantity) from a Thermally-Optimised Ramped Chirp (TORCh) sequence — introduced by Vadillo, Perego et al. (2026) [7]. Either variable can go on either axis (x, y, log_x, log_y), and KymographResult.plot sets the colour scale (log_z, vmin, vmax).

Kymograph transform: temperature x frequency maps of a viscoelastic quantity.

Builds a 2-D map from a MeasurementSet, e.g. one read by hermes_rheo.file_readers.trios_rheo_txt.TriosRheoReader and processed by OWChirpAnalysis. It is intended for TORCh (Thermally Optimised Ramped Chirp) data [Vadillo2026] but is not limited to it: each axis is taken from a dataset column (e.g. temperature) or, failing that, from measurement.conditions (e.g. Angular Frequency on a MutationNumberMeasurementSet).

Examples

>>> from hermes_rheo import read_file
>>> from hermes_rheo.file_readers.trios_rheo_txt import TriosRheoReader
>>> from hermes_rheo.transforms.owchirp_analysis import OWChirpAnalysis
>>> from hermes_rheo.transforms.kymograph import Kymograph
>>> experiment = read_file(filepath, file_reader=TriosRheoReader,
...                        create_composite_datasets=True)
>>> pipeline = OWChirpAnalysis() + Kymograph(quantity="tan(delta)")
>>> result = pipeline.apply_to(experiment)
>>> result.plot()

Frequency on the x-axis, temperature on the y-axis:

Kymograph("tan(delta)", x="angular frequency", y="temperature",
          log_x=True, log_y=False)

Mutation number map of a TORCh ramp:

from hermes_rheo.transforms.mutation_number import MutationNumber
result = (
    MutationNumber(absolute=True, owch_length=10)
    + Kymograph(quantity="mutation number")
).apply_to(experiment)
result.plot(colormap="magma", vmax=0.15)

References

[Vadillo2026]

Vadillo, D. C., Perego, A. et al., “TORCh: Thermally Optimised Ramped Chirp for time-temperature spectroscopy of polymers”, Macromolecules (2026). https://doi.org/10.1021/acs.macromol.6c00679

class hermes_rheo.transforms.kymograph.Kymograph(quantity: str = 'tan(delta)', *, x: str = 'temperature', y: str = 'angular frequency', n_x: int = 200, n_y: int = 80, interpolation: str = 'cubic', log_y: bool = True, log_x: bool = False, x_min: float | None = None, fill_nearest: bool = True, **kwargs)[source]

Bases: MeasurementSetTransform

Map a viscoelastic quantity onto a regular (x, y) grid.

Collects (x, y, z) points from all datasets in a MeasurementSet (x: slow variable, typically temperature; y: fast variable, typically angular frequency; z: the quantity), interpolates them onto an (n_x, n_y) mesh with scipy.interpolate.griddata() and returns a KymographResult.

Parameters:
  • quantity (str, optional) – The viscoelastic quantity to map. Must be one of the keys in QUANTITY_ALIASES or a column name that exists in the data. Defaults to "tan(delta)".

  • x (str, optional) – Name of the x-axis (slowly varying) variable. Resolved first against dataset column names, then against measurement.conditions (a scalar condition is broadcast across the accompanying z array). Defaults to "temperature".

  • y (str, optional) – Name of the y-axis (fast varying) variable. Same resolution rule as x. Defaults to "angular frequency".

  • n_x (int, optional) – Number of grid points along the x-axis. Defaults to 200.

  • n_y (int, optional) – Number of grid points along the y-axis. Defaults to 80.

  • interpolation (str, optional) – Interpolation method passed to scipy.interpolate.griddata(). One of "cubic", "linear", or "nearest". Defaults to "cubic".

  • log_y (bool, optional) – Interpolate in log-space along y and plot y on a log scale (recommended for frequency axes). Defaults to True.

  • log_x (bool, optional) – Same as log_y for the x-axis, e.g. when frequency is on x. Defaults to False.

  • x_min (float or None, optional) – Override the minimum x-value of the grid (e.g. to crop a cool-down ramp). When None (default), the minimum observed x-value is used.

  • fill_nearest (bool, optional) – After cubic/linear interpolation, fill remaining NaN pixels using nearest-neighbour interpolation. Defaults to True.

Examples

>>> from hermes_rheo import read_file
>>> from hermes_rheo.file_readers.trios_rheo_txt import TriosRheoReader
>>> from hermes_rheo.transforms.owchirp_analysis import OWChirpAnalysis
>>> from hermes_rheo.transforms.kymograph import Kymograph
>>>
>>> experiment = read_file(
...     "path/to/torch_data.txt",
...     file_reader=TriosRheoReader,
...     create_composite_datasets=True,
... )
>>> result = (OWChirpAnalysis() + Kymograph(quantity="tan(delta)")).apply_to(experiment)
>>> result.plot()
class hermes_rheo.transforms.kymograph.KymographResult(*, raw_x: ndarray, raw_y: ndarray, raw_z: ndarray, x_grid: ndarray, y_grid: ndarray, z_grid: ndarray, quantity: str, x_name: str, y_name: str, x_unit: str, y_unit: str, z_unit: str, log_y: bool, log_x: bool = False, conditions: dict | None = None, details: dict | None = None)[source]

Bases: TwoDimensionalDataset

Gridded 2-D piblin dataset produced by Kymograph.

A TwoDimensionalDataset (x_values, y_values, z_values) that also keeps the raw scatter points and provides slicing, plotting and Excel export. conditions and details carry the metadata shared by the source measurements plus the gridding parameters; to_measurement() wraps the dataset and this metadata in a piblin Measurement.

Parameters:
  • raw_x (np.ndarray) – Scattered x-values (e.g. temperature in °C).

  • raw_y (np.ndarray) – Scattered y-values (e.g. angular frequency in rad/s).

  • raw_z (np.ndarray) – Scattered z-values (the viscoelastic quantity).

  • x_grid (np.ndarray, shape (n_x,)) – Regular x-axis grid.

  • y_grid (np.ndarray, shape (n_y,)) – Regular y-axis grid.

  • z_grid (np.ndarray, shape (n_x, n_y)) – Interpolated grid values.

  • quantity (str) – Canonical quantity name (stored as z_name).

  • x_name (str) – Name of the x-axis variable.

  • y_name (str) – Name of the y-axis variable.

  • x_unit (str) – Physical unit of x.

  • y_unit (str) – Physical unit of y.

  • z_unit (str) – Physical unit of z.

  • log_y (bool) – Whether the y-axis uses a logarithmic scale.

  • log_x (bool, optional) – Whether the x-axis uses a logarithmic scale. Defaults to False.

  • conditions (dict, optional) – Conditions shared by the source measurements.

  • details (dict, optional) – Details shared by the source measurements and gridding parameters.

property grid: KymographResult

The gridded dataset (this object).

plot(*, ax: Axes | None = None, colormap: str = 'RdYlBu_r', log_z: bool = False, vmin: float | None = None, vmax: float | None = None, add_scatter: bool = False, add_contours: bool = False, contour_levels: int | list[float] | None = None, contour_colors: str = 'k', figsize: tuple[float, float] = (6.5, 4.8), title: str | None = None) → tuple[Figure, Axes][source]

Plot the 2-D kymograph (heatmap).

Which variable is on which axis is set when the map is built, e.g. Kymograph(x="angular frequency", y="temperature", log_x=True, log_y=False) puts frequency on the horizontal axis.

Parameters:
  • ax (matplotlib.axes.Axes, optional) – Axes to draw into. A new figure is created when None.

  • colormap (str, optional) – A Matplotlib colormap name. Defaults to "RdYlBu_r".

  • log_z (bool, optional) – Use a logarithmic colour scale.

  • vmin (float, optional) – Colour-scale limits. Default: the range of the data (positive values only when log_z).

  • vmax (float, optional) – Colour-scale limits. Default: the range of the data (positive values only when log_z).

  • add_scatter (bool, optional) – Overlay the raw data points as black dots.

  • add_contours (bool, optional) – Overlay contour lines on the heatmap. Defaults to False.

  • contour_levels (int or list of float, optional) – Number of contour levels (int) or explicit level values (list). When None (default), Matplotlib chooses automatically. Only used when add_contours is True.

  • contour_colors (str, optional) – Colour of the contour lines. Defaults to "k" (black).

  • figsize (tuple, optional) – Figure size in inches (width, height).

  • title (str, optional) – Figure title override.

Returns:

fig, ax

Return type:

matplotlib.figure.Figure, matplotlib.axes.Axes

plot_slice_at_x(x_values: float | list[float], *, ax: Axes | None = None, log_x: bool = False, log_y_axis: bool = True, figsize: tuple[float, float] = (7, 4)) → tuple[Figure, Axes][source]

Plot z vs y (frequency) at fixed x (temperature) values.

Parameters:
  • x_values (float or list of float) – Temperature (or other x-axis) value(s) at which to slice.

  • ax (matplotlib.axes.Axes, optional)

  • log_x (bool, optional) – Axis scaling. log_y_axis defaults to True for moduli.

  • log_y_axis (bool, optional) – Axis scaling. log_y_axis defaults to True for moduli.

  • figsize (tuple, optional)

Return type:

fig, ax

plot_slice_at_y(y_values: float | list[float], *, ax: Axes | None = None, log_y_axis: bool = True, figsize: tuple[float, float] = (7, 4)) → tuple[Figure, Axes][source]

Plot z vs x (temperature) at fixed y (frequency) values.

Parameters:
  • y_values (float or list of float) – Angular frequency (or other y-axis) value(s) at which to slice.

  • ax (matplotlib.axes.Axes, optional)

  • log_y_axis (bool, optional) – Log scale on the z-axis. Defaults to True.

  • figsize (tuple, optional)

Return type:

fig, ax

slice_at_x(x_values: float | list[float], *, use_raw: bool = False) → dict[float, tuple[ndarray, ndarray]][source]

Return z vs y at one or more fixed x (e.g. temperature) values.

Parameters:
  • x_values (float or list of float) – The x-axis value(s) at which to slice.

  • use_raw (bool, optional) – If True, search the raw scatter data (nearest-x match). If False (default), interpolate from the regular grid.

Returns:

{x_val: (y_array, z_array)} for each requested x.

Return type:

dict

Examples

>>> slices = result.slice_at_x([25, 100])
>>> for T, (freq, modulus) in slices.items():
...     plt.loglog(freq, modulus, label=f"T={T} °C")
slice_at_y(y_values: float | list[float], *, use_raw: bool = False) → dict[float, tuple[ndarray, ndarray]][source]

Return z vs x at one or more fixed y (e.g. frequency) values.

Parameters:
  • y_values (float or list of float) – The y-axis value(s) at which to slice.

  • use_raw (bool, optional) – If True, search the raw scatter data (nearest-y match). If False (default), interpolate from the regular grid.

Returns:

{y_val: (x_array, z_array)} for each requested y.

Return type:

dict

Examples

>>> slices = result.slice_at_y([1.0, 6.28])   # rad/s
>>> for omega, (temp, modulus) in slices.items():
...     plt.semilogy(temp, modulus, label=f"ω={omega:.2f} rad/s")
to_excel(path: str) → None[source]

Export raw and gridded data to an Excel workbook.

Parameters:

path (str) – File path (should end in .xlsx).

Notes

The workbook contains three sheets:

  • raw_data — longform scatter (x, y, z).

  • grid_data — the interpolated 2-D grid.

  • metadata — quantity, axis names, units.

to_measurement() → Measurement[source]

Return a piblin Measurement with this dataset, conditions and details.

Frequency → Time Relaxation Modulus (iRheoTempo)

Reconstructs time-domain relaxation modulus \(G(t)\) from frequency-domain \(G'(\omega), G''(\omega)\) using the interval-slope method implemented in iRheoTempo (Ramírez and Tassieri, 2026) [15].

This transform is intended to compose with existing pipelines. Common patterns include raw frequency sweeps, and chaining after SplitTemperatureSweep or OWChirpAnalysis. The resulting time-domain dataset can be fitted with FitModel(domain="relaxation").

Relaxation modulus from oscillatory data with i-Rheo-Tempo.

Reconstructs the relaxation modulus \(G(t)\) from \(G'(\omega)\) and \(G''(\omega)\) with the interval-slope Fourier reconstruction of i-Rheo-Tempo (Ramírez and Tassieri, 2026) [RamirezTassieri2026], as implemented in iRheoTempo V9.

Notes

Given \(\eta^*(\omega) = G^*(\omega)/(i\omega)\) with \(\eta^*(\omega)=\eta'(\omega)-i\eta''(\omega)\), the reconstruction uses piecewise-linear interval slopes in frequency space:

\[a_{1,k}=\frac{\eta'_{k+1}-\eta'_k}{\omega_{k+1}-\omega_k},\quad a_{2,k}=\frac{\eta''_{k+1}-\eta''_k}{\omega_{k+1}-\omega_k}\]
\[G_{\eta'}(t)=\frac{2}{\pi t^2}\sum_k \left[\cos(\omega_{R,k}t)-\cos(\omega_{L,k}t)\right]a_{1,k}\]
\[G_{\eta''}(t)=\frac{2}{\pi t^2}\sum_k \left[\sin(\omega_{R,k}t)-\sin(\omega_{L,k}t)\right]a_{2,k} +\frac{2}{\pi}\frac{\eta''(0)}{t}\]

References

[RamirezTassieri2026] (1,2)

Ramírez, J. and Tassieri, M., i-Rheo-Tempo: A model-free, quadrature-free reconstruction of the shear relaxation modulus from complex viscosity, J. Rheol. 70, 1089-1104 (2026). DOI: 10.1122/8.0001232. Reference implementation (MATLAB/Python): University of Glasgow Research Data, 10.5525/gla.researchdata.2230.

class hermes_rheo.transforms.frequency_to_time.FrequencyToTime(units: str = 'rad/s', lf_poly_order: int = 1, lf_fit_decades: float = 0.1, hf_poly_order: int = 1, hf_fit_decades: float = 1.0, hf_ext_decades: float = 1.0, interpolation_method: str = 'spline', csaps_smoothing: float = 0.999, resample_points: int = 5000, enforce_eta2_zero: bool = True, eta2_zero: float = 0.0, n_time_points: int = 600, experimental_gt: tuple[ndarray, ndarray] | None = None, fit_maxwell_tail: bool = False, use_maxwell_eta10: bool = False, n_maxwell_tail_points: int = 200, *args: Any, **kwargs: Any)[source]

Bases: MeasurementTransform

Reconstruct the relaxation modulus from \(G'(\omega)\), \(G''(\omega)\).

Implements i-Rheo-Tempo [RamirezTassieri2026]. The first dataset of the measurement must contain angular frequency, storage modulus and loss modulus; otherwise, or if the frequency range spans less than a factor of 2, the measurement is returned unchanged. A dataset with time (s), G_from_eta1 (Pa) and G_from_eta2 (Pa) is prepended. The two moduli come from \(\eta'\) and \(\eta''\); see Notes in the module docstring.

The reconstruction is stored in details["frequency_to_time_result"] and the method, settings and reference in details["frequency_to_time"].

Parameters:
  • units ({"rad/s", "Hz"}, default "rad/s") – Unit of the input frequency axis

  • lf_poly_order (int, default 1) – Polynomial order for low-frequency \(\eta'(0)\) fit

  • lf_fit_decades (float, default 0.1) – Width of the low-frequency fit window in decades

  • hf_poly_order (int, default 1) – Polynomial order for high-frequency extension

  • hf_fit_decades (float, default 1.0) – Width of high-frequency fitting window in decades

  • hf_ext_decades (float, default 1.0) – Span of the high-frequency extension above the maximum frequency, in decades

  • interpolation_method ({"pchip", "spline", "csaps"}, default "spline") – Interpolant for the viscosity curves; "csaps" needs the csaps package and falls back to "spline" with a warning

  • csaps_smoothing (float, default 0.999) – Smoothing parameter in [0, 1] for "csaps"

  • resample_points (int, default 5000) – Number of points of the resampled frequency grid (at least 50)

  • enforce_eta2_zero (bool, default True) – If True, force \(\eta''(0)\) to eta2_zero

  • eta2_zero (float, default 0.0) – Zero-frequency anchor value for \(\eta''(0)\)

  • n_time_points (int, default 600) – Number of output time points (at least 100)

  • experimental_gt (tuple[np.ndarray, np.ndarray] or None, default None) – Reference (t, G(t)) used only for the MRAE diagnostics

  • fit_maxwell_tail (bool, default False) – Fit a single-mode Maxwell model to the data below the lowest \(G'=G''\) crossover. The fitted \(G_0, \tau, \omega_c\) and the tail \(G_0\exp(-t/\tau)\) are stored in the result.

  • use_maxwell_eta10 (bool, default False) – If the Maxwell fit succeeds, use \(G_0\tau\) instead of the polynomial estimate of \(\eta'(0)\). Requires fit_maxwell_tail=True.

  • n_maxwell_tail_points (int, default 200) – Number of time samples of the Maxwell tail (at least 50)

Examples

>>> from hermes_rheo.file_readers.trios_rheo_txt import TriosRheoReader
>>> from hermes_rheo.transforms.frequency_to_time import FrequencyToTime
>>> exp = TriosRheoReader().data_from_filepath(
...     "test_frequency_sweep_tts_ARES.txt", create_composite_datasets=True
... )
>>> out = FrequencyToTime()(exp)
>>> out.measurements[0].datasets[0].data_array_names
['time', 'G_from_eta1', 'G_from_eta2']
property result: FrequencyToTimeResult | None

Most recent processed result

class hermes_rheo.transforms.frequency_to_time.FrequencyToTimeResult(omega_exp: ndarray, eta1_exp: ndarray, eta2_exp: ndarray, eta10_fit: float, W_interpdata: ndarray, eta1_interpdata: ndarray, eta2_interpdata: ndarray, W_proc: ndarray, eta1_proc: ndarray, eta2_proc: ndarray, t_report: ndarray, G_eta1: ndarray, G_eta2: ndarray, delta_a1_raw: ndarray, delta_a2_raw: ndarray, delta_a1_proc: ndarray, delta_a2_proc: ndarray, W_raw: ndarray, G_threshold_const: float, mrae_eta1: float | None, mrae_eta2: float | None, dataset: OneDimensionalCompositeDataset, maxwell_tail_available: bool = False, maxwell_G0: float = nan, maxwell_tau: float = nan, maxwell_wcross: float = nan, maxwell_ttail: ndarray = <factory>, maxwell_Gtail: ndarray = <factory>)[source]

Bases: object

Outputs of FrequencyToTime.

omega_exp (rad/s) and eta1_exp, eta2_exp (Pa s) are the cleaned input and the complex viscosity \(\eta^* = \eta' - i\eta''\). W_interpdata / W_proc are the extended and resampled frequency grids with their viscosities. t_report, G_eta1, G_eta2 are the reconstruction in s and Pa. The delta_a* fields are slope-jump diagnostics on grid W_raw (raw) and W_proc. mrae_* are set only when experimental_gt is given. dataset is the dataset added to the measurement; the maxwell_* fields hold the optional Maxwell tail.

G_eta1: ndarray
G_eta2: ndarray
G_threshold_const: float
W_interpdata: ndarray
W_proc: ndarray
W_raw: ndarray
dataset: OneDimensionalCompositeDataset
delta_a1_proc: ndarray
delta_a1_raw: ndarray
delta_a2_proc: ndarray
delta_a2_raw: ndarray
eta10_fit: float
eta1_exp: ndarray
eta1_interpdata: ndarray
eta1_proc: ndarray
eta2_exp: ndarray
eta2_interpdata: ndarray
eta2_proc: ndarray
maxwell_G0: float = nan
maxwell_Gtail: ndarray
maxwell_tail_available: bool = False
maxwell_tau: float = nan
maxwell_ttail: ndarray
maxwell_wcross: float = nan
mrae_eta1: float | None
mrae_eta2: float | None
omega_exp: ndarray
t_report: ndarray

Relaxation Spectrum (pyReSpect)

Continuous relaxation spectrum \(H(s)\) and discrete Prony series \((g_i, \tau_i)\) recovered from either a stress-relaxation \(G(t)\) experiment (Shanbhag, 2019 [16]; Takeh & Shanbhag, 2013 [18]) or an oscillatory frequency sweep \(G'(\omega), G''(\omega)\) (Shanbhag, 2020 [17]) via the pyReSpect solvers, which are vendored in hermes_rheo._vendor (MIT licence).

Both branches are exposed through a single MeasurementTransform with an explicit domain={"time", "frequency"} argument.

Continuous and discrete relaxation spectra via pyReSpect.

Two vendored pyReSpect solvers are exposed through one MeasurementTransform:

  • domain="time": pyReSpect-time [Shanbhag2019] computes the continuous spectrum \(H(s)\) from a relaxation modulus \(G(t)\) by Tikhonov regularisation with Bayesian selection of the regularisation parameter, and a discrete spectrum \((g_i, \tau_i)\) by AIC minimisation.

  • domain="frequency": pyReSpect-freq [Shanbhag2020] [TakehShanbhag2013] computes the same quantities from \(G'(\omega)\) and \(G''(\omega)\).

The solvers are vendored in hermes_rheo._vendor (MIT licence, see _vendor/README.md). The inverse problems are

\[G(t) = G_\infty + \int_{-\infty}^{\infty} H(s) e^{-t/s} \, d\ln s\]
\[G'(\omega) = G_\infty + \int_{-\infty}^{\infty} \frac{\omega^2 s^2}{1 + \omega^2 s^2} H(s)\, d\ln s\]
\[G''(\omega) = \int_{-\infty}^{\infty} \frac{\omega s}{1 + \omega^2 s^2} H(s)\, d\ln s\]

The transform prepends two composite datasets to the measurement:

  1. relaxation time (s), H (log spectrum, dimensionless), h = exp(H) (Pa) and, for the frequency branch, dH.

  2. discrete relaxation time (s), g (Pa) and, for the time branch, dtau (s).

The full solver output is stored in details["relaxation_spectrum_result"] as a RelaxationSpectrumResult; the method, domain, solver settings and references are stored in details["relaxation_spectrum"].

References

[Shanbhag2019] (1,2)

Shanbhag, S., pyReSpect: A Computer Program to Extract Discrete and Continuous Spectra from Stress Relaxation Experiments, Macromol. Theory Simul. 28, 1900005 (2019). DOI: 10.1002/mats.201900005.

[Shanbhag2020] (1,2)

Shanbhag, S., Relaxation spectra using nonlinear Tikhonov regularization with a Bayesian criterion, Rheologica Acta 59, 509 (2020). DOI: 10.1007/s00397-020-01212-w.

[TakehShanbhag2013] (1,2)

Takeh, A. and Shanbhag, S., A computer program to extract the continuous and discrete relaxation spectra from dynamic viscoelastic measurements, Applied Rheology 23, 24628 (2013). DOI: 10.3933/applrheol-23-24628.

class hermes_rheo.transforms.relaxation_spectrum.RelaxationSpectrum(domain: str = 'frequency', *, plateau: bool = False, ns: int = 100, freq_end: str = 'lenient', lam_min: float = 1e-10, lam_max: float = 1000.0, lam_C: float | None = None, SmFacLam: float = 0.0, max_num_modes: int | None = None, delta_base_weight_dist: float = 0.2, min_tau_spacing: float = 1.25, resample: bool = True, n_resample: int = 100, lam_density: int = 2, time_name: str = 'time', relaxation_name: str = 'G_from_eta1', omega_name: str = 'angular frequency', storage_name: str = 'storage modulus', loss_name: str = 'loss modulus')[source]

Bases: MeasurementTransform

Continuous and discrete relaxation spectra via pyReSpect.

Uses pyrespect_time for domain="time" [Shanbhag2019] and pyrespect_freq for domain="frequency" [Shanbhag2020] [TakehShanbhag2013]. If the required columns are missing or fewer than five valid points remain, the measurement is returned unchanged.

Parameters:
  • domain ({"time", "frequency"}, default "frequency") – "time" uses the relaxation modulus \(G(t)\); "frequency" uses \(G'(\omega)\) and \(G''(\omega)\).

  • plateau (bool, default False) – Fit a plateau modulus \(G_\infty\) (viscoelastic solids).

  • ns (int, default 100) – Discretisation points on the \(s\) axis.

  • freq_end ({"lenient", "neutral", "strict"}, default "lenient") – Width of the \(s\) window relative to the data window.

  • lam_min (float) – Search bounds for the regularisation parameter \(\lambda\).

  • lam_max (float) – Search bounds for the regularisation parameter \(\lambda\).

  • lam_C (float or None) – Fixed \(\lambda\); skips the \(\lambda\) search when set.

  • SmFacLam (float, default 0.0) – Smoothing shift of the selected \(\lambda\), in \([-1, 1]\).

  • max_num_modes (int or None) – Maximum number of discrete modes.

  • delta_base_weight_dist (float, default 0.2) – Step size of the base-weight scan in the AIC search.

  • min_tau_spacing (float, default 1.25) – Minimum ratio \(\tau_{i+1}/\tau_i\); closer modes are merged.

  • resample (bool, default True) – Resample the input onto a geometric grid before fitting (applied in the time branch only).

  • n_resample (int, default 100) – Number of points of the resampled grid.

  • lam_density (int, default 2) – \(\lambda\) grid points per decade.

  • time_name (str, default "time") – Time column; falls back to "step time".

  • relaxation_name (str, default "G_from_eta1") – \(G(t)\) column. Falls back to G_from_eta1 / G_from_eta2 (written by FrequencyToTime), relaxation modulus, G_relax and modulus.

  • omega_name (str, default "angular frequency") – Angular-frequency column (frequency branch).

  • storage_name (str) – \(G'\) and \(G''\) columns (defaults "storage modulus", "loss modulus").

  • loss_name (str) – \(G'\) and \(G''\) columns (defaults "storage modulus", "loss modulus").

Notes

Input is expected in SI units (s, rad/s, Pa). Non-positive and non-finite samples are dropped. In the frequency branch the data must span at least a factor of 2 in \(\omega\).

Examples

Frequency sweep from a TRIOS file:

>>> from hermes_rheo.file_readers.trios_rheo_txt import TriosRheoReader
>>> from hermes_rheo.transforms.relaxation_spectrum import RelaxationSpectrum
>>> exp = TriosRheoReader().data_from_filepath(
...     "frequency_sweep.txt", create_composite_datasets=True
... )
>>> out = RelaxationSpectrum(domain="frequency")(exp)

Relaxation modulus simulated with hermes_rheo.lve_models.MultiModeMaxwellModel:

>>> import numpy as np
>>> from hermes_rheo.lve_models import MultiModeMaxwellModel, Protocols
>>> model = MultiModeMaxwellModel(g=[1e5, 1e4], tau=[0.1, 1.0])
>>> t = np.geomspace(1e-3, 1e2, 200)
>>> meas = Protocols.stress_relaxation(model, t)
>>> out = RelaxationSpectrum(
...     domain="time",
...     relaxation_name="relaxation modulus",
... )(meas)
property result: RelaxationSpectrumResult | None

Result of the most recent application, or None.

class hermes_rheo.transforms.relaxation_spectrum.RelaxationSpectrumResult(domain: str, config: dict, s: ndarray, H: ndarray, h: ndarray, lam_C: float, G0: float, G_fit_cont: ndarray, lam: ndarray | None = None, rho: ndarray | None = None, eta: ndarray | None = None, log_P: ndarray | None = None, H_lam: ndarray | None = None, dH: ndarray | None = None, g: ndarray = <factory>, tau: ndarray = <factory>, dtau: ndarray | None = None, N_opt: int = 0, G_fit_disc: ndarray = <factory>, G0_disc: float = 0.0, wt_base: ndarray | None = None, AIC_bst: ndarray | None = None, N_bst: ndarray | None = None, dataset_continuous: OneDimensionalCompositeDataset | None = None, dataset_discrete: OneDimensionalCompositeDataset | None = None)[source]

Bases: object

Unified container for pyReSpect-time and pyReSpect-freq outputs.

Variables:
  • domain ({"time", "frequency"}) – Which pyReSpect branch produced the result.

  • config (dict) – ReSpectConfig values passed to pyReSpect.

  • spectrum (Discrete relaxation)

  • s (np.ndarray, shape (ns,)) – Relaxation-time grid in seconds.

  • H (np.ndarray, shape (ns,)) – Log-CRS \(H(s) = \ln h(s)\) (dimensionless).

  • h (np.ndarray, shape (ns,)) – Spectrum \(h(s) = \exp(H(s))\) in Pa.

  • lam_C (float) – Optimal Tikhonov \(\lambda\).

  • G0 (float) – Fitted plateau modulus, or 0.0 when plateau=False.

  • G_fit_cont (np.ndarray) – Predicted \(G(t)\) (time branch) or concatenated \([G' \| G'']\) (frequency branch) from the continuous spectrum, in Pa.

  • dH (lam, rho, eta, log_P, H_lam,) – L-curve and evidence diagnostics. Some are populated by only one branch; see the ContinuousResult classes in pyrespect_time and pyrespect_freq.

  • spectrum

  • g (np.ndarray, shape (N_opt,)) – Discrete mode weights \(g_i\) in Pa.

  • tau (np.ndarray, shape (N_opt,)) – Discrete relaxation times \(\tau_i\) in seconds.

  • dtau (np.ndarray or None) – NLLS uncertainty estimates for \(\tau_i\) (time branch only); None for the frequency branch.

  • N_opt (int) – Optimal number of discrete modes.

  • G_fit_disc (np.ndarray) – Predicted response from the discrete spectrum.

  • G0_disc (float) – Plateau modulus of the discrete fit, in Pa.

  • N_bst (wt_base, AIC_bst,) – AIC-scan diagnostics.

  • dataset_discrete (dataset_continuous,) – The datasets prepended to the measurement; the discrete one is None when no modes were found.

AIC_bst: ndarray | None = None
G0: float
G0_disc: float = 0.0
G_fit_cont: ndarray
G_fit_disc: ndarray
H: ndarray
H_lam: ndarray | None = None
N_bst: ndarray | None = None
N_opt: int = 0
config: dict
dH: ndarray | None = None
dataset_continuous: OneDimensionalCompositeDataset | None = None
dataset_discrete: OneDimensionalCompositeDataset | None = None
domain: str
dtau: ndarray | None = None
eta: ndarray | None = None
g: ndarray
h: ndarray
lam: ndarray | None = None
lam_C: float
log_P: ndarray | None = None
rho: ndarray | None = None
s: ndarray
tau: ndarray
wt_base: ndarray | None = None

Split Temperature Sweep

Splits a multi-temperature isothermal-frequency-sweep block into individual frequency-sweep measurements suitable for downstream master-curve construction.

class hermes_rheo.transforms.split_temperature_sweep.SplitTemperatureSweep(n_steps: int | None = None, temp_step_interval: list[float] | None = None, temp_tolerance: float = 1.0, *args, **kwargs)[source]

Bases: MeasurementSetTransform

Split temperature-sweep measurements into one frequency sweep per temperature.

Each input measurement holds consecutive frequency sweeps at stepped temperatures. Block boundaries are temperature jumps larger than temp_tolerance. Moduli and stress given in Pa are converted to MPa. Each output Measurement carries details["nominal_temperature"].

Parameters:
  • n_steps (int or None, optional) – Number of temperature-sweep measurements expected. If None (default), any number is accepted.

  • temp_step_interval (list of float or None, optional) – Nominal temperature step (degC) for each measurement. If None (default), the step is detected from the data. Length must equal n_steps when both are given.

  • temp_tolerance (float, optional) – Temperature jump (degC) that marks a block boundary. Default 1.0.

Examples

>>> SplitTemperatureSweep()
>>> SplitTemperatureSweep(n_steps=2, temp_step_interval=[5.0, 2.5])
split_by_temperature(dataset: OneDimensionalCompositeDataset, temp_step: float, temp_tolerance: float) → list[dict[str, ndarray]][source]

Split one composite dataset into frequency-sweep blocks.

Data are sorted by step time and grouped at temperature jumps larger than temp_tolerance. Each block is labeled with its median temperature rounded to a multiple of temp_step and sorted by angular frequency.

Returns:

One dict per nominal temperature, mapping array names to arrays, plus the key "nominal_temperature".

Return type:

list of dict

States to Conditions

Convert a data-array (e.g. a per-measurement transient) into a scalar condition (average / first point / last point).

class hermes_rheo.transforms.states_to_conditions.StatesToConditions(state: str = 'temperature', method: str = 'average', *args, **kwargs)[source]

Bases: MeasurementTransform

Store a summary of a measured state as a measurement condition.

The first dataset containing the array state is used. The summary (mean, first or last point) is added to target.conditions[state]. The dataset’s independent variable is left unchanged.

Parameters:
  • state (str, default "temperature") – Name of the data array to summarize.

  • method ({"average", "first point", "last point"}, default "average") – Aggregation applied to the state’s values.

Examples

>>> from hermes_rheo.transforms.states_to_conditions import StatesToConditions
>>> StatesToConditions(state="temperature", method="average")

References