MINFLUX-like simulator
Project description
SimFlux — MINFLUX-like simulator (NPC and DNA-Origami)
Real MINFLUX - and single molecule localisation microscopy at large - datasets rarely have a perfect, known ground truth. This package generates synthetic SMLM datasets representative of NPC-like structures, or of DNA-Origami, with user-customised settings impacting replicating the effects of biolabelling, clutter, or the probability of oligomer chains. SimFlux aims to be explicit about what’s generated and how, so results are reproducible and assumptions are visible. The code focuses on two simple structures that cover a lot of benchmarking needs:
- poly — regular polygons acting as monomers, assembled into linear oligomers with a spacing rule that enforces a single shared interface between neighbors.
- grid — DNA‑origami–style point lattices (
rows × cols) with a fixed node separation, randomly rotated and placed in a field of view.
Both modes share the same measurement model (label assignment, measurement noise, optional clutter) and the same I/O layout (in‑memory return or HDF5 on disk).
The Simulating Process
This section spells out the generation steps and how the parts fit together.
1) Centroids
A centroid anchors each object: either a monomer in a chain (poly) or a whole grid (grid). The number of groups to place is drawn from a Poisson with mean --centroid-mean, then each group attempts placement inside --xrange/--yrange with a hard‑core rule that prevents different groups from getting too close (distance threshold depends on the geometry: 2 × radius for poly, 2 × R for grid). Multiple attempts are made per group up to an internal budget; if no acceptable spot is found, the group is skipped.
2) Geometry per centroid
- poly: for each chain, a polygon type is sampled from
--mix, and an oligomer length (number of monomers) is sampled from--oligomers. Adjacent monomers are spaced so that one full edge is shared; optionally, the two shared vertices are snapped to coincide exactly (--enforce-shared-edge). - grid: a centered
rows × colslattice with node separation--sepis rotated by a random angle, then translated to the grid centroid. The effective grid radiusRis computed from the farthest node offset.
3) Emitters
Every retained polygon vertex (poly) or lattice node (grid) becomes an emitter. Each emitter is independently tagged labelled with probability p (unlabelled otherwise). Two uncertainties can be introduced:
gt_uncertainty(ground‑truth jitter) perturbs the emitter anchors before any measurements are drawn.ms_uncertainty(measurement noise) is used when drawing individual measurement locations.
4) Measurements
Each labelled emitter draws a Poisson number of measurement attempts (measured). Every attempt generates a noisy coordinate by adding Gaussian scatter with standard deviation ms_uncertainty to the emitter location. A Bernoulli filter with probability q keeps or discards each noisy point. Unlabelled emitters do not generate measurements.
5) Clutter (optional)
If clutter_fraction is set, a number of clutter clusters equal to floor(clutter_fraction × N_labelled) is created. Clutter cluster centers are sampled inside the bounding box of the emitters but are kept away from real centroids by an exclusion radius. Each clutter cluster then draws measurements using the same Poisson + Gaussian model (with the same measured and ms_uncertainty), but these points are tagged type="clutter" and have emitter_id = -1.
6) Membrane lifting (optional, on write)
If a callable membrane_function(x, y) is supplied and a filename is given, 2D coordinates are lifted to 3D by appending z = membrane_function(x, y) when writing the HDF5 file. In‑memory results remain 2D unless you explicitly apply the membrane first.
7) Audits & reproducibility
After placement, a quick hard‑core audit checks inter‑group centroid spacing and prints a short report if any pairs violate the rule. All top‑level routines accept a seed and set NumPy’s RNG accordingly inside the function for deterministic runs.
Features
- Oligomer‑aware centroid placement with hard‑core spacing between different groups
- Grid placement with automatic effective radius R
- Shared‑edge enforcement for polygon chains
- Configurable measurement model:
p,q,measured,gt_uncertainty,ms_uncertainty - Optional clutter proportional to the number of labelled emitters
- Optional membrane lifting to 3D on write
- Compact, descriptive auto‑naming of output files
- Plotly 2D quicklook
- pytest test suite
Requirements
- Python ≥ 3.9 (tested with 3.13)
- NumPy ≥ 1.23
- pandas ≥ 1.5
- Plotly ≥ 5.0
- h5py ≥ 3.7
Installation
Create and activate a fresh environment (example with venv):
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
Install in editable mode (recommended for development):
pip install -e .
Optionally, install dev tools:
pip install -e ".[dev]"
Quick verification:
python -c "import simflux, pkgutil; print(simflux.__file__)"
Project layout
simflux/
├─ simflux/ # Python package
│ ├─ __init__.py
│ └─ simflux.py # simulator module (CLI & API)
├─ tests/ # pytest tests
│ └─ tests.py
├─ pyproject.toml
└─ README.md
Run tests from the repository root so the package import resolves cleanly.
Command‑line usage
Global options (e.g. --plot) must appear before the subcommand (poly or grid) due to argparse rules.
python -m simflux.simflux \
--xrange XMIN XMAX \
--yrange YMIN YMAX \
--centroid-mean MU \
[--p P] [--q Q] [--measured LAMBDA] \
[--gt-uncertainty SIG_GT] [--ms-uncertainty SIG_MS] \
[--clutter-fraction FRACTION] \
[--plot] [--seed SEED] \
-o OUT_PREFIX \
{poly|grid} ...
poly‑specific options
poly
--radius R
--mix "polygon:S@W[,polygon:S@W...]"
[--oligomers "K:W[,K:W...]"] # or short names: mono/di/tri/...
[--enforce-shared-edge | --no-enforce-shared-edge]
[--store-edges]
grid‑specific options
grid
--grid ROWS COLS
--sep SEPARATION
Examples
Octagons (poly mode)
Octagonal monomers, monomer‑only (no oligomers), modest noise, write HDF5 and show a 2D plot.
python -m simflux.simflux \
--xrange 0 80 \
--yrange 0 80 \
--centroid-mean 6 \
--p 0.8 \
--q 0.9 \
--measured 5 \
--gt-uncertainty 0.05 \
--ms-uncertainty 0.5 \
--clutter-fraction 0.1 \
--plot \
--seed 42 \
-o out/sim \
poly \
--radius 3.5 \
--mix "polygon:8@1.0" \
--oligomers "mono:1.0" \
--enforce-shared-edge
This produces a file named like:
out/sim_poly-8_olig-mono1_p-0.8_q-0.9_cl-0.1_mu-6_R-3.5_gt-0.05_ms-0.5_SE_seed-42.h5
The 2D plot shows centroids, emitter anchors, observed points, and clutter (if any).
3×3 DNA‑origami grid (grid mode)
A 3×3 lattice with 10‑unit separation, moderate noise.
python -m simflux.simflux \
--xrange 0 120 \
--yrange 0 120 \
--centroid-mean 4 \
--p 0.75 \
--q 0.8 \
--measured 6 \
--gt-uncertainty 0.05 \
--ms-uncertainty 0.4 \
--clutter-fraction 0.05 \
--plot \
--seed 99 \
-o out/grid \
grid \
--grid 3 3 \
--sep 10
The R term in the filename is the automatically computed effective radius of the grid.
Python API quickstart
from simflux.simflux import (
simulate_poly, simulate_grid, plot2d,
parse_mix, parse_oligomers
)
# Poly: octagons
geom_mix = parse_mix("polygon:8@1.0")
oligs = parse_oligomers("mono:1.0")
poly_data = simulate_poly(
filename=None,
xrange=(0, 80), yrange=(0, 80),
centroid_mean_groups=6,
radius=3.5,
geom_mix=geom_mix,
oligomer_pmf=oligs,
p=0.8, q=0.9, measured=5,
gt_uncertainty=0.05, ms_uncertainty=0.5,
clutter_fraction=0.1,
membrane_function=None,
enforce_shared_edge=True,
store_edges=False,
seed=42,
)
plot2d(data=poly_data)
# Grid: 3×3
grid_data = simulate_grid(
filename=None,
xrange=(0, 120), yrange=(0, 120),
centroid_mean_groups=4,
rows=3, cols=3, sep=10,
p=0.75, q=0.8, measured=6,
gt_uncertainty=0.05, ms_uncertainty=0.4,
clutter_fraction=0.05,
membrane_function=None,
seed=99,
)
plot2d(data=grid_data)
Output data model (HDF5 schema)
When filename is provided, the following groups/datasets are written. In‑memory results mirror the same structure (positions are 2D unless a membrane is applied prior to write).
/centroid
position (C, 3) float # x,y[,z]; z=0 if no membrane
id (C,) int32
group_id (C,) int32 # chain/grid id
[poly only]
group_index (C,) int32 # index within chain
group_size (C,) int32
sides (C,) int32 # polygon sides
theta (C,) float # monomer orientation
[grid only]
theta (C,) float # grid rotation angle
rows, cols (C,) int32
sep, R (C,) float
/ emitter
position (E, 3) float
id (E,) int32
centroid_id (E,) int32
type (E,) string # "labelled" or "unlabelled"
/ observed
position (M, 3) float
emitter_id (M,) int32
centroid_id (M,) int32
/ clutter
position (K, 3) float
emitter_id (K,) int32 # always -1
type (K,) string # "clutter"
/ edges [poly, optional]
(P, 2) int32 # emitter id pairs per monomer if --store-edges
Reproducibility and seeding
simulate_poly,simulate_grid, and the centroid generators acceptseedand reseed NumPy’s RNG inside the function. Two calls with the same arguments and the sameseedproduce identical results.- For tests that need deterministic outputs, pass a
seedto the call. Global RNG state in the calling code remains predictable across separate runs.
Testing
Run everything:
pytest -q
Common issues:
- String arrays (e.g.,
"labelled") should be compared with exact equality, not numeric closeness. - When running tests from an IDE, point the working directory to the repository root or ensure
pip install -e .has been done in the active environment.
Troubleshooting
- Global flags after subcommand: argparse won’t see them. Put flags like
--plotbeforepoly/grid. - Too few centroids placed: widen the field of view, reduce the hard‑core scale (smaller
--radiusfor poly or--sepfor grid), or reduce--centroid-mean. - Editable install:
pip install -e .. Runningpip install -ewithout a path is an error.
License
MIT License — include a plain‑text LICENCE file at the repository root. The pyproject.toml already declares the license.
A note on scope
This is a simulator for shape‑aware point patterns under a simple measurement model. It is not a physical fluorophore simulator, a photon budget model, or a microscope aberration engine. The goal is fast, controllable synthetic data for algorithm development and validation.
Future work shelved for future consideration includes time-series like spawned emitters to replicate STORM data, and a manual input scheme that accepts coordinates to build the structures. Presets for common structures appearing across SMLM are in the works, such as the 3-dimensional, double layer 8-fold symmetry of Nup96-SNAP.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simflux-0.1.2.tar.gz.
File metadata
- Download URL: simflux-0.1.2.tar.gz
- Upload date:
- Size: 30.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
498712d3aa3ccbefb2eea23a10002e4252d42a07c20a93b009aab8d4e99faa67
|
|
| MD5 |
ae9431ba519f09991d01a7c0d241c77b
|
|
| BLAKE2b-256 |
cb8b8d47b4b4512b41118b1034c106563672ad225050351e38db737a482e80d1
|
File details
Details for the file simflux-0.1.2-py3-none-any.whl.
File metadata
- Download URL: simflux-0.1.2-py3-none-any.whl
- Upload date:
- Size: 22.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bfd95c8c70e42fd7dae0af26f37c30562ac5c6053f2d27ccb7f9e4755b7165c4
|
|
| MD5 |
5deeb44e689a4839df5e60c382cbaf2c
|
|
| BLAKE2b-256 |
db8d6c445cb88d81b322bcaf476c7bcc817f510473ee5d68a5e38aa6c24c6185
|