WLF-TTE-TTS

Quick Start

With foods we often need to know their behaviour over large ranges of temperature (freezing to hot) and over large ranges of time (microseconds to years). Our measurement techniques generally work over much smaller ranges. Via WLF we can easily calculate what will happen at any combination of time or temperature.

Credits

These food science apps are something I'm generating slowly over time.

WLF

Tg °C
C
B
Tmax °C
Fact 10 step
Tshift °C
//One universal basic required here to get things going once loaded
window.onload = function () {
    restoreDefaultValues(); //Un-comment this if you want to start with defaults
    Main();
};
//Any global variables go here


//Main is hard wired as THE place to start calculating when input changes
//It does no calculations itself, it merely sets them up, sends off variables, gets results and, if necessary, plots them.
function Main() {
    saveSettings();

    //Send all the inputs as a structured object
    //If you need to convert to, say, SI units, do it here!
    const inputs = {
        Tg: sliders.SlideTg.value,
        C: sliders.SlideC.value,
        B: sliders.SlideB.value,
        Tmax: sliders.SlideTmax.value,
        Step: sliders.SlideStep.value,
    }

    //Get all the resonses as a structure
    const result = CalcIt(inputs)

    //Set all the text box outputs
    document.getElementById('Tshift').value = result.Tshift
    if (result.plots) {
        for (let i = 0; i < result.plots.length; i++) {
            plotIt(result.plots[i], result.canvas[i]);
        }
    }

}

//Here's the real app calculation
function CalcIt({ Tg, C, B, Tmax,Step }) {
    //The structure automatically has the names provided from input
    //By convention the values are provided with the correct units within CalcIt
    //Now we return everything - text boxes, plot and the name of the canvas, which is 'canvas' for a single plot
    let FPtsF=[],FPtsM=[],FPtsS=[],FPtsFs=[],FPtsMs=[],FPtsSs=[],WLFPts=[],y
    const theMax=100000,theE=25,Fchange=Math.log10(10)
    const Tshift=(Tg*(C+Fchange)-Fchange*B)/(Fchange+C)
    for (let T=Tg;T<=Tmax;T++){
        y=theMax*Math.exp(-(T-Tg)/theE)
        FPtsF.push({x:T,y:y*Step})
        FPtsM.push({x:T,y:y})
        FPtsS.push({x:T,y:y/Step})
        FPtsFs.push({x:T+Tshift,y:y*Step})
        FPtsMs.push({x:T,y:y})
        FPtsSs.push({x:T-Tshift,y:y/Step})
   }

    const prmap = {
        plotData: [FPtsS, FPtsM, FPtsF], //An array of 1 or more datasets
        lineLabels: ["0.1", "1", "10"], //An array of labels for each dataset
        xLabel: 'T&°C', //Label for the x axis, with an & to separate the units
        yLabel: 'Value& ', //Label for the y axis, with an & to separate the units
        y2Label: null, //Label for the y2 axis, null if not needed
        yAxisL1R2: [], //Array to say which axis each dataset goes on. Blank=Left=1
        logX: false, //Is the x-axis in log form?
        xTicks: undefined, //We can define a tick function if we're being fancy
        logY: false, //Is the y-axis in log form?
        yTicks: undefined, //We can define a tick function if we're being fancy
        legendPosition: 'top', //Where we want the legend - top, bottom, left, right
        xMinMax: [,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        yMinMax: [,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        y2MinMax: [,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        xSigFigs: 'P3', //These are the sig figs for the Tooltip readout. A wide choice!
        ySigFigs: 'P3', //F for Fixed, P for Precision, E for exponential

    }
    const prmap1 = {
        plotData: [FPtsSs, FPtsMs, FPtsFs], //An array of 1 or more datasets
        lineLabels: ["0.1", "1", "10"], //An array of labels for each dataset
        xLabel: 'T&°C', //Label for the x axis, with an & to separate the units
        yLabel: 'Value& ', //Label for the y axis, with an & to separate the units
        y2Label: null, //Label for the y2 axis, null if not needed
        yAxisL1R2: [], //Array to say which axis each dataset goes on. Blank=Left=1
        logX: false, //Is the x-axis in log form?
        xTicks: undefined, //We can define a tick function if we're being fancy
        logY: false, //Is the y-axis in log form?
        yTicks: undefined, //We can define a tick function if we're being fancy
        legendPosition: 'top', //Where we want the legend - top, bottom, left, right
        xMinMax: [,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        yMinMax: [,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        y2MinMax: [,], //Set min and max, e.g. [-10,100], leave one or both blank for auto
        xSigFigs: 'P3', //These are the sig figs for the Tooltip readout. A wide choice!
        ySigFigs: 'P3', //F for Fixed, P for Precision, E for exponential

    }
    return {
        Tshift:Math.abs(Tshift).toFixed(1),
        plots: [prmap,prmap1],
        canvas: ['canvas','canvas1'],
    };

}
            

As a specific example, and to twist WLF around a bit from its standard portrayal (see my Practical Adhesion WLF app) we'll imagine measuring the (high) viscosity of, say, a starchy material. We do this in a rheometer that can oscillate at various frequencies, detecting the forces required. The sample is on a temperature stage so we can measure the sample at different temperatures.

The app shows that measuring over a timescale of 1 (e.g. 1 second or 1 Hz) we have a value of 100000 at the lowest temperature (the Tg of the material), decreasing exponentially at higher temperatures. But viscosity is a function of the starch molecules' ability to move with respect to each other, and this depends not only on the temperature but on the timescale of the test. If you test in 1/10 the time (10 Hz) the molecular motion can't keep up, so the measured value is higher. Testing in 10x the time (0.1 Hz) the molecules have time to move, so the measured value is lower.

For simplicity none of these values has units - we're just showing the principle - and the relative effect of a factor of 10 shift in measurement time is whatver factor, from 1.1 to 5, you choose. The default is 3.

WLF-TTE-TTS

The left-hand graph shows the individual curves. That's fine, but doesn't convey any deeper connection between the temperature and the measurement time. With the default settings, the right-hand graph has shifted the 0.1 and 10 curves to the left and the right by a temperature shift Tshift. Having a single curve helps us expand the temperature range in case we need values outside what we could conveniently measure, but it also shows that the material is obeying the general idea that temperature and time are interconvertible.

This phenomenon is general and is called Time-Temperature Equivalence (TTE) or Time-Temperature Superposition (TTS). The general theory (the idea works for Time Water Equivalence or Crosslink Temperature Equivalence ...) is called WLF after Williams, Landel and Ferry. The formula is simple but confusing:

`log(a_t)=(-C(T-T_r))/(B+(T-T_r))`

What does this mean? A high temperature measurement is equivalent to one made over a long time, with low temperature being equivalent to a short timescale. The WLF equation says that we can shift measurements made at one temperature by a time factor at. Everything is relative to a reference temperature Tr. If we do a measurement at T=Tr then log(at)=0, so at=1, i.e. we multiply time by 1. Now let's say that we increase T so term on the right becomes -1. That means that at=0.1. That means that to get the same measured value at the higher temperature, we'd have to decrease the measurement time by a factor of 10 or increase the frequency by a factor of 10. Once you try a few examples, it starts to make sense.

What are C and B?

The app shows an idealised dataset of curves measured at different temperature across a relatively narrow time or frequency range and creates a single curve over a wide frequency/temperature range which can be extrapolated beyond your measurements. C and B are fitting parameters along with your definition of Tr which is often the Tg of the sample.

Although they are fitting parameters, they also have a meaning. C is a measure of the range of times/frequencies spanned by that process over the entire relevant temperature range. To say that it is 15 (the usual default value is 17.5) is to say that there are 17.5 orders of magnitude in the process. B is the temperature range that changes the process by half C; in other words, using the default value of 50 °C (the usual default value is 51.6), over 50° the process changes by ~7.5 orders of magnitude.

When you change Tg and/or the Step in values from a factor of 10, you have to re-fit C and B. Although I could add a button to do this automatically, it's a good exercise to do the fitting manually to get a feel for what's going on. It's easy to get lost in WLF fitting space. When in doubt, restore Tg to -25, C to 15, B to 50 and Step to 3. Now make a small change in Tg and refit C and B, then, maybe, a small change in Step. Note that the sliders have limits so you can't always get a perfect fit!

Why should we care?

It turns out that WLF is everywhere in food science. Many properties are dramatically temperature and time dependent (as well as being water/temperature dependent), covering 15 orders of magnitude as shown by the default C value. WLF is a great way to capture these extremes via measurements done over less extreme conditions. And we often need to be clear on the difference between WLF-style phenomena and the other famous temperature effect, Arrhenius. With Arrhenius the default value is a factor of 2 for every 10 °C. So something which takes 128 time units at 25°C will take 64 at 35, 32 at 45, 16 at 55, down to 1 unit at 95 °C. The Arrhenius effects are quite strong - that's a factor of 100 over 70 °C, but nothing like the factors of millions possible with WLF.

Viscosity

In the Powders-Moisture app we need to know how much the viscosity changes as the Tg of the poweder changes in the presence of water. As we know, relatively small amounts of moisture can have large changes in powder properties. The moisture reduces the Tg by modest amounts, but viscosity behaves in a WLF-like manner with respect to Tg, so we have, changing from the log format to the power-of-10 format:

`η = η_0 10^(-(C(T-T_g))/(B+(T-T_g)))`

If anyone ever asks "Why should we care about WLF?" take them to a silo full of a powder that accidentally got exposed to rather too much moisture and ask them to find a way to empty the near-solid contents. Or take them to a production line where the dough or batter is too runny to stay in shape before baking or too stiff to be extruded into the correct shape. Each of those problems is due to properties shifting by large amounts from small effects, all readily calculable via WLF. It can even be too runny at long timescales and too rigid for fast processes!

Accelerated Ageing

If you want to know what happens to your product over extended times, with exposure to different temperatures and, possibly, humidities, you have to do lab tests at rather more extreme conditions to be able to extrapolate from (necessarily) short-temr data to longer times. To be able to extrapolate, you need to know whether your behaviour is basically Arrhenius, relatively gentle, or WLF, relatively catastrophic.