VENUS TPX1 Data Reduction Workflow
Beamline: VENUS (SNS) Detector: Timepix1 (TPX1) Beam Type: Pulsed with TOF Data Mode: Histogram (frame-mode, TOF bins fixed at acquisition) Input Format: TIFF stacks (efficiency-corrected) Applications: Bragg edge imaging, resonance imaging, hyperspectral nCT
Data Flow Overview
EXTERNAL (Auto-Reduction):
TPX1 Detector → FITS (raw histogram) → Efficiency correction → TIFF stacks
NeuNorm Pipeline:
Input: TIFF stacks (TOF, y, x) - efficiency-corrected, TOF bins fixed
Pipeline Flowchart
flowchart TD
subgraph Input["1. Data Loading (TIFF)"]
A1[TIFF Stack] --> A[Load Sample TOF,y,x]
A2[TIFF Stack] --> B[Load OB TOF,y,x]
A3[Metadata] --> C[Load per-image TOF values]
A4[DAQ] --> M[Load proton_charge, duration]
end
subgraph RunCombine["2. Run Combining"]
RC1{Multiple Runs?}
RC2[Sum then divide by run count; avg metadata]
RC3[Single Run]
end
subgraph ROI["3. ROI Clipping"]
D{ROI Specified?}
E[Apply Spatial ROI]
F[Full Frame]
end
subgraph PixelDetect["4. Dead Pixel Detection"]
PD[Sum OB over TOF → Identify Zeros]
PM[Dead Pixel Mask 2D]
end
subgraph Stats["5. Statistics Analysis"]
ST1[Count per TOF Bin]
ST2[SNR Analysis]
ST3[Rebinning Recommendation]
end
subgraph Rebin["6. Rebinning (Optional)"]
RB1{Rebin?}
RB2[Combine N Adjacent TOF Bins]
RB3[Spatial Binning NxN]
RB4[Keep Original]
end
subgraph BeamCorr["7. Beam Correction"]
BC1["f = p_charge_OB / p_charge_Sample"]
BC2["Shutter counts correlation check"]
end
subgraph Norm["8. Normalization"]
N["T(TOF) = Sample(TOF) / OB(TOF) × f"]
end
subgraph AirCorr["9. Air Region Correction (Optional)"]
AC1{Air ROI?}
AC2["T_final = T / mean(T_air)"]
AC3[Skip]
end
subgraph UQ["10. Experiment Error"]
UQ1[Poisson per TOF bin]
UQ2[p_charge σ]
UQ3[Error Propagation]
end
subgraph Output["11. Output"]
O1[Transmission 3D TOF,y,x]
O2[Uncertainty 3D]
O3[tof coordinate]
O4[Dead Pixel Mask]
O5[Metadata]
end
Input --> RC1
RC1 -->|Yes| RC2
RC1 -->|No| RC3
RC2 --> D
RC3 --> D
D -->|Yes| E
D -->|No| F
E --> PD
F --> PD
PD --> PM
PM --> ST1
ST1 --> ST2
ST2 --> ST3
ST3 --> RB1
RB1 -->|TOF| RB2
RB1 -->|Spatial| RB3
RB1 -->|No| RB4
RB2 --> BC1
RB3 --> BC1
RB4 --> BC1
BC1 --> BC2
BC2 --> N
N --> AC1
AC1 -->|Yes| AC2
AC1 -->|No| AC3
AC2 --> UQ1
AC3 --> UQ1
UQ1 --> UQ2
UQ2 --> UQ3
UQ3 --> O1
UQ3 --> O2
UQ3 --> O3
PM --> O4
O1 --> O5
O2 --> O5
O3 --> O5
O4 --> O5
style Input fill:#e1f5ff
style RunCombine fill:#f5e1ff
style ROI fill:#fff4e1
style PixelDetect fill:#ffe1e1
style Stats fill:#e1f5ff
style Rebin fill:#ffe1f5
style BeamCorr fill:#e1f5ff
style Norm fill:#e1ffe1
style AirCorr fill:#fff4e1
style UQ fill:#ffe1cc
style Output fill:#f5e1ff
1. Inputs
Input |
Format |
Required |
Description |
|---|---|---|---|
Sample data |
TIFF stack |
Yes |
3D histogram (TOF, y, x), efficiency-corrected |
Open Beam (OB) |
TIFF stack |
Yes |
3D reference (TOF, y, x), efficiency-corrected |
TOF bin edges |
Metadata/file |
Yes |
Time-of-flight bin boundaries (fixed at acquisition) |
ROI |
(x0, y0, x1, y1) |
No |
Spatial region of interest |
Metadata (from files or DAQ):
Acquisition time per frame
p_charge (proton charge - beam intensity proxy)
shutter_counts (number of neutron pulses captured per frame)
Source-to-detector distance (L)
Key Characteristics:
Frame mode: Detector operates in frame readout mode with histogram accumulation
No dark current correction: Counting detector (not integrating)
Efficiency pre-corrected: Input TIFFs already have detector efficiency correction applied (external auto-reduction)
TOF bins fixed at acquisition: Rebinning limited to combining adjacent bins
Shutter counts: A metadata field (pulses per frame); not currently loaded or used by the TPX1 pipeline
2. Processing Pipeline
┌─────────────────────────────────────────────────────────────────┐
│ STEP 1: Load Data (TIFF Stacks) │
│ ─────────────────────────────── │
│ • Load Sample TIFF stack → 3D array (TOF, y, x) │
│ (TIFF stack dim N_image renamed to tof; no rotation axis) │
│ • Load OB TIFF stack → 3D array (TOF, y, x) │
│ • Load per-image TOF values → 1D array (N_images,) │
│ • Load metadata: proton_charge (p_charge), duration │
│ • Validate dimensions match │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 2: Run Combining (Critical for VENUS) │
│ ────────────────────────────────────────── │
│ IF multiple runs provided (normalize_by_runs=True): │
│ • Sum histogram data across runs, then divide by run count │
│ (sample, OB separately) → per-run average │
│ • Average proton_charge across runs (sc.mean) │
│ • Average duration across runs (sc.mean) │
│ • Dead pixels detected post-combine, not per run │
│ (re-detected if spatial rebinning is applied) │
│ │
│ Note: All runs must have same TOF bin edges │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 3: ROI Clipping (Optional) │
│ ─────────────────────────────── │
│ IF ROI specified: │
│ • Crop spatial dimensions: arr[..., y0:y1, x0:x1] │
│ • TOF dimension unchanged │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 4: Dead Pixel Detection │
│ ──────────────────────────── │
│ • Sum OB across TOF dimension: OB_summed = sum(OB, axis=TOF) │
│ • Identify pixels with zero total counts │
│ • dead_mask = (OB_summed == 0) │
│ • Output: 2D boolean mask (y, x) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 5: Statistics Analysis & Rebinning Recommendation │
│ ────────────────────────────────────────────────────── │
│ Analyze count statistics per TOF bin: │
│ │
│ FOR each TOF bin t: │
│ • N_counts[t] = sum(OB[t, :, :]) (excluding dead pixels) │
│ • SNR[t] = √(N_counts[t]) │
│ │
│ Generate recommendation: │
│ • Identify bins with inadequate statistics (SNR < threshold) │
│ • Recommend rebinning factor N (combine N adjacent bins) │
│ • Or recommend spatial binning (NxN pixels) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 6: Rebinning (Optional) │
│ ──────────────────────────── │
│ IF rebinning requested: │
│ │
│ Option A: TOF rebinning (combine N adjacent bins) │
│ • Sum counts from N adjacent TOF bins │
│ • Update TOF bin edges: edges[::N] │
│ • Reduces TOF dimension by factor N │
│ │
│ Option B: Spatial rebinning (NxN pixel binning) │
│ • Sum counts from NxN pixel blocks │
│ • Reduces (y, x) dimensions by factor N │
│ • Preserves TOF resolution │
│ │
│ Note: Both options can be combined │
│ Note: Rebinning sums counts, preserving Poisson statistics │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 7: Beam Correction │
│ ─────────────────────── │
│ PRIMARY correction for VENUS pulsed source │
│ │
│ p_charge correction: │
│ f_beam = p_charge_OB / p_charge_sample │
│ │
│ Note: shutter_counts is not loaded/used by the TPX1 pipeline. │
│ │
│ Note: Correction factor applies uniformly across all TOF bins │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 8: Normalization │
│ ───────────────────── │
│ FOR each TOF bin t: │
│ │
│ T[t, y, x] = (Sample[t, y, x] / OB[t, y, x]) × f_beam │
│ │
│ │
│ Handle division: │
│ • Dead pixels carried as a scipp mask, not NaN-filled │
│ • Where OB[t, y, x] == 0: division yields inf/nan (mask │
│ before calling; not explicitly replaced with NaN) │
│ │
│ Formula: │
│ T(TOF) = [I_sample(TOF) / I_OB(TOF)] × f_beam │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 9: Air Region Correction (Optional) │
│ ───────────────────────────────────────── │
│ Post-normalization refinement if p_charge wasn't sufficient │
│ │
│ IF Air ROI specified: │
│ FOR each TOF bin t: │
│ 1. Calculate mean transmission in air region: │
│ <T_air(t)> = mean(T[air_ROI, t]) │
│ │
│ 2. Scale to ensure air = 1.0: │
│ T_final(t) = T(t) / <T_air(t)> │
│ │
│ Note: Always averages over (x, y); other dims (e.g. TOF) are │
│ preserved, so scaling is per-TOF-bin (no mode option) │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 10: Experiment Error Propagation │
│ ───────────────────────────────────── │
│ Sources of uncertainty: │
│ • Poisson: σ_N = √(N) for counts per TOF bin │
│ • p_charge: σ_p (Gaussian, from DAQ measurement) │
│ • Air region: σ_air (if air correction applied) │
│ │
│ Error propagation per TOF bin: │
│ │
│ σ_T(TOF) = T(TOF) × √[ 1/N_sample(TOF) + 1/N_OB(TOF) + │
│ (σ_p_sample/p_sample)² + │
│ (σ_p_OB/p_OB)² ] │
│ │
│ Note: For rebinned data, counts are summed so σ = √(sum) │
│ If air correction: add (σ_air/<T_air>)² term │
└─────────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────────┐
│ STEP 11: Output │
│ ──────────── │
│ • Transmission: 3D array (TOF, y, x) │
│ • Experiment Error: 3D array (same shape) │
│ • tof coordinate: one TOF value per image (written by name) │
│ • Dead Pixel Mask: 2D boolean array (y, x) │
│ • Metadata: processing parameters, provenance │
└─────────────────────────────────────────────────────────────────┘
3. Output Specification
Output |
Dimensions |
dtype |
Description |
|---|---|---|---|
Transmission |
(TOF, y, x) |
float32 |
TOF-resolved transmission |
Experiment Error |
(TOF, y, x) |
float32 |
Propagated uncertainty (1σ) |
tof coordinate |
(N_images,) |
float64 |
One TOF value per image (written by coord name) |
Dead Pixel Mask |
(y, x) |
bool |
True = dead pixel |
Metadata |
dict |
- |
Processing provenance |
Metadata contents:
Input file paths (sample/OB HDF5 and TIFF paths)
Processing timestamp
Software version
ROI applied (if any)
4. Coordinate Conversions
TOF can be converted to energy or wavelength using the flight path length and the
detector time offset (detector_time_offset from metadata, issue #141):
TOF → Wavelength:
λ = (h × (TOF + offset)) / (m_n × L)
TOF → Energy:
E = (1/2) × m_n × (L / (TOF + offset))²
where:
h = Planck's constant
m_n = neutron mass
L = source-to-detector distance
offset = detector time offset
5. Decision Points
Step |
Decision |
Options |
|---|---|---|
2 |
Multiple runs? |
Combine or single run |
3 |
ROI needed? |
Apply crop or full frame |
5 |
Statistics adequate? |
Yes → proceed / No → recommend rebinning |
6 |
Rebinning type |
TOF (combine N bins) / Spatial (NxN) / None |
9 |
Air region correction? |
Apply if p_charge insufficient / Skip |
6. Rebinning Constraints
TPX1 histogram data has fixed TOF bins determined at acquisition. Rebinning options:
TOF Rebinning (rebin_tof, unit selects the mode):
bins(default): combine N adjacent bins; new edges =original_edges[::N], with the final original edge appended if[::N]did not already include itmanual/time/wavelength: request edges by explicit list, time width, or wavelength width — requested edges are snapped to the nearest original edge (bins are never split), so the result still only combines adjacent original bins
Spatial Rebinning:
Combine NxN pixel blocks
Reduces spatial resolution
Preserves TOF resolution
Cannot do:
Split an existing bin or place an edge inside one (sub-original-bin resolution) — requested edges that fall mid-bin are snapped to the nearest original edge
Heterogeneous (variable-width) edges are allowed via
manual/time/wavelength, but only on the original boundaries; finer-than-acquisition binning requires event-mode data (TPX3)
7. Development Components
Required Modules
Component |
Purpose |
Priority |
|---|---|---|
|
Load TIFF histogram stacks |
P0 |
|
Extract p_charge, shutter_counts, TOF edges |
P0 |
|
Aggregate multiple runs |
P0 |
|
Apply ROI to arrays |
P1 |
|
Identify dead pixels |
P0 |
|
Analyze bin occupancy, compute SNR |
P0 |
|
Combine adjacent TOF bins |
P0 |
|
Combine NxN pixel blocks |
P1 |
|
Apply p_charge correction |
P0 |
|
Compute transmission |
P0 |
|
Optional post-normalization correction |
P1 |
|
Error propagation |
P0 |
|
TOF ↔ λ ↔ E |
P1 |
|
Write results (HDF5 primary; TIFF optional) |
P0 |
Data Models
InputData:
- sample: NDArray[float32] # (N, TOF, y, x) or (TOF, y, x)
- open_beam: NDArray[float32] # (TOF, y, x)
- tof_edges: NDArray[float64] # (N_bins + 1,)
- p_charge_sample: float32
- p_charge_OB: float32
- flight_path_length: float32 # meters
- roi: Optional[Tuple[int, int, int, int]]
- metadata: Dict
RebinConfig:
- tof_factor: Optional[int] # combine N adjacent TOF bins
- spatial_factor: Optional[int] # combine NxN pixels
ProcessedData:
- transmission: NDArray[float32] # (TOF, y, x)
- uncertainty: NDArray[float32] # (TOF, y, x)
- tof_edges: NDArray[float64] # (N_bins + 1,) - updated if rebinned
- dead_pixel_mask: NDArray[bool] # (y, x)
- metadata: Dict
8. Validation Criteria
[ ] Transmission values in expected range per TOF bin
[ ] inf/nan only at zero-denominator (OB) pixels; masks are preserved, not value-filled
[ ] Uncertainty > 0 for all valid pixels and TOF bins
[ ] Dead pixel mask correctly identifies zero-count pixels
[ ] TOF bin edges monotonically increasing
[ ] Rebinning preserves total counts
[ ] Beam correction factor close to 1.0