FTIR Diffusion

Quick Start

By following the ATR-FTIR signal at a chosen absorption band you can follow diffusion from the opposite side and calculate the diffusion coefficient. It involves some fancy arithmetic with ATR convolution, but modern Javascript handles it no problem.

Comments

The blending of the standard Crank diffusion term with the ATR sensitivity is from Fieldson & Barbari1.

FTIR Diffusion

D cm²/s
Crystal
tmax d
n1
θ °
Peak cm-1
L μm
Depth μm
"use strict"
//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();
};

//Main() is hard wired as THE place to start calculating when inputs change
//It does no calculations itself, it merely sets them up, sends off variables, gets results and, if necessary, plots them.
function Main() {
    //Save settings every time you calculate, so they're always ready on a reload
    saveSettings();

    //Send all the inputs as a structured object
    //If you need to convert to, say, SI units, do it here!
    const inputs = {
        D: parseFloat(document.getElementById('D').value),
        Crystal: document.getElementById("Crystal").value,
        tmax: sliders.Slidetmax.value * 24*3600, //day to s
        n1: sliders.Sliden1.value,
        theta: sliders.Slidetheta.value * Math.PI / 180, //deg to rad
        lambda: 1 / sliders.Slidelambda.value, // cm-1 to cm
        L: sliders.SlideL.value/1e4, //μm to cm
    };

    //Send inputs off to CalcIt where the names are instantly available
    //Get all the resonses as an object, result
    console.time("check")
    const result = CalcIt(inputs);
    console.timeEnd("check")
    //Set all the text box outputs
     document.getElementById('Dp').value = result.Dp;

    //Do all relevant plots by calling plotIt - if there's no plot, nothing happens
    //plotIt is part of the app infrastructure in app.new.js
    if (result.plots) {
        for (let i = 0; i < result.plots.length; i++) {
            plotIt(result.plots[i], result.canvas[i]);
        }
    }
    //You might have some other stuff to do here, but for most apps that's it for Main!
}

//Here's the app calculation
function CalcIt({ D, tmax, Crystal, n1, theta, lambda, L }) {
    let PlotA = [], i = 0
    const cVal = Crystal.split(" : ")
    const n2 = parseFloat(cVal[1]), pi = Math.PI
    //gamma is the penetration depth but we use the inverse!
   const gamma = 1/(lambda/(2 * n2 * pi * Math.sqrt(Math.sin(theta) ** 2 - (n1 / n2) ** 2) ))
    const Dp=(1/gamma*1e4).toFixed(2)
 
    let nterms = 100
    let At = 0, AtMax=0, sigma = 0, f,g
    L*=2 //We use 2L
    const L2=L*L, pi2=pi*pi //and 4L² and pi²
    const gL=-gamma*L, g42=4*gamma**2
    //console.log(D,n1,n2,theta,lambda,L,lambda/L,g, g*2*L)
    //Now we brute force it
    PlotA.push({x:0,y:0})
    for (let t = 100; t < tmax; t += 10) {
        sigma=0
        for (let n = 0; n < nterms; n++){
            f=pi*(2*n+1)/L
            g=-D*(2*n+1)**2*pi2*t/L2
            sigma+=Math.exp(g)*(f*Math.exp(gL)+(-1)**n*(2*gamma))/((2*n+1)*(g42+f*f))
        }
        At=1-8*gamma/(pi*(1-Math.exp(-L*gamma)))*sigma
        AtMax=Math.max(At,AtMax)
        PlotA.push({x:t/3600/24,y:At})
        //a large nterms is only needed at short times
        if (t > 20000 && nterms == 100) nterms=10
    }
    if (AtMax <1e-3) { //These gave numerical arterfacts
        PlotA=[]
        PlotA.push({x:0,y:0})
        PlotA.push({x:tmax/3600/24,y:0})
    }

    const plotData = [PlotA], lineLabels = ["At/A∞"], myColors = ["blue"], borderWidth = [3]

    //Now set up all the graphing data detail by detail.
    const prmap = {
        plotData: plotData, //An array of 1 or more datasets
        lineLabels: lineLabels, //An array of labels for each dataset
        colors: myColors, //An array of colors for each dataset
        hideLegend: true,
        borderWidth: borderWidth,
        xLabel: 't&d', //Label for the x axis, with an & to separate the units
        yLabel: 'At/A∞& ', //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: [0,], //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: 'F1', //These are the sig figs for the Tooltip readout. A wide choice!
        ySigFigs: 'F3', //F for Fixed, P for Precision, E for exponential
    };

    //Now we return everything - text boxes, plot and the name of the canvas, which is 'canvas' for a single plot
    return {
        plots: [prmap],
        canvas: ['canvas'],
        Dp: Dp,
    };

}
            

The ATR technique

One side of a thin polymer film, thickness L, is exposed to your permeant. The other side is in contact with your ATR crystal. How you do this depends on your system and ingenuity, especially if you want T-dependent values. Here we assume that you have everything set up nicely.

The permeant has a distinctive peak in an area where the film has no absorption. [If you don't have good separation of absorption peaks, you'll have to do more data massaging before using the app]. At time t the absorption At is given by the standard Crank diffusion equation convoluted with penetration-depth dependence of the ATR setup, which in turn depends on crystal (especially its RI n2), angle θ, wavelength Peak cm-1 and also the RI of the polymer n1. In reality, and as is obvious, when L > a few μm the ATR correction makes little difference; but it's good to have it there. At will eventually plateau to A. Using the complex equation from Fieldson & Barbari, we can calculate the At/A curve depending on the diffusion coefficient, D.

Although we could in principle load your data and find D by fitting, it's just as easy to find the best visual match to your experimental data by adjusting D when the other known parameters have been entered.

1

G. T. Fieldson and T. A. Barbari, The use of FTIR-ATR spectroscopy to characterize penetrant diffusion in polymers, Polymer, 1993, 34, 1146-1153