Moisture Sorption

Quick Start

We need to know how much moisture a food powder such as starch, a dried beverage or a sugar absorbs at a given water activity aw. This dependency is described via a water sorption isotherm, conveniently fitted to the GAB (Guggenheim-Anderson-de Boer) model. The amount absorbed affects the glass transition temperature Tg, which in turn affects whether the powder will stick together as calculated via the Powders-Moisture app.

Credits

These food science apps are produced in conjunction with expert Prof Emmanuel Purlis, CONICET, Argentina. This app is the first part of a chain of logic1 from a Nestlé & U Sheffield team.

Sorption Isotherms

M
C
K
Measured RH
% Water
//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 = {
        M: sliders.SlideM.value,
        C: sliders.SlideC.value,
        K: sliders.SlideK.value,
        RH: sliders.SlideRH.value,
    }

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

    //Set all the text box outputs
    document.getElementById('PercW').value = result.PercW
    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({ M,C,K,RH }) {
    //The structure automatically has the names provided from input
    //By convention the values are provided with the correct units within CalcIt
    let GotRH=false,PercW=0,WPts=[]
    for (let i = 0; i <= 95; i++) {
        aw=i/100
        M1=100*M*C*K*aw/((1-K)*(1-K*aw+C*K*aw))
         WPts.push({x:aw*100,y:M1})
         if (i>=RH && ! GotRH){
             GotRH=true
             PercW=M1
         }
    }

    //Now we return everything - text boxes, plot and the name of the canvas, which is 'canvas' for a single plot
    const prmap = {
        plotData: [WPts], //An array of 1 or more datasets
        lineLabels: ["%Water"], //An array of labels for each dataset
        hideLegend: true,
        xLabel: 'a_w& ', //Label for the x axis, with an & to separate the units
        yLabel: '%Water& ', //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 {
        PercW: PercW.toFixed(1),
        plots: [prmap],
        canvas: ['canvas'],
    };
}
            

A water sorption isotherm shows the volume fraction of water absorbed versus the water activity, aw or, if you prefer, %RH/100. To get these isotherms you need to be able to measure the weight increase as the %RH of the air is changed, allowing enough time for equilibrium to be reached before stepping to the next level. There is often hysteresis (not shown) between the values measured going from 0 to 100% and 100% going down to 0, with the descending isotherm showing more sorption that the ascending one.

The GAB model has three parameters: M which is supposed to what would be expected from a monolayer coverage, typically in the 0.05 to 0.2 range, but with foods we have absorption as well as adsorption, then there's C which is in the 1-50 range, and K which is an adjustment parameter, typically in the 0.5 to 1 range. When K=1 the equation is formally equivalent to the well-known BET isotherm. The GAB model calculates the Sorption, S for a given water activity, aw. Because experimental data and GAB both have problems above 0.95 activity, the graph stops at that point.

`S=(MCKa_w)/((1-Ka_w)(1-Ka_w+CKa_w))`

When you set your actual RH the fraction at that RH is calculated and you can use the value in the Tg-Moisture app.

1Christine I. Haider, Gerhard Niederreiter, Stefan Palzer, Michael J. Hounslow, Agba D. Salman, Unwanted agglomeration of industrial amorphous food powder from a particle perspective, Chem. Eng. Res. & Design, 132, 2018, 1160–1169