Measuring Concentration by Adding Up Trapezoids
When you shine an LED through a coloured solution and measure absorbance across wavelengths, you get a curve with peaks where the dye molecules grab photons. The Beer-Lambert law says concentration is proportional to the area under those peaks—so measuring “how much blue dye” becomes an integration problem. You have discrete sampled points from your photodiode, not a continuous function.
The trapezoidal rule dates to Newton but became standard in the 1960s when scientists moved from slide rules to early Fortran codes. You approximate the curve as a series of trapezoids between adjacent points, sum their areas. Each trapezoid has area (y₀ + y₁) × Δx / 2. Chain them together and you get total area.
Rust does it imperatively, walking pairs of points:
fn trapezoid(xs: &[f64], ys: &[f64]) -> f64 {
xs.windows(2).zip(ys.windows(2))
.map(|(x, y)| (x[1] - x[0]) * (y[0] + y[1]) / 2.0)
.sum()
}
fn main() {
let wavelengths = vec![400.0, 450.0, 500.0, 550.0, 600.0];
let absorbance = vec![0.1, 0.8, 1.2, 0.9, 0.2];
println!("Integrated absorbance: {:.2}", trapezoid(&wavelengths, &absorbance));
}
Haskell zips the lists and folds:
trapezoid :: [Double] -> [Double] -> Double
trapezoid xs ys = sum $ zipWith area (zip xs ys) (zip (tail xs) (tail ys))
where area (x0, y0) (x1, y1) = (x1 - x0) * (y0 + y1) / 2
main = print $ trapezoid [400, 450, 500, 550, 600] [0.1, 0.8, 1.2, 0.9, 0.2]
Both output 215.00 (arbitrary units × nm). That number would scale with dye concentration. Higher peak means more absorbance means more dye molecules in the beam path. The method’s error is O(Δx²)—halve your wavelength spacing and you quadruple accuracy. For a quick measurement with an LED and a handful of samples, it’s sufficient. Simpson’s rule would be better but needs more bookkeeping.