API Reference
This page provides a comprehensive reference for the types and functions in SMLMSim.
SMLMSim.SMLMSim — Module
SMLMSimMain module for the SMLMSim.jl package.
API Overview
For a comprehensive overview of the API, use the help mode on api:
?apiOr access the complete API documentation programmatically:
docs = SMLMSim.api()This package provides tools for simulating Single Molecule Localization Microscopy (SMLM) data. It includes modules for:
- Core: Fundamental types (molecules, patterns) and photophysics simulation (CTMC, blinking).
- StaticSMLM: Simulating static emitters with blinking and localization noise.
- InteractionDiffusion: Simulating diffusing and interacting emitters (e.g., dimerization) using Smoluchowski dynamics.
- CameraImages: Generating simulated camera images from emitter data, including noise models.
The main SMLMSim module re-exports key types and functions from these submodules to provide a unified user interface.
Usage
using SMLMSim
# Example: Static simulation
params_static = StaticSMLMConfig(density=1.0, σ_psf=0.13)
smld_noisy, info = simulate(params_static)
# Access intermediate results: info.smld_true, info.smld_model
# Example: Diffusion simulation
params_diff = DiffusionSMLMConfig(density=0.5, diff_monomer=0.1)
smld, info = simulate(params_diff)
# Example: Generate images
psf = GaussianPSF(0.15)
images, img_info = gen_images(smld_noisy, psf)SMLMSim.ImageInfo — Type
ImageInfoMetadata from image generation functions.
Fields
elapsed_s::Float64: Wall-clock time for image generation in secondsbackend::Symbol: Computation backend (:cpu)device_id::Int: Device identifier (-1 for CPU)frames_generated::Int: Number of frames generatedn_photons_total::Float64: Total photon count across all framesoutput_size::Tuple{Int,Int,Int}: Image dimensions (height, width, n_frames)
Example
images, info = gen_images(smld, psf)
println("Generated $(info.frames_generated) frames in $(info.elapsed_s) seconds")
println("Total photons: $(info.n_photons_total)")SMLMSim.SimInfo — Type
SimInfoMetadata and intermediate results from simulation functions.
Fields
elapsed_s::Float64: Wall-clock time for simulation in secondsbackend::Symbol: Computation backend (:cpu)device_id::Int: Device identifier (-1 for CPU)seed::Union{UInt64, Nothing}: RNG seed for reproducibility (if applicable)smld_true::Union{BasicSMLD, Nothing}: Ground truth positions (static simulation only)smld_model::Union{BasicSMLD, Nothing}: After kinetic model (static simulation only)n_patterns::Int: Number of spatial patterns generatedn_emitters::Int: Total emitters simulatedn_localizations::Int: Total localizations generatedn_frames::Int: Number of frames in simulation
Example
smld_noisy, info = simulate(params)
println("Simulation took $(info.elapsed_s) seconds")
println("Generated $(info.n_localizations) localizations from $(info.n_emitters) emitters")
# Access intermediate results for analysis
if info.smld_true !== nothing
# Compare true vs noisy positions
endSMLMSim.api — Method
SMLMSim.jl API Overview
SMLMSim is a Julia package for simulating Single Molecule Localization Microscopy (SMLM) data with realistic physical properties. It provides tools for generating static SMLM simulations, diffusion-interaction simulations, and microscope image generation.
Key Concepts
Physical Units
All simulations use consistent physical units:
- Spatial dimensions: microns (μm)
- Time: seconds (s)
- Diffusion coefficients: μm²/s
- Rate constants: s⁻¹
Core Components
- Patterns: Spatial arrangements of molecules (e.g., oligomers, lines)
- Molecules: Photophysical models of fluorophores with state transitions
- Simulation: Parameters and functions for different simulation types
- Noise: Realistic localization uncertainty based on photon statistics
- Camera Images: Generation of microscope images from simulation data
Type Hierarchy
AbstractSMLMConfig: Base type for all config types (from SMLMData)SMLMSimParams: Base type for simulation parametersStaticSMLMConfig: Parameters for static SMLM simulationDiffusionSMLMConfig: Parameters for diffusion simulation
AbstractSMLMInfo: Base type for info structs (from SMLMData)SimInfo: Metadata and intermediate results from simulation functionsImageInfo: Metadata from image generation functions
Pattern: Base type for all molecular patternsPattern2D: Base type for 2D patternsNmer2D: N molecules arranged in a circleLine2D: Molecules arranged along a line
Pattern3D: Base type for 3D patternsNmer3D: N molecules arranged in a circle in 3DLine3D: Molecules arranged along a 3D line
AbstractLabeling: Base type for labeling strategiesFixedLabeling: Deterministic number of fluorophores per sitePoissonLabeling: Poisson-distributed number of fluorophores per siteBinomialLabeling: Binomial-distributed number of fluorophores per site
Molecule: Base type for all photophysical modelsGenericFluor: General fluorophore with kinetic state model
AbstractDiffusingEmitter: Base type for diffusing emittersDiffusingEmitter2D: 2D emitter with diffusion stateDiffusingEmitter3D: 3D emitter with diffusion state
Essential Types
StaticSMLMConfig
Parameters for static SMLM simulation with fixed molecular patterns.
Base.@kwdef mutable struct StaticSMLMConfig <: SMLMSimParams
density::Float64 = 1.0 # density in particles per square micron
σ_psf::Float64 = 0.13 # PSF width in microns
minphotons::Int = 50 # minimum photons for detection
ndatasets::Int = 10 # number of datasets to simulate
nframes::Int = 1000 # number of frames per dataset
framerate::Float64 = 50.0 # frames per second
ndims::Int = 2 # dimensionality (2 or 3)
zrange::Vector{Float64} = [-1.0, 1.0] # axial range for 3D simulations [min_z, max_z]
endDiffusionSMLMConfig
Parameters for diffusion-based SMLM simulation using Smoluchowski dynamics.
Base.@kwdef mutable struct DiffusionSMLMConfig <: SMLMSimParams
density::Float64 = 1.0 # number density (molecules/μm²)
box_size::Float64 = 10.0 # simulation box size (μm)
diff_monomer::Float64 = 0.1 # monomer diffusion coefficient (μm²/s)
diff_dimer::Float64 = 0.05 # dimer diffusion coefficient (μm²/s)
diff_dimer_rot::Float64 = 0.5 # dimer rotational diffusion coefficient (rad²/s)
k_off::Float64 = 0.2 # dimer dissociation rate (s⁻¹)
r_react::Float64 = 0.01 # reaction radius (μm)
d_dimer::Float64 = 0.05 # monomer separation in dimer (μm)
dt::Float64 = 0.01 # time step (s)
t_max::Float64 = 10.0 # total simulation time (s)
ndims::Int = 2 # number of dimensions (2 or 3)
boundary::String = "periodic" # boundary condition type ("periodic" or "reflecting")
camera_framerate::Float64 = 10.0 # camera frames per second (Hz)
camera_exposure::Float64 = 0.1 # camera exposure time per frame (s)
endMolecular Patterns
Nmer2D
N molecules symmetrically organized around a circle with diameter d.
mutable struct Nmer2D <: Pattern2D
n::Int # Number of molecules in the pattern
d::Float64 # Diameter of the circle in microns
x::Vector{Float64} # X positions of molecules in microns
y::Vector{Float64} # Y positions of molecules in microns
endLine2D
Points with uniform random distribution between two endpoints.
mutable struct Line2D <: Pattern2D
n::Int # Number of molecules in the pattern
x::Vector{Float64} # X positions of molecules in microns
y::Vector{Float64} # Y positions of molecules in microns
λ::Float64 # Linear molecule density (molecules per micron)
endpoints::Vector{Tuple{Float64,Float64}} # Vector of endpoint coordinates
endInfo Types
SimInfo
Metadata and intermediate results from simulation functions. Returned as the second element of the tuple from simulate().
struct SimInfo <: AbstractSMLMInfo
elapsed_s::Float64 # Wall-clock time in seconds
backend::Symbol # Computation backend (:cpu)
device_id::Int # Device identifier (-1 for CPU)
seed::Union{UInt64, Nothing} # RNG seed for reproducibility
smld_true::Any # Ground truth positions (static only)
smld_model::Any # After kinetic model (static only)
n_patterns::Int # Number of spatial patterns
n_emitters::Int # Total emitters simulated
n_localizations::Int # Total localizations generated
n_frames::Int # Number of frames
endImageInfo
Metadata from image generation functions. Returned as the second element of the tuple from gen_images() and gen_image().
struct ImageInfo <: AbstractSMLMInfo
elapsed_s::Float64 # Wall-clock time in seconds
backend::Symbol # Computation backend (:cpu)
device_id::Int # Device identifier (-1 for CPU)
frames_generated::Int # Number of frames generated
n_photons_total::Float64 # Total photon count
output_size::Tuple{Int,Int,Int} # Image dimensions (H, W, T)
endFluorophore Models
GenericFluor
Defines a fluorophore with photophysical properties.
struct GenericFluor <: Molecule
γ::AbstractFloat # Photon emission rate in Hz
q::Array{<:AbstractFloat} # Rate matrix for state transitions
endLabeling Strategies
Labeling is separate from photophysics (Molecule). These types control how many fluorophores attach to each binding site, while Molecule handles blinking kinetics of each fluorophore.
AbstractLabeling
Abstract type for labeling strategies. All implementations support an efficiency parameter controlling the probability that a binding site gets labeled at all.
FixedLabeling
Deterministic labeling with exactly n fluorophores per site.
struct FixedLabeling <: AbstractLabeling
n::Int # Number of fluorophores per site
efficiency::Float64 # Probability that a site gets labeled (0 to 1)
endPoissonLabeling
Poisson-distributed number of fluorophores per site.
struct PoissonLabeling <: AbstractLabeling
mean::Float64 # Mean number of fluorophores per site (λ for Poisson)
efficiency::Float64 # Probability that a site gets labeled (0 to 1)
endNote: With Poisson statistics, some sites may receive 0 fluorophores even when efficiency=1.0 (especially for small mean values).
BinomialLabeling
Binomial-distributed number of fluorophores per site. Models scenarios where each binding site has n potential attachment points, each occupied with probability p.
struct BinomialLabeling <: AbstractLabeling
n::Int # Number of potential attachment points per site
p::Float64 # Probability each attachment point is occupied (0 to 1)
efficiency::Float64 # Probability that a site gets labeled (0 to 1)
endConstructor Examples
Creating Simulation Parameters
# Static SMLM with default parameters
params_static = StaticSMLMConfig()
# Custom parameters for static simulation
params_static = StaticSMLMConfig(
density = 2.0, # 2 patterns per μm²
σ_psf = 0.15, # 150nm PSF width
nframes = 2000, # 2000 frames
framerate = 20.0 # 20 fps
)
# Diffusion simulation with default parameters
params_diff = DiffusionSMLMConfig()
# Custom parameters for diffusion simulation
params_diff = DiffusionSMLMConfig(
density = 0.5, # molecules per μm²
box_size = 15.0, # 15μm box size
diff_monomer = 0.2, # 0.2 μm²/s diffusion coefficient
k_off = 0.1, # 0.1 s⁻¹ dissociation rate
t_max = 20.0 # 20s simulation time
)Creating Patterns
# Create an 8-molecule pattern with 100nm diameter (default)
nmer = Nmer2D()
# Create a custom pattern with 6 molecules and 200nm diameter
hexamer = Nmer2D(n=6, d=0.2) # d is in microns
# Create a 3D pattern
octamer3d = Nmer3D(n=8, d=0.15)
# Create a line pattern
line = Line2D(λ=5.0, endpoints=[(-2.0, 0.0), (2.0, 0.0)]) # 5 molecules/μm
# Create a 3D line pattern
line3d = Line3D(λ=8.0, endpoints=[(-1.0, 0.0, -0.5), (1.0, 0.0, 0.5)])Creating Fluorophore Models
# Create a fluorophore with default parameters
fluor = GenericFluor()
# Create a fluorophore with custom parameters
# γ=100,000 photons/s, k_off=10 Hz, k_on=0.1 Hz
fluor = GenericFluor(1e5, [-10.0 10.0; 0.1 -0.1])
# Create a fluorophore using the 2-state keyword constructor
fluor = GenericFluor(photons=1e5, k_off=50.0, k_on=1e-2)Creating Labeling Strategies
# Perfect labeling - exactly 1 fluorophore per site (default)
labeling = FixedLabeling()
# 2 fluorophores per site, 90% of sites labeled
labeling = FixedLabeling(2; efficiency=0.9)
# Poisson labeling - average 1.5 fluorophores per site
labeling = PoissonLabeling(1.5)
# Poisson with 80% labeling efficiency
labeling = PoissonLabeling(2.0; efficiency=0.8)
# Binomial labeling - antibody with 4 dye attachment points, 80% occupancy
labeling = BinomialLabeling(4, 0.8)
# Same with 90% of sites receiving an antibody
labeling = BinomialLabeling(4, 0.8; efficiency=0.9)Creating a Camera
# IdealCamera: Poisson noise only
camera = IdealCamera(128, 128, 0.1) # 128×128 pixels, 100nm pixels
# Specify field of view with an array of pixel edges
pixel_edges_x = 0.0:0.1:12.8 # 0 to 12.8μm in 0.1μm steps
pixel_edges_y = 0.0:0.1:12.8
camera = IdealCamera(pixel_edges_x, pixel_edges_y)
# SCMOSCamera: Realistic noise model with per-pixel calibration (SMLMData 0.4+)
# Parameters: width, height, pixel_size, readnoise
camera_scmos = SCMOSCamera(128, 128, 0.1, 1.6) # 1.6 e⁻ RMS read noise
# Advanced: Specify all calibration parameters
# offset (ADU), gain (e⁻/ADU), readnoise (e⁻ RMS), quantum efficiency (0-1)
camera_scmos = SCMOSCamera(
128, 128, 0.1;
offset=100.0, # 100 ADU dark level
gain=0.5, # 0.5 e⁻/ADU
readnoise=1.6, # 1.6 e⁻ RMS
qe=0.95 # 95% quantum efficiency
)Core Functions
Simulation
simulate
The main simulation function with multiple methods for different simulation types. All simulate() methods return a tuple: (primary_output, info).
# Static SMLM simulation
# First create simulation parameters
params = StaticSMLMConfig()
# Then run simulation - returns (smld_noisy, SimInfo)
smld_noisy, info = simulate(
params;
pattern=Nmer2D(),
labeling=FixedLabeling(), # Default: 1 fluorophore per site
molecule=GenericFluor(),
camera=IdealCamera(128, 128, 0.1)
)
# Access intermediate results from info
smld_true = info.smld_true # Ground truth positions
smld_model = info.smld_model # After kinetic model
# With Poisson labeling (variable fluorophores per site)
smld_noisy, info = simulate(
params;
pattern=Nmer2D(n=6, d=0.2),
labeling=PoissonLabeling(1.5), # Average 1.5 fluorophores per site
molecule=GenericFluor()
)
# Diffusion simulation
# First create simulation parameters
params_diff = DiffusionSMLMConfig()
# Then run simulation - returns (smld, SimInfo)
smld, info = simulate(
params_diff;
photons=1000.0
)
# Check timing
println("Simulation took $(info.elapsed_s) seconds")kinetic_model
Generate kinetic blinking model from existing localization data.
# Example of how to call kinetic_model
# First create or obtain the required inputs
smld_true = ... # Some BasicSMLD with true positions
fluor = GenericFluor(1e5, [-10.0 10.0; 0.5 -0.5]) # Fluorophore model
nframes = 1000 # Number of frames
framerate = 50.0 # Frames per second
# Call the function
smld_model = kinetic_model(
smld_true, # BasicSMLD with emitter positions
fluor, # Molecule with kinetic rates
nframes, # Number of frames
framerate; # Frames per second
ndatasets=1, # Number of independent datasets
minphotons=50.0, # Minimum photons for detection
state1=:equilibrium # Initial state sampling
)apply_noise
Add localization uncertainty to emitter positions based on photon counts.
# Example usage of apply_noise
# First, you need smld_model from a previous step
# For example, from the output of kinetic_model()
# For 2D emitters - add noise with 130nm PSF width
smld_noisy = apply_noise(smld_model, 0.13) # 0.13 μm = 130nm PSF width
# For 3D emitters - specify PSF width in each dimension
smld_noisy_3d = apply_noise(smld_model_3d, [0.13, 0.13, 0.39]) # [x, y, z] widths in μmImage Generation
gen_images
Generate camera images from SMLD data using the specified PSF model. Returns a tuple: (images, ImageInfo).
images, info = gen_images(
smld::SMLD,
psf::AbstractPSF;
dataset::Int=1, # Dataset number to use from SMLD
frames=nothing, # Specific frames to generate (default: all frames)
support=Inf, # PSF support region size (see details below)
sampling=2, # Supersampling factor for PSF integration
threaded=true, # Enable multithreading
bg=0.0, # Background signal level (photons per pixel)
poisson_noise=false, # Apply Poisson noise only (simple shot noise)
camera_noise=false # Apply full camera noise model (requires SCMOSCamera)
# - For SCMOSCamera: QE, Poisson, read noise, gain, offset
# - For IdealCamera: ignored (use poisson_noise instead)
)
# Access image metadata
println("Generated $(info.frames_generated) frames in $(info.elapsed_s) seconds")
println("Total photons: $(info.n_photons_total)")
# The support parameter controls PSF computation region:
# 1. Inf (default): Compute PSF over entire image (most accurate but slowest)
support=Inf
# 2. Real number: Use circular region with given radius around each emitter
# Typically 3-5× the PSF width is sufficient for accuracy with better performance
support=1.0 # 1.0 µm radius around each emitter
# 3. Tuple (xmin, xmax, ymin, ymax): Explicit region in microns
support=(4.0, 6.0, 4.0, 6.0) # Only compute PSF within this region
# Example: sCMOS camera with realistic noise
camera_scmos = SCMOSCamera(128, 128, 0.1, 1.6)
smld = BasicSMLD(emitters, camera_scmos, n_frames, n_datasets)
images_scmos, info = gen_images(smld, psf, bg=10.0, camera_noise=true)
# Applies: QE → Poisson → read noise → gain → offset
# Example: IdealCamera with Poisson noise only
camera_ideal = IdealCamera(128, 128, 0.1)
smld = BasicSMLD(emitters, camera_ideal, n_frames, n_datasets)
images_poisson, info = gen_images(smld, psf, bg=10.0, poisson_noise=true)gen_image
Generate a single frame camera image. Returns a tuple: (image, ImageInfo).
# Example of generating a single frame image
# First, define variables
smld = ... # Your SMLD data
psf = GaussianPSF(0.15) # PSF model with 150nm width
frame_number = 10 # The frame you want to generate
# Generate image for a specific frame
single_frame, info = gen_image(
smld, # SMLD data
psf, # PSF model
frame_number; # Frame to generate
support=1.0, # Same keyword arguments as gen_images
bg=5.0,
poisson_noise=true
)Analysis Functions
Diffusion Analysis
# Example usage of diffusion analysis functions
# First, run a diffusion simulation
params = DiffusionSMLMConfig()
smld, info = simulate(params)
# Extract dimers from diffusion simulation
dimer_smld = get_dimers(smld)
# Extract monomers
monomer_smld = get_monomers(smld)
# Analyze dimer formation over time
frames, fractions = analyze_dimer_fraction(smld)
# Analyze average dimer lifetime
lifetime = analyze_dimer_lifetime(smld)
# Track state changes over time
state_history = track_state_changes(smld)Track Utilities
# Example usage of track utilities
# First, run a simulation
params = StaticSMLMConfig()
smld_noisy, info = simulate(params)
# Specify a track ID to extract
track_id = 1 # ID of the track to extract
# Get a specific track by ID
track_smld = get_track(smld_noisy, track_id)
# Get number of unique tracks
n_tracks = get_num_tracks(smld_noisy)
# Get all tracks as separate SMLDs
track_smlds = get_tracks(smld_noisy)Pattern Manipulation
# Example usage of pattern manipulation
# Create patterns
pattern2d = Nmer2D(n=6, d=0.2)
pattern3d = Nmer3D(n=8, d=0.15)
# Rotate a 2D pattern by 45 degrees
rotate!(pattern2d, π/4)
# Rotate a 3D pattern with Euler angles (ZYZ convention)
rotate!(pattern3d, π/4, π/6, π/3) # α, β, γ angles
# Rotate a 3D pattern with a rotation matrix
θ = π/2 # 90 degrees
R = [cos(θ) -sin(θ) 0; sin(θ) cos(θ) 0; 0 0 1] # Z-axis rotation
rotate!(pattern3d, R)Labeling Functions
apply_labeling
Apply labeling strategy to binding site coordinates, expanding each site to the appropriate number of fluorophore positions while preserving pattern IDs.
# Generate binding site coordinates with pattern IDs from pattern distribution
x, y, pattern_ids = uniform2D(1.0, Nmer2D(), 10.0, 10.0)
# Apply labeling with pattern ID tracking (returns coords tuple and new IDs)
(x_labeled, y_labeled), new_ids = apply_labeling((x, y), pattern_ids, PoissonLabeling(1.5))
# Backward-compatible: without pattern IDs (returns only coords)
x_labeled, y_labeled = apply_labeling((x, y), FixedLabeling(1; efficiency=0.8))
# Works with 3D coordinates too
x, y, z, pattern_ids = uniform3D(1.0, Nmer3D(), 10.0, 10.0)
(x_labeled, y_labeled, z_labeled), new_ids = apply_labeling((x, y, z), pattern_ids, BinomialLabeling(4, 0.8))n_fluorophores
Sample the number of fluorophores to place at a single binding site. Called internally by apply_labeling, but can be used directly for custom workflows.
labeling = PoissonLabeling(1.5)
# Sample number of fluorophores for one site
n = n_fluorophores(labeling) # Returns Int (can be 0)Pattern Distribution
Generate random pattern distributions in a field. Each function returns coordinates plus pattern instance IDs for tracking which emitters belong to the same pattern.
# Create patterns
pattern2d = Nmer2D(n=6, d=0.2)
pattern3d = Nmer3D(n=8, d=0.15)
# Generate random pattern distribution in a field
field_x = 10.0 # μm
field_y = 10.0 # μm
density = 1.0 # patterns per μm²
# Get coordinates for 2D distribution (returns pattern IDs)
x, y, pattern_ids = uniform2D(density, pattern2d, field_x, field_y)
# pattern_ids[i] indicates which pattern instance point i belongs to
# Get coordinates for 3D distribution (returns pattern IDs)
x, y, z, pattern_ids = uniform3D(density, pattern3d, field_x, field_y, zrange=[-2.0, 2.0])Common Workflows
Static SMLM Simulation Workflow
- Define simulation parameters
- Create a pattern (or use default)
- Choose a labeling strategy (or use default FixedLabeling)
- Define a fluorophore model (or use default)
- Run simulation to get noisy localizations and info (with intermediate results)
- Generate microscope images or analyze the data
# 1. Define parameters
params = StaticSMLMConfig(
density = 1.0,
σ_psf = 0.13,
nframes = 1000
)
# 2. Create a pattern
pattern = Nmer2D(n=6, d=0.2) # hexamer with 200nm diameter
# 3. Choose labeling strategy
labeling = PoissonLabeling(1.5) # Average 1.5 fluorophores per binding site
# 4. Define fluorophore model
fluor = GenericFluor(photons=1e5, k_off=50.0, k_on=1e-2)
# 5. Run simulation - returns (smld_noisy, SimInfo)
smld_noisy, info = simulate(
params;
pattern=pattern,
labeling=labeling,
molecule=fluor
)
# Access intermediate results from info
smld_true = info.smld_true # Ground truth positions
smld_model = info.smld_model # After kinetic model
# 6. Create microscope images with efficient PSF support
psf = GaussianPSF(0.15) # 150nm PSF width
images, img_info = gen_images(smld_model, psf;
support=1.0, # 1.0 μm radius around each emitter
poisson_noise=true # Add realistic photon counting noise
)Diffusion Simulation Workflow
- Define diffusion parameters
- Run simulation to get emitter trajectories
- Analyze the diffusion and interaction dynamics
- Generate microscope images
# 1. Define parameters
params = DiffusionSMLMConfig(
density = 0.5, # molecules per μm²
box_size = 10.0, # μm
diff_monomer = 0.1, # μm²/s
diff_dimer = 0.05, # μm²/s
k_off = 0.2, # s⁻¹
t_max = 10.0 # s
)
# 2. Run simulation - returns (smld, SimInfo)
smld, info = simulate(params)
# 3. Analyze the results
dimer_smld = get_dimers(smld)
frames, fractions = analyze_dimer_fraction(smld)
# 4. Generate microscope images with realistic sCMOS noise
# Create sCMOS camera matching simulation box
n_pixels = Int(ceil(params.box_size / 0.1)) # 0.1 μm pixels
camera_scmos = SCMOSCamera(n_pixels, n_pixels, 0.1, 1.6)
smld_cam = BasicSMLD(smld.emitters, camera_scmos, smld.n_frames, 1)
psf = GaussianPSF(0.15) # 150nm PSF width
images, img_info = gen_images(smld_cam, psf;
support=1.0, # 1.0 μm PSF support radius (faster)
bg=5.0, # Background photons per pixel
camera_noise=true # Full sCMOS noise model (QE, Poisson, read noise, gain, offset)
)Complete Examples
Static SMLM with Image Generation
using SMLMSim
using MicroscopePSFs
# Define a camera and simulation parameters
camera = IdealCamera(128, 128, 0.1) # 128×128 pixels, 100nm pixels
params = StaticSMLMConfig(density=1.0, σ_psf=0.13, nframes=1000)
# Run simulation for an 8-molecule ring pattern
smld_noisy, info = simulate(
params;
pattern=Nmer2D(n=8, d=0.1), # 100nm diameter ring
molecule=GenericFluor(1e5, [-10.0 10.0; 0.5 -0.5]), # γ=100,000, k_off=10, k_on=0.5
camera=camera
)
# Access intermediate results from info
smld_model = info.smld_model
# Create a PSF model
psf = GaussianPSF(0.15) # 150nm PSF width
# Generate microscope images with finite PSF support
images, img_info = gen_images(smld_model, psf;
support=1.0, # 1.0 μm PSF support radius (faster than Inf)
bg=5.0, # 5 background photons per pixel
poisson_noise=true # Add realistic photon counting noise
)
println("Generated $(length(smld_noisy.emitters)) localizations and $(img_info.frames_generated) images.")
println("Simulation took $(info.elapsed_s) seconds")Diffusion with Dimer Analysis
using SMLMSim
using MicroscopePSFs
# Set diffusion simulation parameters
params = DiffusionSMLMConfig(
density = 0.5, # molecules per μm²
box_size = 10.0, # μm
diff_monomer = 0.1, # μm²/s
diff_dimer = 0.05, # μm²/s
k_off = 0.2, # s⁻¹
t_max = 10.0, # s
boundary = "reflecting" # Use reflecting boundaries
)
# Run diffusion simulation
smld, info = simulate(params)
# Analyze dimer formation
frames, dimer_fraction = analyze_dimer_fraction(smld)
avg_lifetime = analyze_dimer_lifetime(smld)
# Generate microscope images with finite PSF support
psf = GaussianPSF(0.15) # 150nm PSF width
images, img_info = gen_images(smld, psf;
support=1.0, # 1.0 μm PSF support radius (faster)
bg=2.0, # Background photons per pixel
poisson_noise=true # Add realistic photon counting noise
)
println("Simulation complete with $(length(smld.emitters)) emitters")
println("Average dimer fraction: $(mean(dimer_fraction))")
println("Average dimer lifetime: $(avg_lifetime) seconds")Custom Pattern with 3D Simulation
using SMLMSim
using MicroscopePSFs
# Define a custom 3D pattern: two rings at different z-positions
mutable struct DoubleRing3D <: Pattern3D
n::Int
d1::Float64
d2::Float64
z1::Float64
z2::Float64
x::Vector{Float64}
y::Vector{Float64}
z::Vector{Float64}
end
function DoubleRing3D(; n=8, d1=0.1, d2=0.2, z1=-0.2, z2=0.2)
total_n = 2*n
x = zeros(total_n)
y = zeros(total_n)
z = zeros(total_n)
# First ring (bottom)
for i = 1:n
θ = 2π * (i-1) / n
x[i] = d1/2 * cos(θ)
y[i] = d1/2 * sin(θ)
z[i] = z1
end
# Second ring (top)
for i = 1:n
θ = 2π * (i-1) / n + π/n # Offset angle for second ring
x[n+i] = d2/2 * cos(θ)
y[n+i] = d2/2 * sin(θ)
z[n+i] = z2
end
return DoubleRing3D(n, d1, d2, z1, z2, x, y, z)
end
# Create camera and parameters
camera = IdealCamera(128, 128, 0.1)
params = StaticSMLMConfig(
density = 0.5,
σ_psf = 0.13,
nframes = 2000,
ndims = 3,
zrange = [-1.0, 1.0]
)
# Create custom pattern
double_ring = DoubleRing3D(n=6, d1=0.15, d2=0.3, z1=-0.3, z2=0.3)
# Run simulation
smld_noisy, info = simulate(
params;
pattern=double_ring,
camera=camera
)
# Access intermediate results
smld_model = info.smld_model
# Generate images with a 3D astigmatic PSF and finite support
# Create a PSF with astigmatism using Zernike coefficients
using MicroscopePSFs
zc = ZernikeCoefficients(15)
zc.phase[6] = 0.5 # Add vertical astigmatism
psf_scalar = ScalarPSF(1.4, 0.532, 1.52; zernike_coeffs=zc)
# Create SplinePSF for speed
xy_sampling, z_sampling = 0.05, 0.1
x_range = y_range = -1.0:xy_sampling:1.0
z_range = -1.0:z_sampling:1.0
psf_spline = SplinePSF(psf_scalar, x_range, y_range, z_range)
# Generate images using the spline PSF with finite support
images, img_info = gen_images(smld_model, psf_spline;
support=0.5, # 0.5 μm PSF support radius for performance
bg=5.0, # Background photons per pixel
poisson_noise=true # Add realistic photon counting noise
)
println("Generated $(length(smld_noisy.emitters)) localizations in 3D")api() returns this documentation as a plain String.
SMLMSim.simulate — Method
simulate(sim::SMLMSimParams; kwargs...)Generic interface for all simulation types. Dispatches to the appropriate method based on the concrete simulation type.
Arguments
sim::SMLMSimParams: The simulation configuration objectkwargs...: Additional keyword arguments specific to the simulation type
Returns
- The result of the specific simulation method
Example
# Create a static SMLM simulation configuration
params = StaticSMLMConfig(
density = 1.0, # Changed from ρ to density
σ_psf = 0.13
)
# Run the simulation
results = simulate(params)SMLMSim.Core — Module
CoreCore module with shared utilities for SMLM simulation.
This module contains fundamental components that can be used across different simulation types (static, diffusion, etc.) including:
- Abstract types for simulation
- Molecule and pattern definitions
- CTMC (Continuous Time Markov Chain) for stochastic state transitions
- Photophysics modeling for blinking kinetics and detection
Usage
using SMLMSim.CoreSMLMSim.Core.AbstractLabeling — Type
AbstractLabelingAbstract type for labeling strategies that determine how many fluorophores attach to each binding site.
Labeling is separate from photophysics (Molecule) - this type hierarchy handles the statistics of fluorophore attachment, while Molecule handles blinking kinetics.
Implementations
FixedLabeling: Deterministic number of fluorophores per sitePoissonLabeling: Poisson-distributed number of fluorophores per siteBinomialLabeling: Binomial-distributed number of fluorophores per site
SMLMSim.Core.BinomialLabeling — Type
BinomialLabeling <: AbstractLabelingBinomial-distributed number of fluorophores per site. Models scenarios where each binding site has n potential attachment points, each occupied with probability p.
Fields
n::Int: Number of potential attachment points per sitep::Float64: Probability each attachment point is occupied (0 to 1)efficiency::Float64: Probability that a site gets labeled at all (0 to 1)
Examples
# Antibody with 4 dye attachment points, each 80% likely to be occupied
labeling = BinomialLabeling(4, 0.8)
# Same, but only 90% of binding sites get an antibody
labeling = BinomialLabeling(4, 0.8; efficiency=0.9)SMLMSim.Core.CTMC — Type
CTMC{T<:AbstractFloat, U<:Int}A Continuous Time Markov Chain representation storing the full trajectory of state transitions.
Fields
simulation_time::T: Total simulation time spantransition_times::Vector{T}: Time points at which state changes occurred, starting at 0.0states::Vector{U}: Sequence of states entered at each transition time, starting with initial state
Type Parameters
T: Floating point type for time valuesU: Integer type for state indices
Note
The states and transition_times vectors have the same length, with each entry in states[i] representing the state entered at time transition_times[i]. The system remains in states[i] until time transition_times[i+1].
SMLMSim.Core.CTMC — Method
CTMC(q::Array{T}, simulation_time::T, state1::Int) where {T<:AbstractFloat}Construct a Continuous Time Markov Chain simulation from a rate matrix.
Arguments
q::Array{T}: Rate matrix where q[i,j] for i≠j is the transition rate from state i to j, and q[i,i] is the negative exit rate from state isimulation_time::T: Total time to simulatestate1::Int: Initial state
Returns
CTMC{T,Int}: Simulated CTMC with transition times and states
Details
Simulates a CTMC using the Gillespie algorithm:
- Start in state1 at time 0
- For current state i:
- Calculate total exit rate ktot = Σj q[i,j]
- Sample time until next transition from Exp(k_tot)
- Sample next state j with probability q[i,j]/k_tot
- Repeat until exceeding simulation_time
SMLMSim.Core.FixedLabeling — Type
FixedLabeling <: AbstractLabelingDeterministic labeling with exactly n fluorophores per site.
Fields
n::Int: Number of fluorophores per siteefficiency::Float64: Probability that a site gets labeled at all (0 to 1)
Examples
# Perfect labeling - exactly 1 fluorophore per site (default/current behavior)
labeling = FixedLabeling()
# 2 fluorophores per site, 90% of sites labeled
labeling = FixedLabeling(2; efficiency=0.9)SMLMSim.Core.GenericFluor — Type
GenericFluor <: MoleculeDefines a fluorophore with photophysical properties.
Fields
γ::AbstractFloat: Photon emission rate in Hz. Default: 1e5q::Array{<:AbstractFloat}: Rate matrix where q[i,j] for i≠j is the transition rate from state i to j, and q[i,i] is the negative exit rate from state i. Default: standard 2-state model with on->off rate of 50Hz and off->on rate of 1e-2Hz
Examples
# Create a fluorophore with default parameters (using the 2-state keyword constructor)
fluor = GenericFluor()
# Create a fluorophore with custom parameters using the positional constructor
fluor = GenericFluor(1e5, [-50.0 50.0; 1e-2 -1e-2])
# Create a fluorophore using the 2-state keyword constructor
fluor = GenericFluor(; photons=1e5, k_off=10.0, k_on=1e-1)SMLMSim.Core.GenericFluor — Method
GenericFluor(; photons::AbstractFloat=1e5, k_off::AbstractFloat=50.0, k_on::AbstractFloat=1e-2)Create a simple two-state (on/off) fluorophore with specified parameters.
Arguments
photons::AbstractFloat: Photon emission rate in Hzk_off::AbstractFloat: Off-switching rate (on→off) in Hzk_on::AbstractFloat: On-switching rate (off→on) in Hz
Details
Creates a fluorophore with a 2-state model and the specified rates. State 1 is the on (bright) state, and state 2 is the off (dark) state. The rate matrix is constructed as: q = [-koff koff; kon -kon]
Note: kon and koff are transition rates (1/s), not duty cycle fractions. The duty cycle (fraction of time in ON state) is kon/(kon + koff). For typical dSTORM, kon << k_off gives low duty cycle (mostly dark, brief blinks).
SMLMSim.Core.Line2D — Type
Line2D <: Pattern2DPoints with uniform random distribution between two endpoints.
Fields
λ::Float64: Linear molecule density (molecules per micron)endpoints::Vector{Tuple{Float64,Float64}}: Vector of endpoint coordinatesn::Int: Number of molecules in the patternx::Vector{Float64}: X positions of molecules in micronsy::Vector{Float64}: Y positions of molecules in microns
Examples
# Create a line with default parameters
line = Line2D()
# Create a custom line
line = Line2D(; λ=5.0, endpoints=[(-2.0, 0.0), (2.0, 0.0)])SMLMSim.Core.Line3D — Type
Line3D <: Pattern3DPoints with uniform random distribution between two 3D endpoints.
Fields
λ::Float64: Linear molecule density (molecules per micron)endpoints::Vector{Tuple{Float64,Float64,Float64}}: Vector of 3D endpoint coordinatesn::Int: Number of molecules in the patternx::Vector{Float64}: X positions of molecules in micronsy::Vector{Float64}: Y positions of molecules in micronsz::Vector{Float64}: Z positions of molecules in microns
Examples
# Create a line with default parameters
line = Line3D()
# Create a custom 3D line
line = Line3D(; λ=5.0, endpoints=[(-1.0, 0.0, -0.5), (1.0, 0.0, 0.5)])SMLMSim.Core.Molecule — Type
MoleculeAbstract type for representing photophysical properties of a molecule.
This is the most general type of luminescent or scattering single molecule. Inherited types will define the properties of specific classes of molecules.
SMLMSim.Core.Nmer2D — Type
Nmer2D <: Pattern2DN molecules symmetrically organized around a circle with diameter d.
Fields
n::Int: Number of molecules in the patternd::Float64: Diameter of the circle in micronsx::Vector{Float64}: X positions of molecules in micronsy::Vector{Float64}: Y positions of molecules in microns
Examples
# Create an 8-molecule pattern with 100nm diameter
nmer = Nmer2D()
# Create a custom pattern with 6 molecules and 200nm diameter
nmer = Nmer2D(; n=6, d=0.2)SMLMSim.Core.Nmer3D — Type
Nmer3D <: Pattern3DN molecules symmetrically organized around a circle with diameter d at z=0.
Fields
n::Int: Number of molecules in the patternd::Float64: Diameter of the circle in micronsx::Vector{Float64}: X positions of molecules in micronsy::Vector{Float64}: Y positions of molecules in micronsz::Vector{Float64}: Z positions of molecules in microns
Examples
# Create an 8-molecule pattern with 100nm diameter
nmer = Nmer3D()
# Create a custom pattern with 6 molecules and 200nm diameter
nmer = Nmer3D(; n=6, d=0.2)SMLMSim.Core.Pattern — Type
PatternAbstract type for all molecular spatial patterns.
SMLMSim.Core.Pattern2D — Type
Pattern2D <: PatternAbstract type for 2D molecular spatial patterns.
SMLMSim.Core.Pattern3D — Type
Pattern3D <: PatternAbstract type for 3D molecular spatial patterns.
SMLMSim.Core.PoissonLabeling — Type
PoissonLabeling <: AbstractLabelingPoisson-distributed number of fluorophores per site.
Fields
mean::Float64: Mean number of fluorophores per site (λ for Poisson)efficiency::Float64: Probability that a site gets labeled at all (0 to 1)
Examples
# Average 1.5 fluorophores per site
labeling = PoissonLabeling(1.5)
# Average 2 fluorophores per site, but only 80% of sites get labeled
labeling = PoissonLabeling(2.0; efficiency=0.8)Note
With Poisson statistics, some sites may receive 0 fluorophores even when efficiency=1.0 (especially for small mean values). The efficiency parameter controls a separate "does this site get labeled at all" step.
SMLMSim.Core.SMLMSimParams — Type
SMLMSimParams <: AbstractSMLMConfigAbstract type for all SMLM simulation parameter types. Inherits from SMLMData.AbstractSMLMConfig to participate in the ecosystem-wide (Config, Info, Data) tuple pattern.
SMLMSim.Core.apply_labeling — Method
apply_labeling(coords, labeling::AbstractLabeling)Apply labeling strategy to binding site coordinates without pattern tracking.
This is a convenience method that returns only the expanded coordinates (for backward compatibility).
Arguments
coords: Tuple of coordinate vectors(x, y)for 2D or(x, y, z)for 3Dlabeling::AbstractLabeling: Labeling strategy to apply
Returns
- Tuple of coordinate vectors with fluorophore positions
Example
# Apply labeling without pattern tracking
x, y = [1.0, 2.0], [3.0, 4.0]
new_x, new_y = apply_labeling((x, y), FixedLabeling(2))SMLMSim.Core.apply_labeling — Method
apply_labeling(coords, pattern_ids, labeling::AbstractLabeling)Apply labeling strategy to binding site coordinates, expanding each site to the appropriate number of fluorophore positions while preserving pattern IDs.
Arguments
coords: Tuple of coordinate vectors(x, y)for 2D or(x, y, z)for 3Dpattern_ids::Vector{Int}: Pattern instance ID for each binding sitelabeling::AbstractLabeling: Labeling strategy to apply
Returns
Tuple: (coords, newpatternids) where coords is a tuple of coordinate vectors and newpatternids preserves pattern membership for each fluorophore
Example
# Original: binding sites with pattern IDs
x, y, pattern_ids = uniform2D(1.0, Nmer2D(), 10.0, 10.0)
# After Poisson labeling: variable number of fluorophores, pattern IDs preserved
(x_labeled, y_labeled), new_ids = apply_labeling((x, y), pattern_ids, PoissonLabeling(1.5))SMLMSim.Core.compute_equilibrium_distribution — Method
compute_equilibrium_distribution(q::Matrix{<:AbstractFloat})Calculate the equilibrium probability distribution for a CTMC rate matrix.
Arguments
q::Matrix{<:AbstractFloat}: Rate matrix where q[i,j] for i≠j is the transition rate from state i to j, and q[i,i] is the negative exit rate from state i
Returns
Vector{Float64}: Equilibrium probabilities for each state
Details
For a rate matrix Q, the equilibrium distribution π satisfies π·Q = 0 subject to Σπ = 1. This function solves the linear system directly to find the equilibrium distribution.
SMLMSim.Core.get_next — Method
get_next(ctmc::CTMC, t::AbstractFloat)Get the next state transition after a specific time point.
Arguments
ctmc::CTMC: The CTMC to queryt::AbstractFloat: Current time point
Returns
Tuple{Int,AbstractFloat}: (nextstate, transitiontime)
Note
Returns the next state that will be entered and when it will be entered, searching from the current time point forward.
SMLMSim.Core.get_num_tracks — Method
get_num_tracks(smld::BasicSMLD)Return the number of unique tracks (based on track_id) in the SMLD.
Arguments
smld::BasicSMLD: The SMLD to analyze
Returns
Int: Number of unique track IDs
Example
# Get the number of tracks
n_tracks = get_num_tracks(smld)SMLMSim.Core.get_state — Method
get_state(ctmc::CTMC, t::AbstractFloat)Get the state of the CTMC at a specific time point.
Arguments
ctmc::CTMC: The CTMC to queryt::AbstractFloat: Time point of interest
Returns
Int: State of the chain at time t
Note
Searches through transition times to find the state active at time t. Returns the state that was entered at the last transition before t.
SMLMSim.Core.get_track — Method
get_track(smld::BasicSMLD, id::Int)Return a new SMLD containing only emitters with the specified track_id.
Arguments
smld::BasicSMLD: The original SMLDid::Int: Track ID to filter by
Returns
BasicSMLD: New SMLD containing only emitters from the specified track
Example
# Get all emitters belonging to track 5
track_smld = get_track(smld, 5)SMLMSim.Core.get_tracks — Method
get_tracks(smld::BasicSMLD)Return a vector of SMLD objects, one for each unique track (based on track_id).
Arguments
smld::BasicSMLD: The original SMLD
Returns
Vector{BasicSMLD}: Vector of SMLD objects, one per track
Example
# Get all tracks as separate SMLD objects
track_smlds = get_tracks(smld)
# Access the first track
first_track = track_smlds[1]SMLMSim.Core.intensity_trace — Method
intensity_trace(f::GenericFluor, nframes::Int, framerate::Real; state1=1, burn_in=0.0)Calculate a fluorescence intensity trace by integrating emission during fluorescent state occupancy.
Arguments
f::GenericFluor: Fluorophore model containing transition rates (q) and emission rate (γ)nframes::Int: Number of frames to simulateframerate::Real: Frame rate in Hzstate1::Int=1: Initial state (default: 1 for fluorescent state)burn_in::Real=0.0: Pre-illumination time in seconds before recording starts. The CTMC runs for this duration first, allowing the system to reach a pseudo-equilibrium (e.g., some molecules bleach before data collection begins).
Returns
Vector{Float64}: Integrated photon counts for each frame
Details
For each frame:
- Determines state occupancy using CTMC (which starts at time 0, but recording starts at burn_in)
- Integrates emission (rate f.γ) during fluorescent state periods
- Accumulates photons within frame exposure time (1/framerate)
Example
fluor = GenericFluor(; γ=10000.0, q=[-10.0 10.0; 1e-1 -1e-1])
photons = intensity_trace(fluor, 1000, 10.0)
# With 5 second burn-in (pre-illumination before recording)
photons = intensity_trace(fluor, 1000, 10.0; burn_in=5.0)Note
- State 1 is assumed to be the fluorescent state
- Emission only occurs in state 1 with rate f.γ
- Frame exposure is assumed to be 1/framerate (100% duty cycle)
- burn_in is useful for models with photobleaching to simulate pre-illumination
SMLMSim.Core.kinetic_model — Method
kinetic_model(smld::BasicSMLD, f::Molecule, nframes::Int, framerate::Real;
ndatasets::Int=1, minphotons=50.0, state1::Union{Int, Symbol}=:equilibrium,
burn_in::Real=0.0)Generate kinetic blinking model from existing localization data.
Arguments
smld::BasicSMLD: Input SMLD containing true emitter positionsf::Molecule: Fluorophore model with kinetic ratesnframes::Int: Number of frames to simulateframerate::Real: Frame rate in Hzndatasets::Int=1: Number of independent datasets to generateminphotons::Float64=50.0: Minimum photons for detectionstate1::Union{Int, Symbol}=:equilibrium: Initial state specification:::Int: Specific state to start in (1=on, 2=off typically):equilibrium: Sample from equilibrium distribution (default)
burn_in::Real=0.0: Pre-illumination time in seconds before recording starts. Simulates the common experimental protocol where high laser power is applied for several seconds before data collection begins, allowing some molecules to bleach and others to reach a pseudo-equilibrium blinking state.
Returns
BasicSMLD: New SMLD with simulated blinking kinetics
Details
For each unique position in the input SMLD:
- Simulates fluorophore blinking using the kinetic model
- Creates emitters for frames where photon count exceeds threshold
- Preserves track_id for linking emitters from same position
- Maintains camera and extends metadata from input SMLD
Example
camera = IdealCamera(1:128, 1:128, 0.1)
pattern = Nmer2D()
smld_true, _, _ = simulate(pattern=pattern, camera=camera)
# Add blinking kinetics
fluor = GenericFluor(; γ=10000.0, q=[-10.0 10.0; 1e-1 -1e-1])
smld_model = kinetic_model(smld_true, fluor, 1000, 10.0)
# With 5 second burn-in to simulate pre-illumination
smld_model = kinetic_model(smld_true, fluor, 1000, 10.0; burn_in=5.0)Note
The emitter type (2D/3D) is automatically determined from the input SMLD. Position uncertainties are initialized to 0 and can be set using the apply_noise() function. For models with photobleaching (absorbing states), use state1=1 instead of :equilibrium since absorbing states have no true equilibrium.
SMLMSim.Core.n_fluorophores — Method
n_fluorophores(labeling::AbstractLabeling) -> IntSample the number of fluorophores to place at a single binding site.
This function is called once per binding site during labeling. It first checks labeling efficiency (probability site gets labeled at all), then samples from the appropriate distribution.
Returns
Int: Number of fluorophores (can be 0)
SMLMSim.Core.rotate! — Method
rotate!(p::Pattern2D, θ::Float64)Rotate a 2D pattern by angle θ (in radians).
Arguments
p::Pattern2D: Pattern to rotateθ::Float64: Rotation angle in radians
Example
nmer = Nmer2D()
rotate!(nmer, π/4) # Rotate 45 degreesSMLMSim.Core.rotate! — Method
rotate!(p::Pattern3D, R::Matrix{Float64})Rotate a 3D pattern by rotation matrix R.
Arguments
p::Pattern3D: Pattern to rotateR::Matrix{Float64}: 3x3 rotation matrix
Example
nmer = Nmer3D()
# Create a rotation matrix for 90 degrees around z-axis
θ = π/2
R = [cos(θ) -sin(θ) 0; sin(θ) cos(θ) 0; 0 0 1]
rotate!(nmer, R)SMLMSim.Core.rotate! — Method
rotate!(p::Pattern3D, α::Float64, β::Float64, γ::Float64)Rotate a 3D pattern by Euler angles α, β, γ (in radians). Uses ZYZ convention.
Arguments
p::Pattern3D: Pattern to rotateα::Float64: First rotation angle (around Z axis)β::Float64: Second rotation angle (around Y' axis)γ::Float64: Third rotation angle (around Z'' axis)
Example
nmer = Nmer3D()
rotate!(nmer, π/4, π/6, π/3)SMLMSim.Core.sample_discrete — Method
sample_discrete(p::Vector{<:AbstractFloat})Sample from a discrete probability distribution.
Arguments
p::Vector{<:AbstractFloat}: Probability distribution
Returns
Int: Sampled state index
Details
Samples a state index i with probability p[i] using the inverse CDF method.
SMLMSim.Core.sample_discrete_with_probs — Method
sample_discrete_with_probs(indices, probs)Sample a discrete value from indices with corresponding probabilities.
Arguments
indices::Vector{Int}: Vector of possible indices to sampleprobs::Vector{<:AbstractFloat}: Corresponding probabilities
Returns
Int: Sampled index
SMLMSim.Core.uniform2D — Method
uniform2D(ρ::Float64, p::Pattern2D, field_x::Float64, field_y::Float64)Create coordinate arrays for randomly placed and rotated 2D patterns.
Arguments
ρ::Float64: Pattern density (patterns per square micron)p::Pattern2D: Pattern to replicatefield_x::Float64: Field width in micronsfield_y::Float64: Field height in microns
Returns
Tuple{Vector{Float64}, Vector{Float64}, Vector{Int}}: (x, y, patternids) where patternids indicates which pattern instance each point belongs to
Example
# Generate coordinates for randomly placed Nmer2D patterns
nmer = Nmer2D(; n=6, d=0.2)
x, y, pattern_ids = uniform2D(1.0, nmer, 10.0, 10.0)SMLMSim.Core.uniform3D — Method
uniform3D(ρ::Float64, p::Pattern3D, field_x::Float64, field_y::Float64;
zrange::Vector{Float64}=[-1.0, 1.0])Create coordinate arrays for randomly placed and rotated 3D patterns.
Arguments
ρ::Float64: Pattern density (patterns per square micron)p::Pattern3D: Pattern to replicatefield_x::Float64: Field width in micronsfield_y::Float64: Field height in micronszrange::Vector{Float64}=[-1.0, 1.0]: [minz, maxz] range in microns
Returns
Tuple{Vector{Float64}, Vector{Float64}, Vector{Float64}, Vector{Int}}: (x, y, z, patternids) where patternids indicates which pattern instance each point belongs to
Example
# Generate coordinates for randomly placed Nmer3D patterns
nmer = Nmer3D(; n=6, d=0.2)
x, y, z, pattern_ids = uniform3D(1.0, nmer, 10.0, 10.0; zrange=[-2.0, 2.0])SMLMSim.StaticSMLM — Module
StaticSMLMModule for simulating static (non-diffusing) SMLM data with blinking kinetics.
This module provides functionality for:
- Generating spatial distributions of emitters based on patterns
- Simulating fluorophore blinking based on stochastic kinetic models
- Adding realistic localization uncertainties
- Creating complete SMLM datasets
Usage
using SMLMSim.StaticSMLMSMLMSim.StaticSMLM.StaticSMLMConfig — Type
StaticSMLMConfig <: SMLMSimParamsParameters for static SMLM simulation.
Fields
density::Float64: density in particles per square micronσ_psf::Float64: PSF width in micronsminphotons::Int: minimum photons for detectionndatasets::Int: number of datasets to simulatenframes::Int: number of frames per datasetframerate::Float64: frames per secondndims::Int: dimensionality (2 or 3)zrange::Vector{Float64}: axial range for 3D simulations [minz, maxz]
Examples
# Default parameters
params = StaticSMLMConfig()
# Custom parameters
params = StaticSMLMConfig(
density = 2.0, # 2 particles per μm²
σ_psf = 0.15, # 150nm PSF width
minphotons = 100, # minimum photons for detection
ndatasets = 5, # 5 independent datasets
nframes = 2000, # 2000 frames per dataset
framerate = 100.0, # 100 frames per second
ndims = 3, # 3D simulation
zrange = [-2.0, 2.0] # 4μm axial range
)SMLMSim.StaticSMLM.add_coordinate_noise — Method
add_coordinate_noise(emitter, σ)Helper function to add position noise to an emitter with appropriate uncertainty. Returns new coordinate values and uncertainty values.
For 2D emitters, σ is a scalar. For 3D emitters, σ is a 3-element vector.
SMLMSim.StaticSMLM.apply_noise — Method
apply_noise(smld::BasicSMLD, σ_psf::AbstractFloat)Add localization uncertainty to 2D emitter positions based on photon counts.
Arguments
smld::BasicSMLD: Input SMLD containing 2D emittersσ_psf::AbstractFloat: PSF width in microns
Returns
BasicSMLD: New SMLD with noisy positions and updated uncertainties
Note
For symmetric (axis-aligned) PSFs, σ_xy covariance is set to 0.0. This is the physically correct value, not a placeholder. Rotated/tilted PSFs would require additional parameters to compute non-zero covariance.
Example
# Then add localization noise with specific PSF width
smld_noisy = apply_noise(smld_model, 0.13) # 130nm PSF widthSMLMSim.StaticSMLM.apply_noise — Method
apply_noise(smld::BasicSMLD, σ_psf::Vector{<:AbstractFloat})Add localization uncertainty to 3D emitter positions based on photon counts.
Arguments
smld::BasicSMLD: Input SMLD containing 3D emittersσ_psf::Vector{<:AbstractFloat}: PSF widths [σx, σy, σz] in microns
Returns
BasicSMLD: New SMLD with noisy positions and updated uncertainties
Note
For symmetric (axis-aligned) PSFs, σ_xy covariance is set to 0.0. This is the physically correct value, not a placeholder. Rotated/tilted PSFs would require additional parameters to compute non-zero covariance.
Example
# Then add localization noise with specific PSF widths
σ_psf = [0.13, 0.13, 0.39] # 130nm lateral, 390nm axial
smld_noisy = apply_noise(smld_model, σ_psf)SMLMSim.simulate — Method
simulate(params::StaticSMLMConfig;
starting_conditions::Union{Nothing, SMLD, Vector{<:AbstractEmitter}}=nothing,
pattern::Pattern=nothing,
labeling::AbstractLabeling=FixedLabeling(),
molecule::Molecule=GenericFluor(photons=1e4, k_off=50.0, k_on=1e-2),
camera::AbstractCamera=IdealCamera(1:128, 1:128, 0.1),
state1::Union{Int, Symbol}=:equilibrium,
burn_in::Real=0.0)Generate simulated static SMLM data with realistic blinking kinetics and localization uncertainty.
Arguments
params::StaticSMLMConfig: Simulation parametersstarting_conditions::Union{Nothing, SMLD, Vector{<:AbstractEmitter}}: Optional starting conditions instead of generating patternspattern::Pattern: Pattern to use (default depends on params.ndims)labeling::AbstractLabeling: Labeling strategy for fluorophore attachment (default: FixedLabeling() = 1 per site)molecule::Molecule: Fluorophore model for blinking simulationcamera::AbstractCamera: Camera model for detection simulationstate1::Union{Int, Symbol}=:equilibrium: Initial fluorophore state:::Int: Specific state (1=on, 2=off typically):equilibrium: Sample from equilibrium distribution (default)
state1=1.burn_in::Real=0.0: Pre-illumination time in seconds before recording starts. Simulates experimental protocol where laser is on before data collection, allowing some molecules to bleach and reach pseudo-equilibrium.
Returns
Tuple{BasicSMLD, SimInfo}: (noisy_data, info)- noisy_data: Positions with blinking and localization uncertainty
- info: SimInfo containing smldtrue, smldmodel, timing, and counts
Example
# Create parameters
params = StaticSMLMConfig(
density = 2.0, # 2 patterns per μm²
σ_psf = 0.15, # 150nm PSF width
minphotons = 100, # 100 photons for detection
ndatasets = 5, # 5 independent datasets
nframes = 2000, # 2000 frames
framerate = 100.0 # 100 frames per second
)
# Run simulation with Nmer pattern
pattern = Nmer3D(n=6, d=0.2)
smld_noisy, info = simulate(params; pattern=pattern)
# Access intermediate results
smld_true = info.smld_true
smld_model = info.smld_model
# Run with Poisson labeling (average 1.5 fluorophores per binding site)
smld_noisy, info = simulate(params;
pattern=pattern,
labeling=PoissonLabeling(1.5)
)
# Run with partial labeling efficiency (80% of sites labeled)
smld_noisy, info = simulate(params;
pattern=pattern,
labeling=PoissonLabeling(1.0; efficiency=0.8)
)
# Run with photobleaching model and 5s burn-in
k_off, k_on, k_bleach = 10.0, 1.0, 0.1
Q = [-(k_off+k_bleach) k_off k_bleach; k_on -k_on 0.0; 0.0 0.0 0.0]
fluor = GenericFluor(1e4, Q)
smld_noisy, info = simulate(params;
molecule=fluor,
state1=1, # Start in ON state (required for absorbing states)
burn_in=5.0 # 5 seconds pre-illumination
)
# Run with custom starting conditions (labeling not applied)
custom_emitters = [
Emitter2DFit{Float64}(x, y, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0; σ_xy=0.0, track_id=i)
for (i, (x, y)) in enumerate(zip(rand(10), rand(10)))
]
smld_noisy, info = simulate(params; starting_conditions=custom_emitters)Note
- The
params.σ_psfvalue is used directly for lateral uncertainty (σx, σy) in both 2D and 3D. - For 3D simulations, the axial uncertainty (σz) is scaled by a factor of 3 (i.e., σz = 3 * σ_psf).
- If
starting_conditionsis provided, it will be used instead of generating patterns, and labeling is not applied. - Labeling and molecule are separate concepts: labeling controls how many fluorophores per binding site, molecule controls the photophysics (blinking) of each fluorophore.
SMLMSim.InteractionDiffusion — Module
InteractionDiffusionThis module provides simulation tools for diffusion and interaction between particles.
Overview
Simulates diffusion and interaction dynamics between particles in 2D/3D space. Includes functionality for generating microscope images, analyzing dimers, and visualizing particle dynamics.
Components
- Abstract and concrete emitter types (AbstractDiffusingEmitter, DiffusingEmitter2D, DiffusingEmitter3D)
- Smoluchowski dynamics simulation
- Analysis tools for dimers and state transitions
- SMLD conversion utilities
Examples
# Set up simulation parameters
params = DiffusionSMLMConfig(
density = 0.5, # molecules per μm²
box_size = 10.0, # μm
diff_monomer = 0.1, # μm²/s
diff_dimer = 0.05, # μm²/s
k_off = 0.2, # s⁻¹
r_react = 0.01, # μm
d_dimer = 0.05, # μm
dt = 0.01, # s
t_max = 10.0, # s
camera_framerate = 20.0, # fps
camera_exposure = 0.04 # s
)
# Run simulation - returns a single SMLD with all emitters
smld = simulate(params)
# Generate images for microscopy
psf = GaussianPSF(0.15) # 150nm PSF width
images = gen_images(psf, smld)
# Analyze results
dimer_smld = get_dimers(smld)
frames, dimer_fractions = analyze_dimer_fraction(smld)SMLMSim.InteractionDiffusion.AbstractDiffusingEmitter — Type
AbstractDiffusingEmitter <: AbstractEmitterAbstract type for all diffusing emitters to enable dispatch-based operations. This provides a common parent for 2D and 3D diffusing emitters.
SMLMSim.InteractionDiffusion.DiffusingEmitter2D — Type
DiffusingEmitter2D{T<:AbstractFloat} <: AbstractDiffusingEmitterA 2D emitter type for diffusion simulations that contains both spatial and temporal information, plus molecular state information.
Fields
x::T: x-coordinate in micronsy::T: y-coordinate in micronsphotons::T: number of photons emittedtimestamp::T: actual simulation time in secondsframe::Int: camera frame number based on framerate and exposuredataset::Int: dataset identifiertrack_id::Int: unique molecule identifierstate::Symbol: molecular state (:monomer or :dimer)partner_id::Union{Int,Nothing}: ID of linked molecule (for dimers), or nothing for monomers
SMLMSim.InteractionDiffusion.DiffusingEmitter3D — Type
DiffusingEmitter3D{T<:AbstractFloat} <: AbstractDiffusingEmitterA 3D emitter type for diffusion simulations that contains both spatial and temporal information, plus molecular state information.
Fields
x::T: x-coordinate in micronsy::T: y-coordinate in micronsz::T: z-coordinate in micronsphotons::T: number of photons emittedtimestamp::T: actual simulation time in secondsframe::Int: camera frame number based on framerate and exposuredataset::Int: dataset identifiertrack_id::Int: unique molecule identifierstate::Symbol: molecular state (:monomer or :dimer)partner_id::Union{Int,Nothing}: ID of linked molecule (for dimers), or nothing for monomers
SMLMSim.InteractionDiffusion.DiffusionSMLMConfig — Type
DiffusionSMLMConfig <: SMLMSimParamsParameters for diffusion-based SMLM simulation using Smoluchowski dynamics.
Fields
density::Float64: number density (molecules/μm²)box_size::Float64: simulation box size (μm)diff_monomer::Float64: monomer diffusion coefficient (μm²/s)diff_dimer::Float64: dimer diffusion coefficient (μm²/s)diff_dimer_rot::Float64: dimer rotational diffusion coefficient (rad²/s)k_off::Float64: dimer dissociation rate (s⁻¹)r_react::Float64: reaction radius (μm)d_dimer::Float64: monomer separation in dimer (μm)dt::Float64: time step (s)t_max::Float64: total simulation time (s)ndims::Int: number of dimensions (2 or 3)boundary::String: boundary condition type ("periodic" or "reflecting")camera_framerate::Float64: camera frames per second (Hz)camera_exposure::Float64: camera exposure time per frame (s)
Examples
# Default parameters
params = DiffusionSMLMConfig()
# Custom parameters
params = DiffusionSMLMConfig(
density = 1.0, # 1 molecule per μm²
box_size = 20.0, # 20μm × 20μm box
diff_monomer = 0.2, # 0.2 μm²/s
diff_dimer = 0.1, # 0.1 μm²/s
diff_dimer_rot = 0.8, # 0.8 rad²/s
k_off = 0.1, # 0.1 s⁻¹
r_react = 0.02, # 20nm reaction radius
d_dimer = 0.06, # 60nm dimer separation
dt = 0.005, # 5ms time step
t_max = 20.0, # 20s simulation
ndims = 3, # 3D simulation
boundary = "reflecting", # reflecting boundaries
camera_framerate = 20.0, # 20 frames per second
camera_exposure = 0.04 # 40ms exposure per frame
)SMLMSim.InteractionDiffusion.add_camera_frame_emitters! — Method
add_camera_frame_emitters!(camera_emitters, emitters, time, frame_num, params)Add emitters to camera frames when they fall within an exposure window.
Arguments
camera_emitters::Vector{<:AbstractDiffusingEmitter}: Collection of emitters for camera framesemitters::Vector{<:AbstractDiffusingEmitter}: Current emitters from simulationtime::Float64: Current simulation timeframe_num::Int: Current frame numberparams::DiffusionSMLMConfig: Simulation parameters
Returns
Nothing
SMLMSim.InteractionDiffusion.analyze_dimer_fraction — Method
analyze_dimer_fraction(smld::BasicSMLD)Calculate the fraction of dimers per frame.
Arguments
smld::BasicSMLD: SMLD containing all emitters
Returns
Tuple{Vector{Int}, Vector{Float64}}: Frame numbers and dimer fractions
Example
# Calculate dimer fraction over time
frames, fractions = analyze_dimer_fraction(smld)
plot(frames, fractions, xlabel="Frame", ylabel="Dimer Fraction")SMLMSim.InteractionDiffusion.analyze_dimer_lifetime — Method
analyze_dimer_lifetime(smld::BasicSMLD)Calculate the average lifetime of dimers.
Arguments
smld::BasicSMLD: SMLD containing all emitters
Returns
Float64: Average dimer lifetime in seconds
SMLMSim.InteractionDiffusion.apply_boundary — Method
apply_boundary(e::AbstractDiffusingEmitter, box_size::Float64, boundary::String)Apply boundary conditions to an emitter (generic fallback).
Arguments
e::AbstractDiffusingEmitter: Emitter to apply boundary tobox_size::Float64: Simulation box size in micronsboundary::String: Boundary condition type ("periodic" or "reflecting")
Returns
AbstractDiffusingEmitter: Emitter with boundary conditions applied
SMLMSim.InteractionDiffusion.apply_boundary — Method
apply_boundary(e::DiffusingEmitter2D, box_size::Float64, boundary::String)Apply boundary conditions to a 2D emitter.
Arguments
e::DiffusingEmitter2D: Emitter to apply boundary tobox_size::Float64: Simulation box size in micronsboundary::String: Boundary condition type ("periodic" or "reflecting")
Returns
DiffusingEmitter2D: New emitter with position constrained to the box
SMLMSim.InteractionDiffusion.apply_boundary — Method
apply_boundary(e::DiffusingEmitter3D, box_size::Float64, boundary::String)Apply boundary conditions to a 3D emitter.
Arguments
e::DiffusingEmitter3D: Emitter to apply boundary tobox_size::Float64: Simulation box size in micronsboundary::String: Boundary condition type ("periodic" or "reflecting")
Returns
DiffusingEmitter3D: New emitter with position constrained to the box
SMLMSim.InteractionDiffusion.can_dimerize — Method
can_dimerize(e1::AbstractDiffusingEmitter, e2::AbstractDiffusingEmitter, r_react::Float64)Check if two emitters can form a dimer.
Arguments
e1::AbstractDiffusingEmitter: First emittere2::AbstractDiffusingEmitter: Second emitterr_react::Float64: Reaction radius in microns
Returns
Bool: True if emitters can form a dimer
SMLMSim.InteractionDiffusion.convert_to_diffusing_emitters — Function
convert_to_diffusing_emitters(emitters::Vector{<:AbstractEmitter}, photons::Float64=1000.0, state::Symbol=:monomer)Convert regular emitters to diffusing emitters for use as starting conditions.
Arguments
emitters::Vector{<:AbstractEmitter}: Vector of static emitters to convertphotons::Float64=1000.0: Number of photons to assignstate::Symbol=:monomer: Initial state (:monomer or :dimer)
Returns
Vector{<:AbstractDiffusingEmitter}: Vector of diffusing emitters
Example
# Convert static emitters to diffusing emitters
static_emitters = smld_static.emitters
diffusing_emitters = convert_to_diffusing_emitters(static_emitters)
# Use as starting conditions for a diffusion simulation
params = DiffusionSMLMConfig(t_max=10.0)
smld = simulate(params; starting_conditions=diffusing_emitters)SMLMSim.InteractionDiffusion.create_smld — Method
create_smld(emitters::Vector{<:AbstractDiffusingEmitter}, camera::AbstractCamera, params::DiffusionSMLMConfig)Convert a collection of diffusing emitters to a BasicSMLD object.
Arguments
emitters::Vector{<:AbstractDiffusingEmitter}: Collection of emitters from simulationcamera::AbstractCamera: Camera model for imagingparams::DiffusionSMLMConfig: Simulation parameters
Returns
BasicSMLD: SMLD containing all emitters for further analysis or visualization
SMLMSim.InteractionDiffusion.diffuse — Method
diffuse(e::DiffusingEmitter2D, diff_coef::Float64, dt::Float64)Create a new emitter with updated position based on Brownian motion.
Arguments
e::DiffusingEmitter2D: Emitter to updatediff_coef::Float64: Diffusion coefficient (μm²/s)dt::Float64: Time step (s)
Returns
DiffusingEmitter2D: New emitter with updated position
SMLMSim.InteractionDiffusion.diffuse — Method
diffuse(e::DiffusingEmitter3D, diff_coef::Float64, dt::Float64)Create a new 3D emitter with updated position based on Brownian motion.
Arguments
e::DiffusingEmitter3D: Emitter to updatediff_coef::Float64: Diffusion coefficient (μm²/s)dt::Float64: Time step (s)
Returns
DiffusingEmitter3D: New emitter with updated position
SMLMSim.InteractionDiffusion.diffuse_dimer — Method
diffuse_dimer(e1::DiffusingEmitter2D, e2::DiffusingEmitter2D, diff_trans::Float64, diff_rot::Float64, d_dimer::Float64, dt::Float64)Diffuse a dimer with both translational and rotational components.
Arguments
e1::DiffusingEmitter2D: First emitter in dimere2::DiffusingEmitter2D: Second emitter in dimerdiff_trans::Float64: Translational diffusion coefficient (μm²/s)diff_rot::Float64: Rotational diffusion coefficient (rad²/s)d_dimer::Float64: Dimer separation distance (μm)dt::Float64: Time step (s)
Returns
Tuple{DiffusingEmitter2D, DiffusingEmitter2D}: Two new emitters with updated positions
SMLMSim.InteractionDiffusion.diffuse_dimer — Method
diffuse_dimer(e1::DiffusingEmitter3D, e2::DiffusingEmitter3D, diff_trans::Float64, diff_rot::Float64, d_dimer::Float64, dt::Float64)Diffuse a 3D dimer with both translational and rotational components.
Arguments
e1::DiffusingEmitter3D: First emitter in dimere2::DiffusingEmitter3D: Second emitter in dimerdiff_trans::Float64: Translational diffusion coefficient (μm²/s)diff_rot::Float64: Rotational diffusion coefficient (rad²/s)d_dimer::Float64: Dimer separation distance (μm)dt::Float64: Time step (s)
Returns
Tuple{DiffusingEmitter3D, DiffusingEmitter3D}: Two new emitters with updated positions
SMLMSim.InteractionDiffusion.dimerize — Method
dimerize(e1::DiffusingEmitter2D, e2::DiffusingEmitter2D, d_dimer::Float64)Create two new emitters in dimer state from two monomers.
Arguments
e1::DiffusingEmitter2D: First emittere2::DiffusingEmitter2D: Second emitterd_dimer::Float64: Dimer separation distance in microns
Returns
Tuple{DiffusingEmitter2D, DiffusingEmitter2D}: Two new emitters in dimer state
SMLMSim.InteractionDiffusion.dimerize — Method
dimerize(e1::DiffusingEmitter3D, e2::DiffusingEmitter3D, d_dimer::Float64)Create two new emitters in dimer state from two monomers in 3D.
Arguments
e1::DiffusingEmitter3D: First emittere2::DiffusingEmitter3D: Second emitterd_dimer::Float64: Dimer separation distance in microns
Returns
Tuple{DiffusingEmitter3D, DiffusingEmitter3D}: Two new emitters in dimer state
SMLMSim.InteractionDiffusion.dissociate — Method
dissociate(e::DiffusingEmitter2D, emitters::Vector{<:AbstractDiffusingEmitter})Create two new monomers from a dimer.
Arguments
e::DiffusingEmitter2D: Emitter part of a dimeremitters::Vector{<:AbstractDiffusingEmitter}: All emitters in the system
Returns
Tuple{DiffusingEmitter2D, DiffusingEmitter2D}: Two new emitters in monomer state
SMLMSim.InteractionDiffusion.dissociate — Method
dissociate(e::DiffusingEmitter3D, emitters::Vector{<:AbstractDiffusingEmitter})Create two new monomers from a 3D dimer.
Arguments
e::DiffusingEmitter3D: Emitter part of a dimeremitters::Vector{<:AbstractDiffusingEmitter}: All emitters in the system
Returns
Tuple{DiffusingEmitter3D, DiffusingEmitter3D}: Two new emitters in monomer state
SMLMSim.InteractionDiffusion.extract_final_state — Method
extract_final_state(smld::SMLD)Extract the emitters from the final frame of a simulation to use as starting conditions.
Arguments
smld::SMLD: SMLD containing emitters from a simulation
Returns
Vector{<:AbstractEmitter}: Emitters from the final frame
Example
# Run a simulation
params = DiffusionSMLMConfig(t_max=5.0)
smld = simulate(params)
# Extract final state
final_state = extract_final_state(smld)
# Continue simulation with new parameters
params_new = DiffusionSMLMConfig(t_max=10.0, diff_monomer=0.2)
smld_continued = simulate(params_new; starting_conditions=final_state)SMLMSim.InteractionDiffusion.filter_by_state — Method
filter_by_state(smld::BasicSMLD, state::Symbol)Filter emitters by their state (monomer or dimer).
Arguments
smld::BasicSMLD: The original SMLD with diffusing emittersstate::Symbol: State to filter by (:monomer or :dimer)
Returns
BasicSMLD: New SMLD containing only emitters with the specified state
Example
# Get only monomers
monomer_smld = filter_by_state(smld, :monomer)
# Get only dimers
dimer_smld = filter_by_state(smld, :dimer)SMLMSim.InteractionDiffusion.get_dimers — Method
get_dimers(smld::BasicSMLD)Extract a new BasicSMLD containing only emitters in dimer state.
Arguments
smld::BasicSMLD: Original SMLD with all emitters
Returns
BasicSMLD: New SMLD containing only dimers
Example
# Extract only dimers from simulation results
smld = simulate(params)
dimer_smld = get_dimers(smld)SMLMSim.InteractionDiffusion.get_frame — Method
get_frame(smld::BasicSMLD, frame_num::Int)Extract emitters from a specific frame.
Arguments
smld::BasicSMLD: SMLD containing all emittersframe_num::Int: Frame number to extract
Returns
BasicSMLD: New SMLD containing only emitters from the specified frame
SMLMSim.InteractionDiffusion.get_monomers — Method
get_monomers(smld::BasicSMLD)Extract a new BasicSMLD containing only emitters in monomer state.
Arguments
smld::BasicSMLD: Original SMLD with all emitters
Returns
BasicSMLD: New SMLD containing only monomers
SMLMSim.InteractionDiffusion.initialize_emitters — Function
initialize_emitters(params::DiffusionSMLMConfig, photons::Float64=1000.0; override_count::Union{Nothing, Int}=nothing)Create initial emitter positions for the simulation.
Arguments
params::DiffusionSMLMConfig: Simulation parametersphotons::Float64=1000.0: Number of photons per emitteroverride_count::Union{Nothing, Int}=nothing: Optional override for the number of molecules
Returns
Vector{<:AbstractDiffusingEmitter}: Vector of initialized emitters
SMLMSim.InteractionDiffusion.should_dissociate — Method
should_dissociate(e::AbstractDiffusingEmitter, k_off::Float64, dt::Float64)Check if a dimer should dissociate based on stochastic rate.
Arguments
e::AbstractDiffusingEmitter: Emitter to checkk_off::Float64: Dissociation rate (s⁻¹)dt::Float64: Time step (s)
Returns
Bool: True if dimer should dissociate
SMLMSim.InteractionDiffusion.track_state_changes — Method
track_state_changes(smld::BasicSMLD)Track state changes of molecules over time.
Arguments
smld::BasicSMLD: SMLD containing all emitters
Returns
Dict{Int, Vector{Tuple{Int, Symbol}}}: Dictionary mapping molecule IDs to vectors of (frame, state) pairs
Example
# Track state changes of molecules
state_history = track_state_changes(smld)
# Plot state history for molecule 1
history = state_history[1]
frames = [h[1] for h in history]
states = [h[2] for h in history]SMLMSim.InteractionDiffusion.update_system — Method
update_system(emitters::Vector{<:AbstractDiffusingEmitter}, params::DiffusionSMLMConfig, dt::Float64)Update all emitters based on Smoluchowski diffusion dynamics.
Arguments
emitters::Vector{<:AbstractDiffusingEmitter}: Current emitters stateparams::DiffusionSMLMConfig: Simulation parametersdt::Float64: Time step
Returns
Vector{<:AbstractDiffusingEmitter}: Updated emitters
SMLMSim.simulate — Method
simulate(params::DiffusionSMLMConfig;
starting_conditions::Union{Nothing, SMLD, Vector{<:AbstractDiffusingEmitter}}=nothing,
photons::Float64=1000.0,
override_count::Union{Nothing, Int}=nothing,
kwargs...)Run a Smoluchowski diffusion simulation and return a BasicSMLD object with emitters that have both frame number and timestamp information.
Arguments
params::DiffusionSMLMConfig: Simulation parameters
Keyword Arguments
starting_conditions::Union{Nothing, SMLD, Vector{<:AbstractDiffusingEmitter}}=nothing: Optional starting emittersphotons::Float64=1000.0: Number of photons per emitteroverride_count::Union{Nothing, Int}=nothing: Optional override for the number of moleculescamera::Union{Nothing, AbstractCamera}=nothing: Camera model (default: IdealCamera with 100nm pixels)- If
nothing, creates IdealCamera with dimensions matching box_size - Can specify SCMOSCamera for realistic noise modeling
- If
- Any additional parameters are ignored (allows unified interface with other simulate methods)
Returns
Tuple{BasicSMLD, SimInfo}: (smld, info)- smld: SMLD object containing all emitters across all frames
- info: SimInfo containing timing and simulation statistics
Example
# Set up parameters with camera settings
params = DiffusionSMLMConfig(
density = 0.5, # molecules per μm²
box_size = 10.0, # μm
camera_framerate = 20.0, # 20 fps
camera_exposure = 0.04 # 40ms exposure
)
# Run basic simulation
smld, info = simulate(params)
# Run simulation with exactly 2 particles
smld, info = simulate(params; override_count=2)
# Use previous simulation state as starting conditions for a new simulation
final_frame = maximum([e.frame for e in smld.emitters])
final_state_emitters = filter(e -> e.frame == final_frame, smld.emitters)
smld_continued, info = simulate(params; starting_conditions=final_state_emitters)SMLMSim.CameraImages — Module
CameraImagesModule for generating simulated camera images from SMLM data.
This module provides functions to:
- Generate ideal camera images by integrating emitter photons over a PSF.
- Add realistic camera noise (e.g., Poisson noise).
Usage
using SMLMSim.CameraImagesSMLMSim.CameraImages.gen_image — Method
gen_image(smld::SMLD, psf::AbstractPSF, frame::Int; kwargs...) -> Tuple{Matrix{T}, ImageInfo} where T<:RealGenerate a single camera image for a specific frame from SMLD data. See gen_images for full documentation of parameters.
Returns
Tuple{Matrix{T}, ImageInfo}: (image, info)- image: 2D camera image as Matrix{T}
- info: ImageInfo containing timing and image statistics
SMLMSim.CameraImages.gen_images — Method
gen_images(smld::SMLD, psf::AbstractPSF; kwargs...) -> Tuple{Array{T, 3}, ImageInfo} where T<:RealGenerate camera images from SMLD data using the specified PSF model.
Arguments
smld::SMLD: Single molecule localization data containerpsf::AbstractPSF: Point spread function model
Keyword arguments
dataset::Int=1: Dataset number to use from SMLDframes=nothing: Specific frames to generate (default: all frames in smld.n_frames)support::Union{Real,Tuple{<:Real,<:Real,<:Real,<:Real}}=Inf: PSF support region size:Inf(default): Calculate PSF over the entire image (most accurate but slowest)Real: Circular region with specified radius (in microns) around each emitterTuple{<:Real,<:Real,<:Real,<:Real}: Explicit region as (xmin, xmax, ymin, ymax) in microns
sampling::Int=2: Supersampling factor for PSF integrationthreaded::Bool=true: Enable multithreading for faster computationbg::Float64=0.0: Background signal level (photons per pixel)poisson_noise::Bool=false: Apply Poisson noise only (for simple shot noise)camera_noise::Bool=false: Apply full camera noise model (requires SCMOSCamera)- For SCMOSCamera: applies QE, Poisson, read noise, gain, and offset
- For IdealCamera: ignored (use poisson_noise instead)
Returns
Tuple{Array{T,3}, ImageInfo}: (images, info)- images: 3D array of camera images with dimensions [height, width, num_frames]
- info: ImageInfo containing timing and image statistics
Performance Note
For the support parameter, using a finite radius (typically 3-5× the PSF width) provides a good balance between accuracy and performance. For example, with a PSF width of 0.15μm, a support radius of 0.5-1.0μm is usually sufficient.
SMLMSim.CameraImages.poisson_noise! — Method
poisson_noise!(image::AbstractArray{T}) where T<:Real -> nothingApply Poisson noise to an image or image stack in-place.
Arguments
image::AbstractArray{T}: Input image or image stack with values representing photon counts
Returns
nothing: The input array is modified in-place
Details
Same as poisson_noise, but modifies the input array directly instead of creating a new one. This can be more memory-efficient for large images or when processing multiple frames.
Example
# Add Poisson noise to an image in-place
image = ones(100, 100) * 100.0 # 100 expected photons per pixel
poisson_noise!(image) # image is modified in-placeSMLMSim.CameraImages.poisson_noise — Method
poisson_noise(image::AbstractArray{T}) where T<:Real -> Array{Float64}Apply Poisson noise to an image or image stack.
Arguments
image::AbstractArray{T}: Input image or image stack with values representing photon counts
Returns
- Array with same dimensions as input, with Poisson noise applied to each pixel
Details
This function creates a copy of the input array and applies Poisson noise to each pixel using the in-place poisson_noise! function.
Non-integer and negative values are handled specially:
- Non-integer values are accepted (treating them as expected photon counts)
- Negative values are clipped to zero before applying noise
- Zero values remain zero (as Poisson(0) always returns 0)
Example
# Add Poisson noise to a clean image
clean_image = ones(100, 100) * 100.0 # 100 expected photons per pixel
noisy_image = poisson_noise(clean_image)SMLMSim.CameraImages.scmos_noise! — Method
scmos_noise!(image::AbstractMatrix{T}, camera::SCMOSCamera) where T<:Real -> nothingApply realistic sCMOS camera noise model to an image in-place.
Arguments
image::AbstractMatrix{T}: Input image with values representing photon counts (modified in-place)camera::SCMOSCamera: sCMOS camera with calibration parameters
Returns
nothing: The input array is modified in-place
Details
Same as scmos_noise, but modifies the input array directly instead of creating a new one.
Example
camera = SCMOSCamera(128, 128, 0.1, 1.6)
image = ones(128, 128) * 100.0
scmos_noise!(image, camera) # image is modified in-placeSMLMSim.CameraImages.scmos_noise — Method
scmos_noise(image::AbstractMatrix{T}, camera::SCMOSCamera) where T<:Real -> Matrix{Float64}Apply realistic sCMOS camera noise model to an image.
Arguments
image::AbstractMatrix{T}: Input image with values representing photon countscamera::SCMOSCamera: sCMOS camera with calibration parameters (offset, gain, readnoise, qe)
Returns
- Matrix with same dimensions as input, with full sCMOS noise model applied
Details
The sCMOS noise model applies the following transformations in order:
- Quantum efficiency: Convert photons to photoelectrons
- Poisson noise: Shot noise on photoelectrons
- Read noise: Gaussian noise per pixel
- Gain: Convert electrons to ADU (analog-to-digital units)
- Offset: Add dark level
The process simulates the physical detection chain:
- QE: Not all photons generate photoelectrons
- Poisson: Fundamental shot noise
- Read noise: Electronic noise from amplifier/ADC
- Gain & Offset: Conversion to digital units
Example
# Create an sCMOS camera with 1.6 e⁻ read noise
camera = SCMOSCamera(128, 128, 0.1, 1.6)
# Apply realistic noise to a clean image
clean_image = ones(128, 128) * 100.0 # 100 photons per pixel
noisy_image = scmos_noise(clean_image, camera)