Pore Size

Quick Start

Here we show two techniques for measuring pore size - CFP, Capillary Flow Porometry, and MIP, Mercury Intrusion Porosimitry. Each has its uses, their core theory is the same, but there are also plenty of differences between them

Credits

This is an amalgam of explanations from the on-line community.

Pore Size

Pore Diameter nm
Width nm
MIP
CFP
//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: sliders.SlideD.value, //keep in nm for the plot
        W: sliders.SlideW.value,
        CFP: document.getElementById('CFP').checked,
    };

    //Send inputs off to CalcIt where the names are instantly available
    //Get all the resonses as an object, result


    const result = CalcIt(inputs);

    //Set all the text box outputs
    //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, W, CFP }) {
    let DPts = [], PPts = [], dv, h, i, maxh = 0, tV = 0, PMax, P, Vol = 0, Dmin = 5, Dmax = 5, FInc = 10
    for (dv = 5; dv <= 1500; dv += 10) { //Extend the range for calcs of wide distributions
        h = Math.exp(-Math.pow((dv - D) / W, 2))
        if (h > 0.01 && Dmin == 5) Dmin = dv //First take-off of pressure
        if (h < 0.01 && Dmin > 5 && Dmax == 5) Dmax = dv //Lowest take-off of pressure
        maxh = Math.max(h, maxh)
        DPts.push({ x: dv, y: h })
    }
    for (i = 0; i < DPts.length; i++) {
        DPts[i].y *= 100 / maxh
    }

    const MV = CFP ? 4 * 0.016 : 4 * 0.48 * 0.766//ST 16 mN/m cosθ = 1 : ST 480 mN/m, θ=140, cosθ=-0.766
    const NPts = DPts.length
    PMax = 1050 * MV / Dmin
    if (CFP) PMax *= 1e3
    i = NPts
    while (i > 0) {
        i--
        P = 1e3 * MV / DPts[i].x //1e9 for nm but then 1e-6 for MPa
        if (CFP) {
            P *= 1e3 //to kPa
            //Simulate the increasing flow at higher P's
            FInc = DPts[i].x > Dmax ? 0 : 20
            Vol += DPts[i].y + FInc * P / PMax
        } else {
            Vol += DPts[i].y
        }
        if (P <= PMax) PPts.push({ x: P, y: Vol })
    }

    for (i = 0; i < PPts.length; i++) {
        PPts[i].y *= 100 / Vol
    }

    const plotData = [DPts]
    const plotData1 = [PPts]
    const Pressure = CFP ? 'Pressure&kPa' : 'Pressure&MPa'
    const yLabel = CFP ? 'Rel Flow&' : 'Rel Volume&'
    //Now set up all the graphing data detail by detail.
    const prmap = {
        plotData: plotData, //An array of 1 or more datasets
        lineLabels: "Size", //An array of labels for each dataset
        colors: ["blue"], //An array of colors for each dataset
        hideLegend: true,
        borderWidth: 2,
        xLabel: 'Pore D&nm', //Label for the x axis, with an & to separate the units
        yLabel: 'φ&', //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: null, //We can define a tick function if we're being fancy
        logY: false, //Is the y-axis in log form?
        yTicks: null, //We can define a tick function if we're being fancy
        legendPosition: 'top', //Where we want the legend - top, bottom, left, right
        xMinMax: [10, 1000], //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: '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: plotData1, //An array of 1 or more datasets
        lineLabels: "Pressure", //An array of labels for each dataset
        colors: ["orange"], //An array of colors for each dataset
        hideLegend: true,
        borderWidth: 2,
        xLabel: Pressure, //Label for the x axis, with an & to separate the units
        yLabel: yLabel, //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: null,
        logY: false, //Is the y-axis in log form?
        yTicks: null,
        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: 'P3', //These are the sig figs for the Tooltip readout. A wide choice!
        ySigFigs: 'P3', //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, prmap1],
        canvas: ['canvas', 'canvas1'],
        // ASI: ASI.toFixed(1),
        // ASIVals: ASIVals,
    };
}

            

Pore pressure

If we assume that our porous material has a bunch of capillaries each with a radius D, then the pressure required to get a liquid of surface tension σ with a contact angle θ into the pore is given by `P=(4σcosθ)/D`

If we start squeezing some non-wetting mercury (σ = 480 mN/m, θ=140°) into a sample of porous material after removing all the air, and can follow the volume of mercury that disappears into the pores then we should see a volume versus pressure curve that reflects the fraction of pores of a given size.

If we start with a sample filled with a fully-wetting non-volatile fluorocarbon (σ = 16 mN/m, θ=0°) and increase the air pressure to empty the liquid, we get a similar curve, though once the sample is empty the curve carries on rising as more and more air squeezes through.

The app is very simple. You choose your (simplified) size distribution and width, and choose either MIP for the Mercury Intrusion Porosimitry or CFP for Capillary Flow Porometry.

In MIP it's straightforward to get the total volume of pores from the absolute volume of mercury that flows into the pores compared to the known volume of particles. No attempt is made to do that here.

To look at super-small pore sizes using MIP, the equipment has to be seriously upgraded to allow super-high pressures. Here we stay in the safer area away from those high pressures.

For CFP a "dry run" is done once the liquid has been expelled. It catches up with the top right of the graph, to define the smallest pore size. The arithmetic "half dry" line crosses the flow graph at the pressure equivalent to half pore size. The lowest pressure for which some air flows is the First Bubble Point, FBP, from which the largest pore size is calculated. Because our size distribution is an input rather than an output, we don't need to show those extra features.

In real CFP you either have to increase the pressure very slowly, or do it in steps, or use a special pressure profile, to give the flow time to equilibrate. Again, no attempt is made to do that here.

What about N2 porosimetry?

The interpretation of liquid N2 isotherms is complex, convoluted and controversial. No attempt is made to model this form of porosimetry.