Python package for size and fluorescence analysis of emulsion droplets by flow cytometry, using Mie scattering theory to convert raw FSC/SSC signals into absolute droplet radii.
Based on the methodology of:
Fattaccioli et al., Size and fluorescence measurements of individual droplets by flow cytometry, Soft Matter 2009, 5, 2232–2238. https://doi.org/10.1039/b814954b
A flow cytometer measures two light-scattering signals per particle:
- Forward scatter (FSC) — collected over a narrow annular cone at low angles (typically 0.7°–4°), sensitive to particle size.
- Side scatter (SSC) — collected at 90° through a high-NA lens, sensitive to both size and internal structure.
For an emulsion droplet (homogeneous sphere of refractive index n_oil in a medium of index n_water), both signals are computed from the Mie amplitude functions S₁(θ) and S₂(θ) via numerical integration over the detector apertures, accounting for the polarisation state of the laser beam and the azimuthal acceptance of the SSC lens.
The theoretical I_SS(I_FS) curve is a 1D parametric curve in the 2D scatter diagram. Each measured event is assigned a radius by nearest-neighbour projection onto this curve. The method is instrument-calibratable and requires no a priori assumption about the size distribution.
pip install .Dependencies: numpy, scipy, pandas, matplotlib
No external Mie library is required — the scattering functions are implemented directly from Bohren & Huffman (1983).
The FCS 2.0/3.0/3.1 parser is also built-in; no fcsparser needed.
Optional, for the notebook:
pip install jupyterfrom cytometry4emulsions import InstrumentParams, FCSFile, assign_radii, size_distribution
from cytometry4emulsions import plots
# 1. Instrument model — use a preset or set parameters manually
params = InstrumentParams(
wavelength_nm = 488.0,
theta_fs_min_deg = 0.716, # inner FS cone angle (deg)
theta_fs_max_deg = 3.954, # outer FS cone angle (deg)
theta_ss_max_deg = 52.38, # SS lens half-angle = arcsin(NA) * 180/pi
n_medium = 1.337, # PBS at 488 nm
n_particle = 1.468, # soybean oil at 488 nm
)
# or: params = InstrumentParams.from_preset("FACSCalibur")
# 2. Load FCS file (channels auto-detected; override with explicit names if needed)
fcs = FCSFile("my_sample.fcs")
fcs.print_summary()
IFS, ISS, fl = fcs.get_scatter_data()
# 3. (Optional) Calibrate on a monodisperse reference emulsion
from cytometry4emulsions import calibrate
fcs_ref = FCSFile("monodisperse_reference.fcs")
IFS_ref, ISS_ref, _ = fcs_ref.get_scatter_data()
params = calibrate(IFS_ref, ISS_ref, params, fit_angles=True)
params.to_json("my_instrument.json")
# 4. Assign radii
events = assign_radii(IFS, ISS, params, fl_data=fl)
# 5. Size distribution
dist = size_distribution(events, n_bins=50, weighting="volume")
# 6. Plot
fig, ax = plots.plot_scattering_diagram(events, params=params)
fig2, ax2 = plots.plot_size_distribution(dist)See notebooks/tutorial.ipynb for a complete interactive walkthrough.
| Preset | Instrument | λ (nm) | θ_FS (°) | θ_SS (°) | n_medium | n_particle |
|---|---|---|---|---|---|---|
FACSCalibur |
BD FACSCalibur | 488 | 0.716 – 3.954 | 52.38 | 1.337 | 1.468 |
Accuri_C6 |
BD Accuri C6 | 488 | 0.7 – 29.2 | 29.2 | 1.337 | 1.468 |
Parameters can be saved and reloaded:
params.to_json("my_cytometer.json")
params = InstrumentParams.from_json("my_cytometer.json")Minimises the mean point-to-curve distance between measured and theoretical I_SS(I_FS) using Nelder-Mead. A monodisperse emulsion (e.g. 2 µm mean diameter) is recommended as reference.
params_cal = calibrate(
IFS_ref, ISS_ref, params,
fit_angles = True, # optimise theta_fs_min, theta_fs_max
fit_n_particle = False, # fix n_oil if known
)from cytometry4emulsions import fluorescence_per_area, fluorescence_per_volume
# Surface-localised dye (e.g. labelled lipid) → surface density
ev = fluorescence_per_area(events, fl_column="FL1-H")
# → adds: surface_nm2, fl_per_area
# Encapsulated dye (e.g. Nile Red) → volume concentration
ev = fluorescence_per_volume(events, fl_column="FL1-H")
# → adds: volume_nm3, fl_per_volumecytometry4emulsions/ importable package
__init__.py public API and usage docstring
mie.py Mie S1/S2 functions and detector integrals
instrument.py InstrumentParams dataclass, presets, Nelder-Mead calibration
fcs_io.py FCS 2.0/3.0/3.1 parser with channel auto-detection
analysis.py KD-tree radius assignment, size histograms, FL normalisation
plots.py matplotlib figures
notebooks/
tutorial.ipynb interactive walkthrough with synthetic data
tests/
test_mie.py
test_fcs_io.py
test_analysis.py
data/example/
FACSCalibur_params.json
- Core-shell particles: extend
mie.pywith coated-sphere coefficients (Bohren & Huffman §4.4) for lipid-shelled droplets or polymer capsules. - Multi-instrument presets: Sony, Beckman Coulter geometries.
- GUI wrapper: simple tkinter or panel interface for non-programmers in the group.
@article{fattaccioli2009,
author = {Fattaccioli, Jacques and Baudry, Jean and Henry, Nicolas
and Brochard-Wyart, Françoise and Bibette, Jérôme},
title = {Size and fluorescence measurements of individual droplets
by flow cytometry},
journal = {Soft Matter},
year = {2009},
volume = {5},
pages = {2232--2238},
doi = {10.1039/b814954b}
}