Source code for hermes_rheo.visualization.temperature_ramp

"""Glass transition from the storage modulus of a temperature ramp."""

from __future__ import annotations

import os
from typing import Any

import matplotlib.figure
import matplotlib.pyplot as plt

from hermes_rheo.visualization import _common as c
from hermes_rheo.visualization import style
from hermes_rheo.visualization.units import axis_label, get_unit, unit_suffix

STORAGE_MODULUS = "storage modulus"
TEMPERATURE = "temperature"
GUIDE_LW = 1.5
GUIDE_COLOR = "0.5"


[docs] def plot_tg_onset( ramp: Any, *, figsize: tuple[float, float] = (5.5, 5.5), colors: tuple[str, str] = ("darkblue", "k"), xlim: tuple[float, float] | None = None, save: str | os.PathLike | None = None, show: bool = False, ) -> tuple[matplotlib.figure.Figure, tuple[Any, Any]]: """Plot how the onset Tg is obtained from the storage modulus. Top: G' with the smoothed curve, the tangent fitted to the glassy plateau (shaded range), the tangent at the inflection and their intersection, the onset Tg. Bottom: d log10 G'/dT, whose minimum is the inflection. Parameters ---------- ramp : TemperatureRampMeasurement Output of ``TemperatureRampAnalysis(tg_method="storage_modulus")`` or ``tg_method="both"``. figsize : tuple of float, default (5.5, 5.5) Figure size. colors : tuple of str, default ("darkblue", "k") Colours of the data and of the fitted tangents; guide lines are gray. xlim : tuple of float, optional Temperature limits, e.g. to zoom on the construction; default: the whole analysis range. save : path-like, optional Save the figure to this path. show : bool, default False Call ``plt.show()``. Returns ------- fig : matplotlib.figure.Figure axes : tuple of matplotlib.axes.Axes ``(ax_modulus, ax_slope)``. """ onset = ramp.onset if onset is None: raise ValueError("No onset construction: use tg_method='storage_modulus' or 'both'.") ds = ramp.dataset data_color, line_color = colors temperature = c.array_by_name(ds, TEMPERATURE) modulus = c.array_by_name(ds, STORAGE_MODULUS) t_unit = get_unit(ds, TEMPERATURE) fig, (ax_g, ax_d) = plt.subplots(2, 1, sharex=True, figsize=figsize, height_ratios=(2, 1)) t = onset.temperature ax_g.semilogy( temperature, modulus, "o", color=data_color, ms=4, alpha=0.35, mew=0, label="data" ) ax_g.semilogy( t, 10**onset.log_modulus, "-", color=data_color, lw=style.LINEWIDTH, label="smoothed" ) ax_g.axvspan(*onset.glassy_range, color="0.9", lw=0, label="glassy fit range") for (slope, intercept), ls in ((onset.glassy_line, "--"), (onset.inflection_line, ":")): ax_g.semilogy(t, 10 ** (slope * t + intercept), ls, color=line_color, lw=GUIDE_LW) ax_g.plot( onset.tg_onset, 10 ** (onset.glassy_line[0] * onset.tg_onset + onset.glassy_line[1]), "o", ms=8, color=line_color, label=f"onset $T_g$ = {onset.tg_onset:.1f} {t_unit}", ) ax_d.plot(t, onset.slope, color=data_color, lw=style.LINEWIDTH) ax_d.plot( onset.t_inflection, onset.slope.min(), "o", ms=8, mew=GUIDE_LW, mfc="none", color=line_color, label=f"inflection = {onset.t_inflection:.1f} {t_unit}", ) for ax in (ax_g, ax_d): ax.axvline(onset.tg_onset, color=GUIDE_COLOR, lw=GUIDE_LW, ls="--") ax.axvline(onset.t_inflection, color=GUIDE_COLOR, lw=GUIDE_LW, ls=":") if xlim is None: xlim = (t[0], t[-1]) shown = (t >= xlim[0]) & (t <= xlim[1]) ax_g.set_ylim( 10 ** onset.log_modulus[shown].min() / 2, 10 ** onset.log_modulus[shown].max() * 2 ) ax_d.set_ylim( onset.slope[shown].min() * 1.15, max(0.0, onset.slope[shown].max()) + 0.1 * abs(onset.slope[shown].min()), ) c.decorate_axes(ax_g, ylabel=axis_label(ds, STORAGE_MODULUS), xlim=xlim) c.decorate_axes( ax_d, xlabel=axis_label(ds, TEMPERATURE), ylabel=f"d log$_{{10}}$({STORAGE_MODULUS})/dT" + unit_suffix(f"1/{t_unit}"), ) ax_d.yaxis.label.set_size(style.LABEL_SIZE * 0.8) for ax in (ax_g, ax_d): c.add_legend(ax, loc="upper right", fontsize=style.LEGEND_SIZE - 1) c.finish_figure(fig, save=save, show=show) return fig, (ax_g, ax_d)