Skip to content

Commit 8cd4595

Browse files
authored
Merge pull request #31 from JuliaSMLM/feature/tuple-pattern
Add tuple-pattern return format for simulate and gen_images
2 parents 55ab1a0 + 464bc8b commit 8cd4595

37 files changed

+1520
-443
lines changed

CLAUDE.md

Lines changed: 91 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,93 @@
1-
# SMLMSim.jl Development Guide
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
24

35
## Build, Test & Run Commands
4-
- Install dependencies: `julia --project -e "using Pkg; Pkg.instantiate()"`
5-
- Run all tests: `julia --project -e "using Pkg; Pkg.test()"`
6-
- Run specific test: `julia --project -e "using Pkg; Pkg.test(\"SMLMSim\", test_args=[\"Patterns\"])"`
7-
- Build docs: `julia --project=docs/ docs/make.jl`
8-
- Run benchmark: `julia --project=dev/ dev/benchmark.jl`
9-
10-
## Code Style Guidelines
11-
- Use physical units consistently (μm for space, seconds for time)
12-
- Follow Julia naming conventions: lowercase for functions, CamelCase for types
13-
- Organize imports: standard library first, then external packages
14-
- Place exports at module level, grouped by functionality
15-
- Type parameters should be explicit for functions with multiple dispatch
16-
- Error handling: use descriptive errors with parameter names and constraints
17-
- Documentation: include docstrings with examples and physical units
18-
- Re-export SMLMData types rather than redefining them
19-
- Follow existing patterns for defining new molecule or pattern types
20-
- Tests should use the `@test` macro with appropriate tolerances for floating-point comparisons
21-
22-
## Module Structure
23-
Functions should be organized in appropriate submodules: core, static, or diffusion.
6+
7+
```bash
8+
# Install dependencies
9+
julia --project -e "using Pkg; Pkg.instantiate()"
10+
11+
# Run all tests
12+
julia --project -e "using Pkg; Pkg.test()"
13+
14+
# Run specific test file (matches testset names like "Patterns", "Static", "Core")
15+
julia --project -e "using Pkg; Pkg.test(\"SMLMSim\", test_args=[\"Patterns\"])"
16+
17+
# Build documentation
18+
julia --project=docs/ docs/make.jl
19+
20+
# Run dev scripts
21+
julia --project=dev/ dev/benchmark.jl
22+
```
23+
24+
## Architecture Overview
25+
26+
SMLMSim simulates Single Molecule Localization Microscopy (SMLM) data. The package has four main modules that build on SMLMData.jl types:
27+
28+
```
29+
SMLMSim (main module - re-exports from submodules)
30+
├── Core - Shared types and photophysics
31+
├── StaticSMLM - Fixed emitter simulation with blinking
32+
├── InteractionDiffusion - Diffusing/interacting molecules (SPT)
33+
└── CameraImages - Generate camera images from emitters
34+
```
35+
36+
### Simulation Pipeline (Static)
37+
38+
```
39+
Pattern (geometry)
40+
→ uniform2D/3D() → binding site coordinates
41+
→ apply_labeling() → fluorophore positions (via AbstractLabeling)
42+
→ create emitters → smld_true (BasicSMLD)
43+
→ kinetic_model() → smld_model (with blinking, uses Molecule)
44+
→ apply_noise() → smld_noisy (with localization uncertainty)
45+
```
46+
47+
Key separation of concerns:
48+
- **Pattern**: Spatial geometry of binding sites (Nmer2D, Line3D, etc.)
49+
- **Labeling**: How many fluorophores per site (FixedLabeling, PoissonLabeling, BinomialLabeling)
50+
- **Molecule**: Photophysics/blinking kinetics (GenericFluor with CTMC rate matrix)
51+
52+
### Type Hierarchy
53+
54+
```julia
55+
# Patterns (geometry)
56+
Pattern Pattern2D Nmer2D, Line2D
57+
Pattern3D Nmer3D, Line3D
58+
59+
# Labeling (fluorophore attachment statistics)
60+
AbstractLabeling FixedLabeling, PoissonLabeling, BinomialLabeling
61+
62+
# Molecules (photophysics)
63+
Molecule GenericFluor
64+
65+
# Simulation params
66+
SMLMSimParams StaticSMLMConfig, DiffusionSMLMConfig
67+
```
68+
69+
### Key Files by Function
70+
71+
| Purpose | Files |
72+
|---------|-------|
73+
| Pattern geometry | `src/core/patterns.jl` |
74+
| Labeling statistics | `src/core/labeling.jl` |
75+
| Photophysics/blinking | `src/core/molecules.jl`, `src/core/photophysics.jl` |
76+
| CTMC (state transitions) | `src/core/ctmc.jl` |
77+
| Static simulation | `src/static/simulation.jl`, `src/static/parameters.jl` |
78+
| Diffusion simulation | `src/diffusion/smoluchowski.jl`, `src/diffusion/types.jl` |
79+
| Image generation | `src/camera_images/gen_images.jl`, `src/camera_images/noise.jl` |
80+
81+
### Dispatch Pattern
82+
83+
The `simulate()` function dispatches on parameter type:
84+
- `simulate(params::StaticSMLMConfig; ...)` → static simulation (returns 3-tuple)
85+
- `simulate(params::DiffusionSMLMConfig; ...)` → diffusion simulation (returns BasicSMLD)
86+
87+
## Code Style
88+
89+
- Physical units: μm for space, seconds for time
90+
- Re-export SMLMData types rather than redefining
91+
- Follow existing patterns when adding new Pattern, Labeling, or Molecule types
92+
- Type parameters explicit for multiple dispatch functions
93+
- Tests use `@test` with tolerances for floating-point comparisons

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "SMLMSim"
22
uuid = "5a0a87e4-02d4-41f5-bf26-b8c94c784a04"
3-
version = "0.4.0"
3+
version = "0.6.0"
44
authors = ["klidke@unm.edu"]
55

66
[deps]
@@ -14,7 +14,7 @@ Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
1414
[compat]
1515
Distributions = "0.25"
1616
MicroscopePSFs = "0.5"
17-
SMLMData = "0.4, 0.5"
17+
SMLMData = "0.7"
1818
julia = "1.6"
1919

2020
[extras]

README.md

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,19 +35,20 @@ using SMLMSim
3535

3636
# Define a camera and simulation parameters
3737
camera = IdealCamera(128, 128, 0.1) # 128×128 pixels, 100nm pixels
38-
params = StaticSMLMParams(density=1.0, σ_psf=0.13) # Density 1/μm², PSF 130nm
38+
params = StaticSMLMConfig(density=1.0, σ_psf=0.13) # Density 1/μm², PSF 130nm
3939

4040
# Run simulation for an 8-molecule ring pattern
41-
smld_true, smld_model, smld_noisy = simulate(
41+
smld_noisy, info = simulate(
4242
params; # Use semicolon to separate positional and keyword arguments
4343
pattern=Nmer2D(n=8, d=0.1), # 100nm diameter ring
4444
camera=camera
4545
)
4646

4747
# smld_noisy contains realistic SMLM coordinates
48+
# info.smld_true and info.smld_model contain intermediate results
4849
println("Generated $(length(smld_noisy.emitters)) localizations.")
4950
```
50-
*Output:* `smld_true` (ground truth), `smld_model` (kinetics), `smld_noisy` (kinetics + noise).
51+
*Output:* `smld_noisy` (kinetics + noise), `info` (SimInfo with `smld_true`, `smld_model`, timing).
5152

5253
### Diffusion & Interaction Simulation
5354

@@ -57,7 +58,7 @@ Simulate molecules diffusing and interacting (e.g., dimerization).
5758
using SMLMSim
5859

5960
# Set diffusion simulation parameters
60-
params = DiffusionSMLMParams(
61+
params = DiffusionSMLMConfig(
6162
density = 0.5, # molecules per μm²
6263
box_size = 10.0, # μm
6364
diff_monomer = 0.1, # μm²/s
@@ -69,7 +70,7 @@ params = DiffusionSMLMParams(
6970
)
7071

7172
# Run diffusion simulation
72-
smld = simulate(params) # Returns a BasicSMLD object with all emitters
73+
smld, info = simulate(params) # Returns (BasicSMLD, SimInfo)
7374

7475
println("Simulated diffusion for $(params.t_max) seconds.")
7576
# 'smld' can be used for analysis or image generation
@@ -108,15 +109,15 @@ Create camera images from simulation results.
108109
using MicroscopePSFs # Needed for PSF types
109110

110111
# Generate images from diffusion simulation output
111-
# Note: Frame timing is controlled by DiffusionSMLMParams (camera_framerate, camera_exposure)
112+
# Note: Frame timing is controlled by DiffusionSMLMConfig (camera_framerate, camera_exposure)
112113
# Multiple simulation timesteps are automatically integrated during simulate()
113114
psf = GaussianPSF(0.15) # 150nm PSF width
114-
images = gen_images(smld, psf;
115+
images, img_info = gen_images(smld, psf;
115116
support=1.0, # PSF support radius in μm (faster than default Inf)
116117
poisson_noise=true # Add shot noise
117118
)
118119

119-
println("Generated $(size(images,3)) camera images.")
120+
println("Generated $(img_info.frames_generated) camera images.")
120121
```
121122

122123
### sCMOS Camera with Realistic Noise
@@ -131,8 +132,8 @@ using MicroscopePSFs
131132
camera_scmos = SCMOSCamera(128, 128, 0.1, 1.6)
132133

133134
# Run static simulation with sCMOS camera
134-
params = StaticSMLMParams(density=1.0, σ_psf=0.13)
135-
smld_true, smld_model, smld_noisy = simulate(
135+
params = StaticSMLMConfig(density=1.0, σ_psf=0.13)
136+
smld_noisy, info = simulate(
136137
params,
137138
pattern=Nmer2D(n=8, d=0.1),
138139
camera=camera_scmos
@@ -141,11 +142,11 @@ smld_true, smld_model, smld_noisy = simulate(
141142
# Generate images with full sCMOS noise model
142143
# (quantum efficiency, Poisson, read noise, gain, offset)
143144
psf = GaussianPSF(0.15)
144-
images_scmos = gen_images(smld_noisy, psf, bg=10.0, camera_noise=true)
145+
images_scmos, img_info = gen_images(smld_noisy, psf, bg=10.0, camera_noise=true)
145146

146147
# For diffusion simulations
147-
diff_params = DiffusionSMLMParams(density=0.5, box_size=10.0)
148-
smld_diff = simulate(diff_params; camera=camera_scmos, override_count=10)
148+
diff_params = DiffusionSMLMConfig(density=0.5, box_size=10.0)
149+
smld_diff, diff_info = simulate(diff_params; camera=camera_scmos, override_count=10)
149150
```
150151

151152
The sCMOS noise model applies:
@@ -164,8 +165,8 @@ using MicroscopePSFs
164165

165166
# --- Simulation Setup ---
166167
camera = IdealCamera(128, 128, 0.1) # 128×128 pixels, 100nm pixels
167-
params = StaticSMLMParams(density=1.0, σ_psf=0.13)
168-
smld_true, smld_model, smld_noisy = simulate(
168+
params = StaticSMLMConfig(density=1.0, σ_psf=0.13)
169+
smld_noisy, info = simulate(
169170
params,
170171
pattern=Nmer2D(n=6, d=0.2), # Hexamer
171172
camera=camera
@@ -202,24 +203,24 @@ using Statistics
202203
camera_scmos = SCMOSCamera(64, 64, 0.1, 1.6) # 64×64 pixels, 100nm/px, 1.6 e⁻ read noise
203204

204205
# Run diffusion simulation
205-
params = DiffusionSMLMParams(
206+
params = DiffusionSMLMConfig(
206207
density = 1.0, # 1 molecule/μm²
207208
box_size = 6.4, # 6.4×6.4 μm field
208209
diff_monomer = 0.1, # 0.1 μm²/s diffusion
209210
t_max = 0.5, # 0.5 second total
210211
camera_framerate = 100.0 # 100 fps
211212
)
212-
smld = simulate(params; camera=camera_scmos, photons=200.0)
213+
smld, info = simulate(params; camera=camera_scmos, photons=200.0)
213214

214215
# Generate images with full sCMOS noise model
215216
# (quantum efficiency, Poisson, read noise, gain, offset)
216217
psf = GaussianPSF(0.13) # 130nm PSF
217-
images_scmos = gen_images(smld, psf, bg=10.0, camera_noise=true)
218+
images_scmos, _ = gen_images(smld, psf, bg=10.0, camera_noise=true)
218219

219220
# For comparison: same data with ideal camera (Poisson noise only)
220221
camera_ideal = IdealCamera(64, 64, 0.1)
221222
smld_ideal = BasicSMLD(smld.emitters, camera_ideal, smld.n_frames, smld.n_datasets)
222-
images_ideal = gen_images(smld_ideal, psf, bg=10.0, poisson_noise=true)
223+
images_ideal, _ = gen_images(smld_ideal, psf, bg=10.0, poisson_noise=true)
223224

224225
# Compare statistics
225226
println("sCMOS: mean=$(round(mean(images_scmos), digits=1)) ADU, std=$(round(std(images_scmos), digits=1))")

0 commit comments

Comments
 (0)