Summary
Add a registered XSpect step that produces per-shot 1D spectra directly from the droplet2photon sparse photon list, without reconstructing the full detector image. Rotation, ROI selection, dispersion/energy projection, and detector-gap correction are all done as geometry operations on the photon coordinates ("list-mode" / "event-mode" processing).
The full-image droplet_reconstruction step (XSpect analysis/droplet.py) is currently the throughput bottleneck. But dense reconstruction is only needed as an intermediate because rotation and gap-patching were written as image operations. Both are point/geometry operations that act more naturally — and more accurately — on the sparse list. Since the per-shot end product is a 1D spectrum (sum rows in an ROI, histogram along dispersion), the ~540k-pixel array is never actually required.
Motivation
- Speed: per-shot cost drops from "allocate + scatter + spline-rotate a 540k-pixel image" to "transform + histogram the handful of photons in that shot" (~100–1000× fewer ops/shot; no image allocation, no interpolation).
- Correctness / artifact removal: the polynomial gap-patch injects dead columns as linear combinations of their neighbors, which spook cannot assign independent incident-energy weight to — this produces the vertical RIXS-map stripes documented in lg345/polarization_dependent_stochastic#28. Geometric exposure normalization injects no linearly-dependent data and removes that artifact.
Method
For a shot's photons (r_i, c_i) with weights w_i (droplet photon count):
1. Rotation = exact linear transform on coordinates (O(N_photons), no interpolation):
d_i = (c_i - c0)*cosθ - (r_i - r0)*sinθ # dispersion axis
s_i = (c_i - c0)*sinθ + (r_i - r0)*cosθ # cross-dispersion axis
(θ sign chosen to match the existing scipy.ndimage.rotate convention.)
2. ROI + projection = filter then histogram:
m = (s_lo <= s_i <= s_hi)
spec_raw = histogram(d_i[m], bins=dispersion_bins, weights=w_i[m])
3. Gaps = static coverage/exposure normalization (computed ONCE):
rr, cc = nonzero(live_pixel_map within ROI) # dead columns excluded
d_live = (cc-c0)*cosθ - (rr-r0)*sinθ
s_live = (cc-c0)*sinθ + (rr-r0)*cosθ
coverage = histogram(d_live[s_lo<=s_live<=s_hi], bins=dispersion_bins)
# per shot:
spec = spec_raw / coverage * coverage.mean()
Because rotation maps a dead column to a diagonal set of output bins, each output bin loses only ~1/N_rows of coverage per crossing — coverage never hits zero away from θ=0, so the correction is smooth and well-conditioned. Guard the division with a coverage floor (mask bins below ~20% of median coverage).
Optional: fold the pixel→eV map into the histogram bin edges to merge reconstruction + rotation + gap correction + energy calibration into a single pass over the photon list.
What still needs a dense image (one-time only, not the hot loop)
- Rotation-angle finding (
mfx101609126_rotation_diagnostic) and gap-column / live-pixel identification (mfx101609126_pixel_patch_diagnostic) — run on the sum image over all shots, built once.
Proposed API
New registered step, e.g. sparse_project_spectrum:
- inputs:
droplet_droplet2phot_sparse_* arrays, rotation angle, pivot (r0,c0), ROI cross-dispersion band [s_lo,s_hi], dispersion bins (or energy bins + calibration), live-pixel map (or explicit dead-column list)
- output: per-shot 1D spectrum
(N_shots, N_bins), already gap-corrected — a drop-in replacement for droplet_reconstruction → patch_pixels → rotate_detector → reduce_detector_spatial.
Acceptance / validation
Caveats
- Carry the droplet photon count as
w_i (a droplet may be >1 photon).
- This is a new, arguably more correct method, not a bit-match to the current path — revalidate rather than expecting
corr=1.0.
- Coverage floor needed only near θ=0 or with a very narrow ROI.
Cross-references
Summary
Add a registered XSpect step that produces per-shot 1D spectra directly from the droplet2photon sparse photon list, without reconstructing the full detector image. Rotation, ROI selection, dispersion/energy projection, and detector-gap correction are all done as geometry operations on the photon coordinates ("list-mode" / "event-mode" processing).
The full-image
droplet_reconstructionstep (XSpectanalysis/droplet.py) is currently the throughput bottleneck. But dense reconstruction is only needed as an intermediate because rotation and gap-patching were written as image operations. Both are point/geometry operations that act more naturally — and more accurately — on the sparse list. Since the per-shot end product is a 1D spectrum (sum rows in an ROI, histogram along dispersion), the ~540k-pixel array is never actually required.Motivation
Method
For a shot's photons
(r_i, c_i)with weightsw_i(droplet photon count):1. Rotation = exact linear transform on coordinates (O(N_photons), no interpolation):
(θ sign chosen to match the existing scipy.ndimage.rotate convention.)
2. ROI + projection = filter then histogram:
3. Gaps = static coverage/exposure normalization (computed ONCE):
Because rotation maps a dead column to a diagonal set of output bins, each output bin loses only ~1/N_rows of coverage per crossing — coverage never hits zero away from θ=0, so the correction is smooth and well-conditioned. Guard the division with a coverage floor (mask bins below ~20% of median coverage).
Optional: fold the pixel→eV map into the histogram bin edges to merge reconstruction + rotation + gap correction + energy calibration into a single pass over the photon list.
What still needs a dense image (one-time only, not the hot loop)
mfx101609126_rotation_diagnostic) and gap-column / live-pixel identification (mfx101609126_pixel_patch_diagnostic) — run on the sum image over all shots, built once.Proposed API
New registered step, e.g.
sparse_project_spectrum:droplet_droplet2phot_sparse_*arrays, rotationangle, pivot(r0,c0), ROI cross-dispersion band[s_lo,s_hi], dispersion bins (or energy bins + calibration), live-pixel map (or explicit dead-column list)(N_shots, N_bins), already gap-corrected — a drop-in replacement fordroplet_reconstruction → patch_pixels → rotate_detector → reduce_detector_spatial.Acceptance / validation
droplet_reconstructionpipeline on mfx101609126 run 79 (Fe foil); report speedup and peak memory.Caveats
w_i(a droplet may be >1 photon).corr=1.0.Cross-references