Foam Fall

Quick Start

When a ball of foam falls from a given height into a pool of liquid, how long does it take to fall, with what velocity does it hit the liquid and how deep does it plunge into the liquid before coming back up to the surface?

It depends on the size of the ball, on the expansion ratio (or, the volume fraction of air φ) which affects the density and on the density of the liquid.

Credits

The app is part of the work of the LASTFIRE team working towards improved on-site knowledge of foam properties relevant to fighting fires.

Foam Fall

Expansion
Height m
Diameter cm
ρ liquid g/cc
Plunge Depth cm
Float Depth cm
Vimpact m/s
Vterminal m/s
Foam density g/cc
Ricochet angle °
H-Scale 10x larger for liquid, mm not cm
//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 = {
        phi: 1 - 1 / sliders.SlideExpansion.value, //From Expansion to φ
        H: sliders.SlideH.value,
        R: sliders.SlideD.value / 200, //cm diameter to m radius
        rho: sliders.Sliderho.value * 1000, //g/cc to kg/m3
    }

    //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
    document.getElementById('Vt').value = result.Vt
    document.getElementById('Vimpact').value = result.Vimpact
    document.getElementById('maxDepth').value = result.maxDepth
    document.getElementById('floatDepth').value = result.floatDepth
    document.getElementById('rhofoam').value = result.rhofoam
    document.getElementById('thetar').value = result.thetar
   //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 CalcIt!
}

//Here's the app calculation
//The inputs are just the names provided - their order in the curly brackets is unimportant!
//By convention the input values are provided with the correct units within Main
function CalcIt({ phi, H, R,rho }) {
    const pi = Math.PI, g = 9.81
    let Comments = ""
    let Hnow = H, Vnow = 0, Volnow = 4 / 3 * pi * Math.pow(R, 3), Anow = pi * R * R
    let dt = 0.001, tnow = 0, acc = 0
    const VolFoam = Volnow
    const rhowater = 1000, rhooil = rho, rhoair = 1.225
    const rhofoam = phi * rhoair + (1 - phi) * rhowater
    const thetar = 18/Math.sqrt(rhofoam/rhooil)
    const m = Volnow * rhofoam
    const Cd = 1 //Used a crude drag coefficient but now swapped to CDv
    const Vt = Math.sqrt(2 * m * g / (rhoair * Anow * Cd))
    const AF = 0.1806, BF = 0.6459, CF = 0.4251, DF = 6880.95
    const ReP = 2 * R / 1.8e-5 //Pre-reynolds number for air
    const RePl = 2 * R / 1e-2 //Pre-reynolds number for typical fuel 10cP
    let HPlot = [], VPlot = [], Re=1 //A reasonable starting value
    HPlot.push({ x: 0, y: Hnow * 100 })
    VPlot.push({ x: 0, y: Vnow })
    while (Hnow >= 0) {
        CDv = 24 / Re * (1 + AF * Math.pow(Re, BF)) + CF / (1 + DF / Re)
        acc = (m * g - 0.5 * rhoair * Anow * CDv * Vnow * Vnow) / m
        Vnow += acc * dt
        Re = Math.abs(ReP * Vnow)
        Hnow -= Vnow * dt
        tnow += dt
        HPlot.push({ x: tnow, y: Math.max(Hnow * 100, 0) })
        VPlot.push({ x: tnow, y: Vnow })
    }
    const Vimpact=Vnow
    HNow = 0
    let Down = true
    dt = 0.0001 //No benefit from a smaller time step
    let maxDepth=0, floatDepth=0
    while ((Vnow > 0 || Hnow < floatDepth) && tnow<4) {
        if (HNow <= R) {
            Volnow = 0.333 * pi * Hnow * Hnow * (3 * R - Math.abs(Hnow))
            Anow = pi * (2 * R * Math.abs(Hnow) - Hnow * Hnow)
        } else {
            Volnow = VolFoam / 2 + 0.333 * pi * Math.pow(R - HNow, 2) * (2 * R - HNow)
            Anow = pi * R * R
        }
        CDv = 24 / Re * (1 + AF * Math.pow(Re, BF)) + CF / (1 + DF / Re)
        acc = ((m - Volnow * rhooil) * g - 0.5 * rhooil * Anow * CDv * Vnow * Vnow) / m
        Re = Math.abs(RePl * Vnow)
        Vnow += acc * dt
        if ((m - Volnow * rhooil)>=0) floatDepth=Hnow //Simpler than solving a quadratic equation
        if (Vnow <= 0 && Down) Down = false
        Hnow -= Vnow * dt
        maxDepth=Math.min(maxDepth,Hnow)
        tnow += dt
        HPlot.push({ x: tnow, y: Hnow * 1000 })
        VPlot.push({ x: tnow, y: Math.abs(Vnow)})
    }
    //Now set up all the graphing data detail by detail.
    let plotData = [HPlot, VPlot]
    let lineLabels = ["Height", "Velocity"] //An array of labels for each dataset
    const prmap = {
        plotData: plotData,
        lineLabels: lineLabels,
        xLabel: "t& s", //Label for the x axis, with an & to separate the units
        yLabel: "H& cm", //Label for the y axis, with an & to separate the units
        y2Label: "v& m/s", //Label for the y2 axis, null if not needed
        yAxisL1R2: [1, 2], //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: [0,], //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: 'F2', //These are the sig figs for the Tooltip readout. A wide choice!
        ySigFigs: 'F1', //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 {
        Vt: Vt.toFixed(1),
        Vimpact: Vimpact.toFixed(1),
        maxDepth: (-maxDepth*100).toFixed(1),
        floatDepth: (floatDepth*100).toFixed(1),
        rhofoam: (rhofoam/1000).toFixed(2),
        thetar: thetar.toFixed(0),
        plots: [prmap],
        canvas: ['canvas'],
    };

}

        

The core equations for the foam sphere radius R (you enter Diameter) falling through air from an initial height H and then in the liquid are the same. The acceleration `a` given a velcocity v at time t is `a=(δv)/(δt)` which depends on the mass m (which, in turn, depends on the foam's volume `V=4/3πR^3` and density, see below for the calculation of how this depends on Expansion ratio, S, or air volume fraction φ), the density of the fluid `ρ_(fluid)`, the drag coefficient Cd, the cross sectional area `A=πR^2` and the velocity.

`m(δv)/(δt)=mg-0.5ρ_(fluid)CdAv^2`

By stepping through with small time intervals δt the current velocity and distance travelled, H, can be calculated as `v_(t+δt)=v_t+aδt` and `H_(t+δt)=H_t+vδt`

The difference in the calculation for the liquid is that the mg term on the right has to be replaced by `(m-Vρ_(liquid))g` which includes the buoyancy term where V is the volume immersed - which rapidly increases (equation not shown - read the code) as the sphere enters the liquid. Also the drag term uses the instantaneous cross-sectional area (up to `πR^2`, again, equation not shown). The foam slows down then moves upwards propelled by the buoyancy. It comes to rest at a float depth where the foam mass and the buoyancy forces balance.

The drag coefficient for a sphere is often said to be 0.5, but in reality it is strongly dependent on the Reynolds number. Those interested in the formula for this dependence can find CDv in the code, the velocity-dependent CD which in turn relies on calculation of the Reynolds number.

The graph shows both the height and velocity with time. The calculation stops when the foam reaches its equilibrium float depth and doesn't attempt to show it bobbing up and down. Note that the scale in the liquid is 10x larger, so read the values as mm not cm.

The textbox outputs show the plunge depth (of the lowest point of the sphere) in the liquid and the equilibrium floating depth. Then the impact velocity is shown and for interest, the terminal velocity of the foam falling from many metres is also shown.

In reality the foam might break up in the air, or on hitting the liquid, or when the liquid starts interacting with the foam. The calculations are simply to show limiting cases.

The foam density depends on the volume fraction of air, φ, which is given from the Expansion, S, as `φ=1-1/S`. The density of the foam is `ρ_(foam)=φρ_(air)+(1-φ)ρ_(water)` or, effectively, `ρ_(foam)=ρ_(water)/S`.

It has been noted that foam bounces ("ricochets") off the liquid when it hits at an angle below a critical ricochet angle `θ_c`. The standard formula (used widely even though there are many assumptions behind it) is that:

`θ_c=18/sqrt(ρ_(foam)/ρ_(liquid))`