API Reference

This page provides a comprehensive reference for the types and functions in SMLMSim.

SMLMSim.SMLMSimModule
SMLMSim

Main module for the SMLMSim.jl package.

API Overview

For a comprehensive overview of the API, use the help mode on api:

?api

Or 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)
source
SMLMSim.ImageInfoType
ImageInfo

Metadata from image generation functions.

Fields

  • elapsed_s::Float64: Wall-clock time for image generation 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 across all frames
  • output_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)")
source
SMLMSim.SimInfoType
SimInfo

Metadata and intermediate results from simulation functions.

Fields

  • elapsed_s::Float64: Wall-clock time for simulation in seconds
  • backend::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 generated
  • n_emitters::Int: Total emitters simulated
  • n_localizations::Int: Total localizations generated
  • n_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
end
source
SMLMSim.apiMethod

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 parameters
      • StaticSMLMConfig: Parameters for static SMLM simulation
      • DiffusionSMLMConfig: Parameters for diffusion simulation
  • AbstractSMLMInfo: Base type for info structs (from SMLMData)

    • SimInfo: Metadata and intermediate results from simulation functions
    • ImageInfo: Metadata from image generation functions
  • Pattern: Base type for all molecular patterns

    • Pattern2D: Base type for 2D patterns
      • Nmer2D: N molecules arranged in a circle
      • Line2D: Molecules arranged along a line
    • Pattern3D: Base type for 3D patterns
      • Nmer3D: N molecules arranged in a circle in 3D
      • Line3D: Molecules arranged along a 3D line
  • AbstractLabeling: Base type for labeling strategies

    • FixedLabeling: Deterministic number of fluorophores per site
    • PoissonLabeling: Poisson-distributed number of fluorophores per site
    • BinomialLabeling: Binomial-distributed number of fluorophores per site
  • Molecule: Base type for all photophysical models

    • GenericFluor: General fluorophore with kinetic state model
  • AbstractDiffusingEmitter: Base type for diffusing emitters

    • DiffusingEmitter2D: 2D emitter with diffusion state
    • DiffusingEmitter3D: 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]
end

DiffusionSMLMConfig

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)
end

Molecular 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
end

Line2D

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
end

Info 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
end

ImageInfo

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)
end

Fluorophore 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
end

Labeling 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)
end

PoissonLabeling

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)
end

Note: 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)
end

Constructor 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 μm

Image 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

  1. Define simulation parameters
  2. Create a pattern (or use default)
  3. Choose a labeling strategy (or use default FixedLabeling)
  4. Define a fluorophore model (or use default)
  5. Run simulation to get noisy localizations and info (with intermediate results)
  6. 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

  1. Define diffusion parameters
  2. Run simulation to get emitter trajectories
  3. Analyze the diffusion and interaction dynamics
  4. 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.

source
SMLMSim.simulateMethod
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 object
  • kwargs...: 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)
source
SMLMSim.CoreModule
Core

Core module with shared utilities for SMLM simulation.

This module contains fundamental components that can be used across different simulation types (static, diffusion, etc.) including:

  1. Abstract types for simulation
  2. Molecule and pattern definitions
  3. CTMC (Continuous Time Markov Chain) for stochastic state transitions
  4. Photophysics modeling for blinking kinetics and detection

Usage

using SMLMSim.Core
source
SMLMSim.Core.AbstractLabelingType
AbstractLabeling

Abstract 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 site
  • PoissonLabeling: Poisson-distributed number of fluorophores per site
  • BinomialLabeling: Binomial-distributed number of fluorophores per site
source
SMLMSim.Core.BinomialLabelingType
BinomialLabeling <: AbstractLabeling

Binomial-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 site
  • p::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)
source
SMLMSim.Core.CTMCType
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 span
  • transition_times::Vector{T}: Time points at which state changes occurred, starting at 0.0
  • states::Vector{U}: Sequence of states entered at each transition time, starting with initial state

Type Parameters

  • T: Floating point type for time values
  • U: 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].

source
SMLMSim.Core.CTMCMethod
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 i
  • simulation_time::T: Total time to simulate
  • state1::Int: Initial state

Returns

  • CTMC{T,Int}: Simulated CTMC with transition times and states

Details

Simulates a CTMC using the Gillespie algorithm:

  1. Start in state1 at time 0
  2. 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
  3. Repeat until exceeding simulation_time
source
SMLMSim.Core.FixedLabelingType
FixedLabeling <: AbstractLabeling

Deterministic labeling with exactly n fluorophores per site.

Fields

  • n::Int: Number of fluorophores per site
  • efficiency::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)
source
SMLMSim.Core.GenericFluorType
GenericFluor <: Molecule

Defines a fluorophore with photophysical properties.

Fields

  • γ::AbstractFloat: Photon emission rate in Hz. Default: 1e5
  • q::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)
source
SMLMSim.Core.GenericFluorMethod
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 Hz
  • k_off::AbstractFloat: Off-switching rate (on→off) in Hz
  • k_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).

source
SMLMSim.Core.Line2DType
Line2D <: Pattern2D

Points with uniform random distribution between two endpoints.

Fields

  • λ::Float64: Linear molecule density (molecules per micron)
  • endpoints::Vector{Tuple{Float64,Float64}}: Vector of endpoint coordinates
  • 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

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)])
source
SMLMSim.Core.Line3DType
Line3D <: Pattern3D

Points 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 coordinates
  • 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
  • z::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)])
source
SMLMSim.Core.MoleculeType
Molecule

Abstract 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.

source
SMLMSim.Core.Nmer2DType
Nmer2D <: Pattern2D

N molecules symmetrically organized around a circle with diameter d.

Fields

  • 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

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)
source
SMLMSim.Core.Nmer3DType
Nmer3D <: Pattern3D

N molecules symmetrically organized around a circle with diameter d at z=0.

Fields

  • 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
  • z::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)
source
SMLMSim.Core.PoissonLabelingType
PoissonLabeling <: AbstractLabeling

Poisson-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.

source
SMLMSim.Core.SMLMSimParamsType
SMLMSimParams <: AbstractSMLMConfig

Abstract type for all SMLM simulation parameter types. Inherits from SMLMData.AbstractSMLMConfig to participate in the ecosystem-wide (Config, Info, Data) tuple pattern.

source
SMLMSim.Core.apply_labelingMethod
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 3D
  • labeling::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))
source
SMLMSim.Core.apply_labelingMethod
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 3D
  • pattern_ids::Vector{Int}: Pattern instance ID for each binding site
  • labeling::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))
source
SMLMSim.Core.compute_equilibrium_distributionMethod
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.

source
SMLMSim.Core.get_nextMethod
get_next(ctmc::CTMC, t::AbstractFloat)

Get the next state transition after a specific time point.

Arguments

  • ctmc::CTMC: The CTMC to query
  • t::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.

source
SMLMSim.Core.get_num_tracksMethod
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)
source
SMLMSim.Core.get_stateMethod
get_state(ctmc::CTMC, t::AbstractFloat)

Get the state of the CTMC at a specific time point.

Arguments

  • ctmc::CTMC: The CTMC to query
  • t::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.

source
SMLMSim.Core.get_trackMethod
get_track(smld::BasicSMLD, id::Int)

Return a new SMLD containing only emitters with the specified track_id.

Arguments

  • smld::BasicSMLD: The original SMLD
  • id::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)
source
SMLMSim.Core.get_tracksMethod
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]
source
SMLMSim.Core.intensity_traceMethod
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 simulate
  • framerate::Real: Frame rate in Hz
  • state1::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:

  1. Determines state occupancy using CTMC (which starts at time 0, but recording starts at burn_in)
  2. Integrates emission (rate f.γ) during fluorescent state periods
  3. 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
source
SMLMSim.Core.kinetic_modelMethod
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 positions
  • f::Molecule: Fluorophore model with kinetic rates
  • nframes::Int: Number of frames to simulate
  • framerate::Real: Frame rate in Hz
  • ndatasets::Int=1: Number of independent datasets to generate
  • minphotons::Float64=50.0: Minimum photons for detection
  • state1::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:

  1. Simulates fluorophore blinking using the kinetic model
  2. Creates emitters for frames where photon count exceeds threshold
  3. Preserves track_id for linking emitters from same position
  4. 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.

source
SMLMSim.Core.n_fluorophoresMethod
n_fluorophores(labeling::AbstractLabeling) -> Int

Sample 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)
source
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 degrees
source
SMLMSim.Core.rotate!Method
rotate!(p::Pattern3D, R::Matrix{Float64})

Rotate a 3D pattern by rotation matrix R.

Arguments

  • p::Pattern3D: Pattern to rotate
  • R::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)
source
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)
source
SMLMSim.Core.sample_discreteMethod
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.

source
SMLMSim.Core.sample_discrete_with_probsMethod
sample_discrete_with_probs(indices, probs)

Sample a discrete value from indices with corresponding probabilities.

Arguments

  • indices::Vector{Int}: Vector of possible indices to sample
  • probs::Vector{<:AbstractFloat}: Corresponding probabilities

Returns

  • Int: Sampled index
source
SMLMSim.Core.uniform2DMethod
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 replicate
  • field_x::Float64: Field width in microns
  • field_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)
source
SMLMSim.Core.uniform3DMethod
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 replicate
  • field_x::Float64: Field width in microns
  • field_y::Float64: Field height in microns
  • zrange::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])
source
SMLMSim.StaticSMLMModule
StaticSMLM

Module for simulating static (non-diffusing) SMLM data with blinking kinetics.

This module provides functionality for:

  1. Generating spatial distributions of emitters based on patterns
  2. Simulating fluorophore blinking based on stochastic kinetic models
  3. Adding realistic localization uncertainties
  4. Creating complete SMLM datasets

Usage

using SMLMSim.StaticSMLM
source
SMLMSim.StaticSMLM.StaticSMLMConfigType
StaticSMLMConfig <: SMLMSimParams

Parameters for static SMLM simulation.

Fields

  • density::Float64: density in particles per square micron
  • σ_psf::Float64: PSF width in microns
  • minphotons::Int: minimum photons for detection
  • ndatasets::Int: number of datasets to simulate
  • nframes::Int: number of frames per dataset
  • framerate::Float64: frames per second
  • ndims::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
)
source
SMLMSim.StaticSMLM.add_coordinate_noiseMethod
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.

source
SMLMSim.StaticSMLM.apply_noiseMethod
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 width
source
SMLMSim.StaticSMLM.apply_noiseMethod
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)
source
SMLMSim.simulateMethod
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 parameters
  • starting_conditions::Union{Nothing, SMLD, Vector{<:AbstractEmitter}}: Optional starting conditions instead of generating patterns
  • pattern::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 simulation
  • camera::AbstractCamera: Camera model for detection simulation
  • state1::Union{Int, Symbol}=:equilibrium: Initial fluorophore state:
    • ::Int: Specific state (1=on, 2=off typically)
    • :equilibrium: Sample from equilibrium distribution (default)
    For models with photobleaching (absorbing states), use 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.σ_psf value 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_conditions is 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.
source
SMLMSim.InteractionDiffusionModule
InteractionDiffusion

This 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)
source
SMLMSim.InteractionDiffusion.DiffusingEmitter2DType
DiffusingEmitter2D{T<:AbstractFloat} <: AbstractDiffusingEmitter

A 2D emitter type for diffusion simulations that contains both spatial and temporal information, plus molecular state information.

Fields

  • x::T: x-coordinate in microns
  • y::T: y-coordinate in microns
  • photons::T: number of photons emitted
  • timestamp::T: actual simulation time in seconds
  • frame::Int: camera frame number based on framerate and exposure
  • dataset::Int: dataset identifier
  • track_id::Int: unique molecule identifier
  • state::Symbol: molecular state (:monomer or :dimer)
  • partner_id::Union{Int,Nothing}: ID of linked molecule (for dimers), or nothing for monomers
source
SMLMSim.InteractionDiffusion.DiffusingEmitter3DType
DiffusingEmitter3D{T<:AbstractFloat} <: AbstractDiffusingEmitter

A 3D emitter type for diffusion simulations that contains both spatial and temporal information, plus molecular state information.

Fields

  • x::T: x-coordinate in microns
  • y::T: y-coordinate in microns
  • z::T: z-coordinate in microns
  • photons::T: number of photons emitted
  • timestamp::T: actual simulation time in seconds
  • frame::Int: camera frame number based on framerate and exposure
  • dataset::Int: dataset identifier
  • track_id::Int: unique molecule identifier
  • state::Symbol: molecular state (:monomer or :dimer)
  • partner_id::Union{Int,Nothing}: ID of linked molecule (for dimers), or nothing for monomers
source
SMLMSim.InteractionDiffusion.DiffusionSMLMConfigType
DiffusionSMLMConfig <: SMLMSimParams

Parameters 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
)
source
Base.copyMethod
Base.copy(e::DiffusingEmitter2D)

Create a copy of a 2D diffusing emitter.

source
Base.copyMethod
Base.copy(e::DiffusingEmitter3D)

Create a copy of a 3D diffusing emitter.

source
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 frames
  • emitters::Vector{<:AbstractDiffusingEmitter}: Current emitters from simulation
  • time::Float64: Current simulation time
  • frame_num::Int: Current frame number
  • params::DiffusionSMLMConfig: Simulation parameters

Returns

  • Nothing
source
SMLMSim.InteractionDiffusion.analyze_dimer_fractionMethod
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")
source
SMLMSim.InteractionDiffusion.apply_boundaryMethod
apply_boundary(e::AbstractDiffusingEmitter, box_size::Float64, boundary::String)

Apply boundary conditions to an emitter (generic fallback).

Arguments

  • e::AbstractDiffusingEmitter: Emitter to apply boundary to
  • box_size::Float64: Simulation box size in microns
  • boundary::String: Boundary condition type ("periodic" or "reflecting")

Returns

  • AbstractDiffusingEmitter: Emitter with boundary conditions applied
source
SMLMSim.InteractionDiffusion.apply_boundaryMethod
apply_boundary(e::DiffusingEmitter2D, box_size::Float64, boundary::String)

Apply boundary conditions to a 2D emitter.

Arguments

  • e::DiffusingEmitter2D: Emitter to apply boundary to
  • box_size::Float64: Simulation box size in microns
  • boundary::String: Boundary condition type ("periodic" or "reflecting")

Returns

  • DiffusingEmitter2D: New emitter with position constrained to the box
source
SMLMSim.InteractionDiffusion.apply_boundaryMethod
apply_boundary(e::DiffusingEmitter3D, box_size::Float64, boundary::String)

Apply boundary conditions to a 3D emitter.

Arguments

  • e::DiffusingEmitter3D: Emitter to apply boundary to
  • box_size::Float64: Simulation box size in microns
  • boundary::String: Boundary condition type ("periodic" or "reflecting")

Returns

  • DiffusingEmitter3D: New emitter with position constrained to the box
source
SMLMSim.InteractionDiffusion.can_dimerizeMethod
can_dimerize(e1::AbstractDiffusingEmitter, e2::AbstractDiffusingEmitter, r_react::Float64)

Check if two emitters can form a dimer.

Arguments

  • e1::AbstractDiffusingEmitter: First emitter
  • e2::AbstractDiffusingEmitter: Second emitter
  • r_react::Float64: Reaction radius in microns

Returns

  • Bool: True if emitters can form a dimer
source
SMLMSim.InteractionDiffusion.convert_to_diffusing_emittersFunction
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 convert
  • photons::Float64=1000.0: Number of photons to assign
  • state::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)
source
SMLMSim.InteractionDiffusion.create_smldMethod
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 simulation
  • camera::AbstractCamera: Camera model for imaging
  • params::DiffusionSMLMConfig: Simulation parameters

Returns

  • BasicSMLD: SMLD containing all emitters for further analysis or visualization
source
SMLMSim.InteractionDiffusion.diffuseMethod
diffuse(e::DiffusingEmitter2D, diff_coef::Float64, dt::Float64)

Create a new emitter with updated position based on Brownian motion.

Arguments

  • e::DiffusingEmitter2D: Emitter to update
  • diff_coef::Float64: Diffusion coefficient (μm²/s)
  • dt::Float64: Time step (s)

Returns

  • DiffusingEmitter2D: New emitter with updated position
source
SMLMSim.InteractionDiffusion.diffuseMethod
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 update
  • diff_coef::Float64: Diffusion coefficient (μm²/s)
  • dt::Float64: Time step (s)

Returns

  • DiffusingEmitter3D: New emitter with updated position
source
SMLMSim.InteractionDiffusion.diffuse_dimerMethod
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 dimer
  • e2::DiffusingEmitter2D: Second emitter in dimer
  • diff_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
source
SMLMSim.InteractionDiffusion.diffuse_dimerMethod
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 dimer
  • e2::DiffusingEmitter3D: Second emitter in dimer
  • diff_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
source
SMLMSim.InteractionDiffusion.dimerizeMethod
dimerize(e1::DiffusingEmitter2D, e2::DiffusingEmitter2D, d_dimer::Float64)

Create two new emitters in dimer state from two monomers.

Arguments

  • e1::DiffusingEmitter2D: First emitter
  • e2::DiffusingEmitter2D: Second emitter
  • d_dimer::Float64: Dimer separation distance in microns

Returns

  • Tuple{DiffusingEmitter2D, DiffusingEmitter2D}: Two new emitters in dimer state
source
SMLMSim.InteractionDiffusion.dimerizeMethod
dimerize(e1::DiffusingEmitter3D, e2::DiffusingEmitter3D, d_dimer::Float64)

Create two new emitters in dimer state from two monomers in 3D.

Arguments

  • e1::DiffusingEmitter3D: First emitter
  • e2::DiffusingEmitter3D: Second emitter
  • d_dimer::Float64: Dimer separation distance in microns

Returns

  • Tuple{DiffusingEmitter3D, DiffusingEmitter3D}: Two new emitters in dimer state
source
SMLMSim.InteractionDiffusion.dissociateMethod
dissociate(e::DiffusingEmitter2D, emitters::Vector{<:AbstractDiffusingEmitter})

Create two new monomers from a dimer.

Arguments

  • e::DiffusingEmitter2D: Emitter part of a dimer
  • emitters::Vector{<:AbstractDiffusingEmitter}: All emitters in the system

Returns

  • Tuple{DiffusingEmitter2D, DiffusingEmitter2D}: Two new emitters in monomer state
source
SMLMSim.InteractionDiffusion.dissociateMethod
dissociate(e::DiffusingEmitter3D, emitters::Vector{<:AbstractDiffusingEmitter})

Create two new monomers from a 3D dimer.

Arguments

  • e::DiffusingEmitter3D: Emitter part of a dimer
  • emitters::Vector{<:AbstractDiffusingEmitter}: All emitters in the system

Returns

  • Tuple{DiffusingEmitter3D, DiffusingEmitter3D}: Two new emitters in monomer state
source
SMLMSim.InteractionDiffusion.extract_final_stateMethod
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)
source
SMLMSim.InteractionDiffusion.filter_by_stateMethod
filter_by_state(smld::BasicSMLD, state::Symbol)

Filter emitters by their state (monomer or dimer).

Arguments

  • smld::BasicSMLD: The original SMLD with diffusing emitters
  • state::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)
source
SMLMSim.InteractionDiffusion.get_dimersMethod
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)
source
SMLMSim.InteractionDiffusion.get_frameMethod
get_frame(smld::BasicSMLD, frame_num::Int)

Extract emitters from a specific frame.

Arguments

  • smld::BasicSMLD: SMLD containing all emitters
  • frame_num::Int: Frame number to extract

Returns

  • BasicSMLD: New SMLD containing only emitters from the specified frame
source
SMLMSim.InteractionDiffusion.get_monomersMethod
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
source
SMLMSim.InteractionDiffusion.initialize_emittersFunction
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 parameters
  • photons::Float64=1000.0: Number of photons per emitter
  • override_count::Union{Nothing, Int}=nothing: Optional override for the number of molecules

Returns

  • Vector{<:AbstractDiffusingEmitter}: Vector of initialized emitters
source
SMLMSim.InteractionDiffusion.should_dissociateMethod
should_dissociate(e::AbstractDiffusingEmitter, k_off::Float64, dt::Float64)

Check if a dimer should dissociate based on stochastic rate.

Arguments

  • e::AbstractDiffusingEmitter: Emitter to check
  • k_off::Float64: Dissociation rate (s⁻¹)
  • dt::Float64: Time step (s)

Returns

  • Bool: True if dimer should dissociate
source
SMLMSim.InteractionDiffusion.track_state_changesMethod
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]
source
SMLMSim.InteractionDiffusion.update_systemMethod
update_system(emitters::Vector{<:AbstractDiffusingEmitter}, params::DiffusionSMLMConfig, dt::Float64)

Update all emitters based on Smoluchowski diffusion dynamics.

Arguments

  • emitters::Vector{<:AbstractDiffusingEmitter}: Current emitters state
  • params::DiffusionSMLMConfig: Simulation parameters
  • dt::Float64: Time step

Returns

  • Vector{<:AbstractDiffusingEmitter}: Updated emitters
source
SMLMSim.simulateMethod
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 emitters
  • photons::Float64=1000.0: Number of photons per emitter
  • override_count::Union{Nothing, Int}=nothing: Optional override for the number of molecules
  • camera::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
  • 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)
source
SMLMSim.CameraImagesModule
CameraImages

Module for generating simulated camera images from SMLM data.

This module provides functions to:

  1. Generate ideal camera images by integrating emitter photons over a PSF.
  2. Add realistic camera noise (e.g., Poisson noise).

Usage

using SMLMSim.CameraImages
source
SMLMSim.CameraImages.gen_imageMethod
gen_image(smld::SMLD, psf::AbstractPSF, frame::Int; kwargs...) -> Tuple{Matrix{T}, ImageInfo} where T<:Real

Generate 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
source
SMLMSim.CameraImages.gen_imagesMethod
gen_images(smld::SMLD, psf::AbstractPSF; kwargs...) -> Tuple{Array{T, 3}, ImageInfo} where T<:Real

Generate camera images from SMLD data using the specified PSF model.

Arguments

  • smld::SMLD: Single molecule localization data container
  • psf::AbstractPSF: Point spread function model

Keyword arguments

  • dataset::Int=1: Dataset number to use from SMLD
  • frames=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 emitter
    • Tuple{<:Real,<:Real,<:Real,<:Real}: Explicit region as (xmin, xmax, ymin, ymax) in microns
  • sampling::Int=2: Supersampling factor for PSF integration
  • threaded::Bool=true: Enable multithreading for faster computation
  • bg::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.

source
SMLMSim.CameraImages.poisson_noise!Method
poisson_noise!(image::AbstractArray{T}) where T<:Real -> nothing

Apply 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-place
source
SMLMSim.CameraImages.poisson_noiseMethod
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)
source
SMLMSim.CameraImages.scmos_noise!Method
scmos_noise!(image::AbstractMatrix{T}, camera::SCMOSCamera) where T<:Real -> nothing

Apply 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-place
source
SMLMSim.CameraImages.scmos_noiseMethod
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 counts
  • camera::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:

  1. Quantum efficiency: Convert photons to photoelectrons
  2. Poisson noise: Shot noise on photoelectrons
  3. Read noise: Gaussian noise per pixel
  4. Gain: Convert electrons to ADU (analog-to-digital units)
  5. 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)
source