MINFLUX-like simulator
Project description
SimFlux — MINFLUX-like simulator (poly & grid)
SimFlux generates synthetic single-molecule localization data in two modes:
- poly — polygonal monomers arranged into linear oligomers under a hard-core constraint, with optional shared-edge snapping between adjacent monomers.
- grid — DNA-origami–style point grids (rows × cols) with fixed node separation, placed as randomly rotated lattices around sampled centroids.
Both modes:
- assign labelled / unlabelled emitters (probability
p) - emit measurements (Poisson count with Gaussian noise, per-measurement signal probability
q) - optionally add spurious clutter clusters
- can write HDF5 datasets and render a 2D Plotly overview
Table of contents
- Features
- Requirements
- Installation
- Project layout
- Command-line usage
- Python API quickstart
- Output data model (HDF5 schema)
- Parameters and behavior
- Reproducibility & seeding
- Testing
- Troubleshooting
- Licence
Features
- Oligomer-aware centroid placement (linear chains) with hard-core spacing
- Grid placement with automatic effective radius R
- Shared-edge enforcement to keep the two vertices on a shared interface coincident
- Configurable measurement physics: label probability
p, signal probabilityq, Poisson meanmeasured, ground-truth jittergt_uncertainty, measurement noisems_uncertainty - Optional clutter clusters proportional to the number of labelled emitters
- Membrane lifting API to produce 3D coordinates from 2D inputs
- 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 virtual environment (example with venv):
python -m venv .venv
source .venv/bin/activate # on Windows: .venv\\Scripts\\activate
Install SimFlux in editable mode and the runtime dependencies:
pip install -e .
# or, when using a requirements.txt alongside:
pip install -r requirements.txt
Verification:
python -c "import simflux, pkgutil; print(simflux.__file__)"
Project layout:
simflux/
├─ simflux/ # Python package
│ ├─ __init__.py
│ └─ simflux.py # the simulator module (CLI & API)
├─ tests/ # pytest tests
│ └─ tests.py
├─ pyproject.toml
├─ requirements.txt # optional
└─ README.md
Running tests from the repository root resolves imports cleanly.
Command-line usage
All global options (e.g., --plot) 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
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
grid
--grid ROWS COLS
--sep SEPARATION
poly mode example
Simulate octagonal monomers (8-gons), monomer-only (no multi-mers), modest noise, and write an HDF5 plus a quick 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
What gets produced
-
HDF5 file whose name encodes key settings, e.g.:
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 -
A Plotly window with centroids, emitters, observed points, and clutter.
grid mode example
Simulate 3 rows × 3 cols lattice, node separation 10 units, 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
Resulting file name looks like:
out/grid_grid-3x3_sep-10_p-0.75_q-0.8_cl-0.05_mu-4_R-14.142_gt-0.05_ms-0.4_seed-99.h5
(The effective radius R is computed from the grid geometry.)
Python API quickstart
All functionality is available programmatically.
import numpy as np
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, # keep in memory (no HDF5)
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: 3x3
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)
Membrane lifting (optional 3D)
def gentle_bump(x, y):
# Small smooth height field
return 0.02 * ((x - x.mean())**2 + (y - y.mean())**2)
# Example: write poly output with membrane-applied positions
simulate_poly(..., filename="with_membrane.h5", membrane_function=gentle_bump)
Output data model (HDF5 schema)
When filename is provided, groups and datasets follow this schema:
/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
[grid only]
theta (C,) float # 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 pairs of emitter ids if --store-edges
In-memory returns mirror the same structure, but positions are (N, 2) unless a membrane is applied before writing.
Parameters and behavior
p— probability an emitter is labelled. Unlabelled emitters never generate observed measurements.measured— Poisson mean for the number of measurement attempts per labelled emitter (or per clutter cluster).q— probability that a generated measurement is kept as signal. Acts as an acceptance filter after drawing noisy positions.gt_uncertainty— Gaussian jitter applied to ground-truth nodes/vertices (absolute units).ms_uncertainty— Gaussian noise applied to measurement positions (absolute units).clutter_fraction— number of clutter clusters isfloor(fraction × N_labelled_emitters).Nonedisables clutter entirely;0.0enables with zero clusters (no effect).- Hard-core spacing:
- poly: centroid placement audited at distance
< 2 × radiusbetween different chains - grid: centroid placement audited at distance
< 2 × Rbetween different grids
- poly: centroid placement audited at distance
enforce_shared_edge(poly) — for adjacent monomers in a chain, keeps exactly two shared vertices coincident and drops duplicates along the shared edge.store_edges(poly) — emits all pairwise edges among kept vertices per monomer; quadratic cost; disabled by default.- Membrane function — callable
f(x, y) -> zapplied to positions on write; shape must match point count; omitted raises a shape error.
Reproducibility & seeding
- Each top-level routine (
simulate_poly,simulate_grid, and centroid generators) acceptsseed; when provided,np.random.seed(seed)is set inside the routine for deterministic runs. - For deterministic testing, pass a
seedto the simulation call. Global RNG state is otherwise unaffected across separate runs in typical usage.
Testing
Run all tests:
pytest -q
Run a subset:
pytest tests -k simulate_poly -q
Note on NumPy 2.x: comparing string arrays with np.testing.assert_allclose now raises a dtype error. Tests here use exact string comparisons for type fields and numeric comparisons for coordinates and ids.
Troubleshooting
Global flags before subcommand
argparserequires global flags beforepolyorgrid. Example:- ✅
... --plot poly --radius 5 --mix "polygon:8@1" - ❌
... poly --radius 5 --mix "polygon:8@1" --plot
- ✅
Editable install
pip install -e .must include the path argument (.). Running justpip install -etriggers an error: “-e option requires 1 argument”.
Tests not finding the package
- Execute
pytestfrom the repository root afterpip install -e ..
NumPy DTypePromotionError in tests
- Occurs when
assert_allcloseis applied to unicode arrays (e.g.,"labelled"/"unlabelled"). Useassert_array_equalfor strings.
Hard-core placement yields too few centroids
- Increase the sampling window (
--xrange/--yrange), reduce--centroid-mean, reduce polygon--radius(poly) or grid--sep/R(grid), or raisemax_group_attemptsinside the code if needed.
Licence
Released under the MIT License.
Copyright © 2025 Jack Peyton.
See LICENCE for the full text.
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.0.tar.gz.
File metadata
- Download URL: simflux-0.1.0.tar.gz
- Upload date:
- Size: 29.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7e9d4fe6ff54aabb86be33d0dca6289a37bb0a9ac13946d91c85da3ffecdfd66
|
|
| MD5 |
176ee438dd2423230cfd35a839201c53
|
|
| BLAKE2b-256 |
4d07d4419398a7b8139684327bc26777d6eb4aab6bc5641a3ae0a004c1003bba
|
File details
Details for the file simflux-0.1.0-py3-none-any.whl.
File metadata
- Download URL: simflux-0.1.0-py3-none-any.whl
- Upload date:
- Size: 22.2 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 |
bc2a4a2b747576ac4185b42404ecae322a3002a31af3da9033ca0cba66d75294
|
|
| MD5 |
55553a8c1d382700ad9c23e9e5011bc0
|
|
| BLAKE2b-256 |
b4022262c2e15f6ed6b01ae203d6c41adbf637c36804d9e27dde3f79a59d40e5
|