Repository navigation
[ESSSANS] Q resolution per I(Q) bin - #766
Draft
SimonHeybrock wants to merge 7 commits into
Draft
SimonHeybrock wants to merge 7 commits into
SimonHeybrock wants to merge 7 commits into
Conversation
Design is in #763. This records decisions, suggested order of work and pointers into the code for whoever implements it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Records that bank merging and the resolution work are independent, the rules that keep them compatible, the suggested ResolutionMoments part of IofQPart, and the definition of the mixture classes. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Implements steps 2 and 3 of #763. Each (pixel, wavelength) element gets a Mildner-Carpenter variance sigma_i**2. The resolution of a Q bin is the second moment of the mixture of element Gaussians weighted by the denominator N_i. It is computed from the sums N*Q and N*(sigma**2 + Q**2), which are two new IofQPart members and therefore go through the same binning, wavelength-band reduction and run merging as the numerator and denominator. The result is QResolution[RunType]. The wavelength spread from the source comes from the variance stored in the wavelength lookup table, via the new ess.reduce.unwrap.wavelength_spread. Workflows without a lookup table (ISIS, LoKI at Larmor) can set SourceWavelengthSpread directly. Aperture radii, collimation length and pixel size are user parameters until the instrument scientists tell us where these values should come from. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The design now lives in the ess.sans.resolution module docstring and in #763. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Events are histogrammed in Q using their own wavelength, so the width of the wavelength bins does not widen the resolution of a Q bin beyond the spread of Q within the bin, which var(Q_i) already includes. The Δλ²/12 term counted it twice and made σ depend on the wavelength binning (up to 60% at high Q on the Larmor data). Replace the scalar DetectorPixelSize by PixelScatteringAngleVariance, computed per pixel from the cylindrical pixel shape and its orientation relative to the direction of increasing 2θ. This handles tilted LoKI side banks. Apply the matching cos(2θ) to the part of the sample aperture term seen from the pixel. Document that analytical lookup tables store half the wavelength range, not a standard deviation. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Events at masked lookup-table entries get no wavelength and never reach the numerator, so their pixels and wavelengths must not contribute to the resolution function. Mark them by a NaN SourceWavelengthSpread (relative spread above LookupTableRelativeErrorThreshold) and give them zero weight. This needs a separate sum of weights, so the resolution now uses its own zeroth moment instead of the denominator. The formula omits the wavelength bin width, which is only correct if the counts are binned in Q by their own wavelength. Raise for data histogrammed in wavelength. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #763 (steps 2 and 3 of the proposed implementation). Refines #360.
Adds
QResolution[RunType]: one σ per I(Q) bin, the NXcanSASQdev. The resolution is built from sums, so it stays correct when runs are merged, and later when banks and live-data chunks are merged.For science review
What is computed
Each element
i(one pixel and one wavelength bin) gets a Gaussian resolution (Mildner–Carpenter, keepingcos θandcos 2θ):σ_pixelis computed per pixel frompixel_shapeand the detector transformation. The extent that matters is the one alonge, which depends on the straw orientation relative toein every bank: on the rear detector, pixels left and right of the beam have their straw alonge(length ℓ ≈ 2 mm counts), pixels above and below have it perpendicular (diameter 7.5 mm counts). A scalar pixel size cannot describe this. For a flat detector perpendicular to the beam with pixel size ΔR alongeit reduces toΔR cos2θ / (√12 L2). Thecos2θin the sample-aperture term is the same foreshortening, applied to the sample aperture as seen from the pixel.The wavelength bin width does not enter. Events are histogrammed in Q with their own wavelength, so the spread of Q in a bin is limited by the Q bin width, which
var(Q_i)below already contains. AΔλ²/12term counts it twice: on the Larmor data it made σ/Q at Q = 0.27 Å⁻¹ 4.9% with 50 wavelength bins and 3.2% with 400. Without it, σ is the same for both to four digits.The resolution function of a Q bin is the mixture of the element Gaussians, weighted by the normalization term
N_i(the existing denominator). This follows fromI_bin = Σ C_i / Σ N_i. We report its standard deviation about its mean:Elements whose events get no wavelength, because the lookup table is masked there (relative spread above
LookupTableRelativeErrorThreshold), are part of the denominator but not of the counts, so they are excluded from the sums. The mixture is exact if counts are binned like the denominator. Events are binned by their own wavelength; an event-level Monte-Carlo on the Larmor data agrees with σ_bin within 1% (σ_λ/λ = 2%) and within 2–3% (σ_λ = 0.12 Å, where linearizing in 1/λ starts to matter at high Q).QResolutionholds σ_bin as data andQ_meanas a coordinate. See #763 for the derivation and for the comparison with per-event weighting and Mantid Q1D.Please check
σ_sourcecomes from the variance stored in the essreduce wavelength lookup table, at the pixel'sLtotal. Tables built by simulation (tof, McStas ESS source with correlated emission time and wavelength, propagated through the choppers) store the right quantity. Tables built inanalyticalmode, which is what esslivedata uses, store half the wavelength range for a flat 0–5 ms source window. Without pulse-shaping choppers this is 2.8 times the true σ (0.345 Å instead of 0.12 Å at 28.7 m). The same flat window also puts the mean emission time at 2.5 ms instead of 1.65–1.85 ms, so analytical tables assign wavelengths that are 0.085–0.12 Å too short at 28.7 m (5.4% at 2 Å). That affects I(Q) itself and is not addressed here. See [essreduce] Analytical wavelength LUT: flat source window biases mean wavelength and spread #770 for a proposed fix (weighting the analytical table by the pulse profile), which would also give the right σ_source.SourceApertureRadius,SampleApertureRadius,CollimationLength). Should they come from NeXus? Are the LoKI apertures circular?PixelScatteringAngleVariance[RunType]can be set directly to override the value from the pixel shape.Q_mean. If I(Q) is reported at the bin centre, the second moment about the centre isσ_bin² + (Q_mean − Q_centre)². On the Larmor test data,Q_meanis within 5% of a bin width of the centre.CorrectForGravityis set, the gravity-corrected2θis used inσ_i. Gravity is not included as a separate resolution term.Not in this PR
Qdevto NXcanSAS:save_background_subtracted_iofqis unchanged.For developer review
IofQPartmembers.ResolutionZerothMoment(N),ResolutionFirstMoment(N·Q) andResolutionSecondMoment(N·(σ² + Q²)) go through the existing genericbin_in_q,reduce_qand run merging, exactly like numerator and denominator. The [ESSSANS] Q resolution per Q bin, compatible with merging LoKI banks #763 plan used a single part with a size-2momentdim. That does not work, because the sums have different units. The zeroth moment is needed instead of the denominator because excluded elements are in the denominator.DetectorQVariance[RunType]on theQDetector[RunType, Denominator]grid. All moments are derived from it. Excluded elements have NaN σ_i² and get weight 0.Nare used. Denominator variances are dropped.PixelScatteringAngleVariance[RunType]is computed fromDetectorPixelShape[RunType]and the detector transformation, on the pixel dims only, and broadcast over wavelength.solid_angleand this provider sharenormalization.pixel_cylinder. Detectors without a pixel shape can set the variance directly.mask_and_scale_resolution_momentis the counterpart ofmask_and_scale_wavelength_q. It multiplies by the values of the monitor term, after binning in Q._set_runsnow also mergesNormalizedQof the three moments.q_resolutioncombines the sums only after all merging. Never compute σ per run and average it.QResolution[RunType]is a separate output rather than variances on theQcoord of I(Q). I(Q) has bin-edgeQ, and requiring the resolution inIntensityQor in the save function would break the ISIS workflows, which have no lookup table.SourceWavelengthSpread[RunType]is where σ_source enters. By default it comes fromLookupTable[RunType, NXdetector]and is NaN where the relative spread exceeds the table's error threshold, which marks the elements to exclude. Workflows without a lookup table (ISIS, LoKI at Larmor) can set it directly as a scalar or as anything broadcastable to (pixel, wavelength).detector_q_varianceraises if the numerator is not binned (event) data, since histogrammed data would need the wavelength-bin term.ess.reduce.unwrap.wavelength_spread(table, ltotal, wavelength):Ltotaloutside the table's distance range raises an error.Test plan
σ_i²against an independent numpy implementation of the formula (mixed units, two pixels).SourceWavelengthSpreadis NaN exactly where the relative spread exceeds the error threshold.2θvariance against a numerical average over the cylinder volume, for four pixel orientations and a rotated detector transformation (agreement 1e-4).QResolutionequals a brute-force N-weighted second moment over the dense (pixel, wavelength) arrays, including pixel masks (rtol 1e-9).with_sample_runsequals σ from the summed per-run sums.QResolutionhas the same dims andQcoord as I(Q), with wavelength bands and withDimsToKeep.SourceWavelengthSpreadfrom the test lookup table is finite and positive, andQResolutioncan be computed.wavelength_spread: exact for spreads linear in distance and wavelength, including unsorted (wrapped) table rows, unit conversion, NaN entries, and out-of-range errors.🤖 Generated with Claude Code