MARS TPX3 Data Reduction Workflow

Beamline: MARS (HFIR) Detector: Timepix3 (TPX3) Beam Type: Continuous (no TOF) Applications: nR (radiography), nCT (computed tomography), nGI (grating interferometry)


Pipeline Flowchart

flowchart TD
    subgraph Input["1. Event Loading"]
        A1[HDF5 Events] --> A[Load Sample Events]
        A2[HDF5 Events] --> B[Load OB Events]
    end

    subgraph EventConv["2. Event Conversion"]
        EC[Bin Events by x,y]
        EH[2D Histogram]
    end

    subgraph RunCombine["3. Run Combining"]
        RC1{Multiple Runs?}
        RC2[Average Histograms]
        RC3[Single Run]
    end

    subgraph ROI["4. ROI Clipping"]
        D{ROI Specified?}
        E[Apply ROI]
        F[Full Frame]
    end

    subgraph PixelDetect["5-6. Pixel Detection"]
        PD1[Dead Pixel Detection]
        PD2[Hot Pixel Detection]
        PM1[Dead Mask]
        PM2[Hot Mask]
        PM3[Combined Bad Pixel Mask]
    end

    subgraph Gamma["7. Gamma Filtering"]
        GF[Detect Gamma Spikes]
        GR[Replace with Median]
    end

    subgraph Norm["8. Normalization"]
        N["T = Sample / OB"]
    end

    subgraph UQ["9. Experiment Error"]
        UQ1[Poisson Statistics]
        UQ2[Error Propagation]
    end

    subgraph Output["10. Output"]
        O1[Transmission 3D]
        O2[Uncertainty 3D]
        O3[Dead Pixel Mask]
        O4[Hot Pixel Mask]
        O5[Metadata]
    end

    Input --> EC
    EC --> EH
    EH --> RC1
    RC1 -->|Yes| RC2
    RC1 -->|No| RC3
    RC2 --> D
    RC3 --> D
    D -->|Yes| E
    D -->|No| F
    E --> PD1
    F --> PD1
    PD1 --> PM1
    PD1 --> PD2
    PD2 --> PM2
    PM1 --> PM3
    PM2 --> PM3
    PM3 --> Gamma
    GF --> GR
    Gamma --> N
    N --> UQ1
    UQ1 --> UQ2
    UQ2 --> O1
    UQ2 --> O2
    PM1 --> O3
    PM2 --> O4
    O1 --> O5
    O2 --> O5
    O3 --> O5
    O4 --> O5

    style Input fill:#e1f5ff
    style EventConv fill:#ffe1f5
    style RunCombine fill:#f5e1ff
    style ROI fill:#fff4e1
    style PixelDetect fill:#ffe1e1
    style Gamma fill:#ffe1cc
    style Norm fill:#e1ffe1
    style UQ fill:#ffe1cc
    style Output fill:#f5e1ff

1. Inputs

Input

Format

Required

Description

Sample events

Event files (HDF5)

Yes

Neutron events (x, y, ToT)

Open Beam events

Event files (HDF5)

Yes

Reference events without sample

ROI

(x0, y0, x1, y1)

No

Region of interest to crop

Metadata (from files):

  • Acquisition time

  • Total event count

Key Differences from CCD/CMOS:

  • No dark current correction (counting detector - no electronic baseline)

  • Event data → histogram conversion required

  • Hot pixel detection required (radiation damage causes false counts)


2. Processing Pipeline

┌─────────────────────────────────────────────────────────────────┐
│  STEP 1: Load Event Data                                        │
│  ───────────────────────                                        │
│  • Load Sample event files → event list (x, y, ToT)             │
│  • Load OB event files → event list (x, y, ToT)                 │
│  • Extract acquisition metadata                                 │
│                                                                 │
│  Note: TPX3 events include ToT (Time over Threshold) which      │
│  correlates with deposited energy. At MARS, TOF not used.       │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 2: Event-to-Histogram Conversion                          │
│  ─────────────────────────────────────                          │
│  Convert event lists to 2D histograms (no TOF at MARS):         │
│                                                                 │
│  FOR each acquisition:                                          │
│    • Bin events by (x, y) position                              │
│    • Sample_hist[i] = histogram(events_sample, bins=(x, y))     │
│    • Result: 2D count image per acquisition                     │
│                                                                 │
│  OB_hist = histogram(events_OB, bins=(x, y))                    │
│                                                                 │
│  Output: Sample and OB each stacked 3D (N_image, x, y);         │
│  OB reduced to (x, y) later by reference preparation            │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 3: Run Combining (Optional)                               │
│  ────────────────────────────────                               │
│  IF multiple runs provided:                                     │
│    • Average histograms across runs (sum ÷ run count)           │
│    • No metadata aggregation (metadata_keys_to_sum=[])          │
│    • Bad pixels detected once on the combined stack             │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 4: ROI Clipping (Optional)                                │
│  ───────────────────────────────                                │
│  IF ROI specified:                                              │
│    • Crop all histogram arrays to ROI                           │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 5: Dead Pixel Detection                                   │
│  ────────────────────────────                                   │
│  • Identify pixels with zero summed counts in the SAMPLE        │
│  • dead_mask = (Sample_summed == 0)  (sum over N_image)         │
│  • Output: 2D boolean mask                                      │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 6: Hot Pixel Detection                                    │
│  ───────────────────────────                                    │
│  TPX3-specific: radiation damage causes false counts            │
│                                                                 │
│  Detection methods:                                             │
│    a) Statistical: pixels with anomalously high count rate      │
│       on the SAMPLE, via a MAD threshold (default sigma=5.0):    │
│       hot_mask = (Sample_summed > median + sigma×MAD×1.4826)     │
│    b) Temporal: inconsistent counts across acquisitions         │
│    c) ToT-based: events with abnormal ToT values                │
│                                                                 │
│  Output: 2D boolean hot_pixel_mask                              │
│                                                                 │
│  Combined mask: bad_pixels = dead_mask | hot_mask               │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 7: Gamma Filtering                                        │
│  ───────────────────────                                        │
│  CRITICAL for MARS (SANS beamline contamination)                │
│                                                                 │
│  FOR each histogram image:                                      │
│    • Detect gamma spikes (outliers > threshold)                 │
│    • Replace with local median (3x3 neighborhood)               │
│                                                                 │
│  Note: Gamma events may have distinct ToT signature -           │
│  could filter at event level before histogramming               │
│                                                                 │
│  Methods:                                                       │
│    a) Histogram-based: same as CCD/CMOS                         │
│    b) Event-level: filter by ToT before histogramming           │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 8: Normalization                                          │
│  ─────────────────────                                          │
│  FOR each image i:                                              │
│                                                                 │
│    T[i] = Sample_hist[i] / OB_hist                              │
│                                                                 │
│  Handle division:                                               │
│    • Bad pixels carried as scipp masks (not NaN-filled)         │
│    • Where OB_hist == 0: T = inf/nan (division artifact)        │
│                                                                 │
│  Formula (no dark current subtraction):                         │
│    T = I_sample / I_OB                                          │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 9: Experiment Error Propagation                           │
│  ────────────────────────────────────                           │
│  Poisson statistics for counting detector:                      │
│    σ_sample = √(N_sample)                                       │
│    σ_OB = √(N_OB)                                               │
│                                                                 │
│  Error propagation for division:                                │
│                                                                 │
│    σ_T = T × √[ 1/N_sample + 1/N_OB ]                           │
│                                                                 │
│  Simplified (no dark current term):                             │
│    σ_T/T = √[ (σ_S/S)² + (σ_OB/OB)² ]                           │
│          = √[ 1/N_S + 1/N_OB ]                                  │
└─────────────────────────────────────────────────────────────────┘
                              ↓
┌─────────────────────────────────────────────────────────────────┐
│  STEP 10: Output                                                │
│  ────────────                                                   │
│  • Transmission: 3D array (N_image, x, y) for HDF5;             │
│    TIFF renames N_image → z                                     │
│  • Experiment Error: 3D array (same shape as Transmission)      │
│  • Dead Pixel Mask: 2D boolean array (x, y)                     │
│  • Hot Pixel Mask: 2D boolean array (x, y)                      │
│  • Metadata: processing parameters, provenance                  │
└─────────────────────────────────────────────────────────────────┘

3. Output Specification

Output

Dimensions

dtype

Description

Transmission

(N_image, x, y)

float32

Normalized transmission values

Experiment Error

(N_image, x, y)

float32

Propagated uncertainty (1σ)

Dead Pixel Mask

(x, y)

bool

True = dead pixel

Hot Pixel Mask

(x, y)

bool

True = hot pixel (TPX3-specific)

Metadata

dict

-

Processing provenance

Metadata contents:

  • Input file paths

  • Processing timestamp

  • Event-to-histogram binning parameters

  • Gamma filter parameters used

  • Hot pixel detection parameters

  • ROI applied (if any)

  • Number of runs combined (if any)

  • Software version


4. Decision Points

Step

Decision

Options

2

Event binning resolution

Native detector / Custom

3

Multiple runs?

Combine or single run

4

ROI needed?

Apply crop or full frame

6

Hot pixel method

Statistical / Temporal / ToT-based

7

Gamma filter level

Event-level / Histogram-level / Both


5. Development Components

Required Modules

Component

Purpose

Priority

loaders.event_loader

Load TPX3 event files

P0

tof.event_converter

Convert events to histogram

P0

processing.run_combiner

Aggregate multiple runs

P1

processing.roi_clipper

Apply ROI to arrays

P1

tof.pixel_detector

Identify dead pixels

P0

tof.pixel_detector

Identify hot pixels (TPX3)

P0

filters.gamma_filter

Remove gamma contamination

P0

processing.normalizer

Compute transmission

P0

processing.uncertainty_calculator

Error propagation

P0

exporters.hdf5_writer / exporters.tiff_writer

Write results (HDF5 primary; TIFF optional)

P0

Data Models

EventData:
  - x: NDArray[uint16]       # pixel x coordinate
  - y: NDArray[uint16]       # pixel y coordinate
  - tot: NDArray[uint16]     # Time over Threshold
  - metadata: Dict           # acquisition info

InputData:
  - sample_events: List[EventData]  # per acquisition
  - ob_events: EventData
  - roi: Optional[Tuple[int, int, int, int]]
  - metadata: Dict

ProcessedData:
  - transmission: NDArray[float32]  # (N, y, x)
  - uncertainty: NDArray[float32]   # (N, y, x)
  - dead_pixel_mask: NDArray[bool]  # (y, x)
  - hot_pixel_mask: NDArray[bool]   # (y, x)
  - metadata: Dict

6. Key Differences from MARS CCD/CMOS

Aspect

CCD/CMOS

TPX3

Input format

TIFF/FITS stacks

Event files

Dark current

Required

Not needed

Hot pixels

Not applicable

Required detection

Gamma filter

Histogram only

Event or histogram level

Error formula

Includes dark term

Simpler (no dark)

Data conversion

None

Event → histogram


7. Validation Criteria

  • [ ] Event-to-histogram conversion preserves total counts

  • [ ] Transmission values in expected range

  • [ ] inf/nan only at zero-denominator (OB) pixels; masks are preserved, not value-filled

  • [ ] Uncertainty > 0 for all valid pixels

  • [ ] Hot pixel mask identifies anomalous count pixels

  • [ ] Dead pixel mask identifies zero-count pixels

  • [ ] Gamma filtering removes spikes without affecting valid data