pub struct TabulatedResolution { /* private fields */ }Expand description
A tabulated resolution function from Monte Carlo instrument simulation.
Contains reference kernels R(Δt; E_ref) at discrete energies, stored in TOF-offset space (μs). Kernels are interpolated between reference energies and converted from TOF to energy space when applied.
§Offset orientation
Positive Δt = delayed emission (the moderator storage tail); the
kernel mode sits at Δt = 0. At apply time the broadener gathers
theory at t − Δt (convolution — see Self::broaden), so the
positive-Δt tail reads theory from earlier TOF = higher energy and
broadened dips acquire their tail toward lower apparent energy.
§File Format (VENUS/FTS)
FTS BL10 case i00dd folded triang FWHM 350 ns PSR ← header
----- ← separator
5.00000e-004 0.00000e+000 ← energy block start
-53.458917835671329 2.051764258257523e-04 ← (tof_offset_μs, weight)
...
← blank line separates blocks
1.00000e-003 0.00000e+000 ← next energy block
...Implementations§
Source§impl TabulatedResolution
impl TabulatedResolution
Sourcepub fn ref_energies(&self) -> &[f64]
pub fn ref_energies(&self) -> &[f64]
Reference energies (eV), sorted ascending.
Sourcepub fn kernels(&self) -> &[(Vec<f64>, Vec<f64>)]
pub fn kernels(&self) -> &[(Vec<f64>, Vec<f64>)]
For each reference energy: (tof_offsets_μs, weights) pairs. Weights are peak-normalized (max=1.0).
Sourcepub fn flight_path_m(&self) -> f64
pub fn flight_path_m(&self) -> f64
Flight path length in meters (needed for TOF↔energy conversion).
Sourcepub fn width_corrected(
&self,
s0: f64,
p: f64,
e_ref: f64,
) -> Result<TabulatedResolution, ResolutionError>
pub fn width_corrected( &self, s0: f64, p: f64, e_ref: f64, ) -> Result<TabulatedResolution, ResolutionError>
Width-corrected copy of this tabulated kernel.
Shape-preserving instrument-resolution calibration knob: each
reference-energy block’s TOF offsets are scaled by
s(E) = s0 · (E / e_ref)^p about the block’s intensity centroid, so
the kernel widens/narrows without moving its centroid — width and position
stay orthogonal (t0/L handle absolute position). Weights are unchanged;
the apply-time trapezoidal renormalization preserves unit area.
Exactness note: the orthogonality is exact at reference
energies. Between references, interpolated_kernel’s
width-normalized blend re-scales each block about the mode
(offset 0), so the applied centroid picks up a second-order
dependence on the width exponent p (measured ~1 % of σ for
|p| ≤ 0.1 on widely spaced references) — absorbed by the
jointly fitted t0 in calibration.
The pivot is the trapezoidal-weighted centroid Σ o·w·dt / Σ w·dt,
using the same dt quadrature weights as the broadening integral (see
Self::broaden). Because dt is itself affine in the offsets, the width
scale multiplies every dt by s, so the integrated centroid is preserved
exactly on any offset grid (uniform or not) — not just on uniform grids
where the trapezoidal and plain centroids happen to coincide.
s0 = 1, p = 0 returns a width-identical copy. This is the fittable model
behind the udr_corr resolution-calibration family: it trusts the
Monte-Carlo shape and calibrates only its width / energy-dependence.
§Errors
Returns ResolutionError::InvalidWidthCorrection unless s0 is finite
and > 0, e_ref is finite and > 0, and p is finite. A non-positive
s0 would reverse/collapse the (ascending) offset ordering the broadening
loop assumes, so it is rejected up front rather than silently clamped.
Sourcepub fn kernel_support_ev(&self, e_ev: f64) -> f64
pub fn kernel_support_ev(&self, e_ev: f64) -> f64
Kernel support at energy e_ev, in eV.
Returns the maximum energy offset over which the tabulated
kernel has non-zero weight at energy e_ev. Past this
distance the kernel is exactly zero, so the broadening
footprint at a given target energy is fully contained within
[e_ev − support, e_ev + support].
Computation:
- Find the bracketing reference kernel(s) for
e_evvia binary search on the sortedref_energiesgrid. - Take the extreme offsets
dt⁺ = max(dt, 0)anddt⁻ = max(−dt, 0)over the kernel entries that can carry weight ate_ev. Between references,Self::broaden’s width-normalized shape blend scales each block’s support in mode-anchoredz = Δt/σ_bto the target widthσ_tand unions them — so the scan takes each block’s closure extremes (the outermostw > 0offset, extended to the adjacentw == 0entry if one exists on that side: the linearly interpolated shape is positive on that fringe, and a merged point from the other block can land there), divides by that block’sσ_b, maxes across the two blocks in z, and multiplies byσ_t. Degenerate blocks (σ ≤ 0) take the nearest-clone fallback at apply time, so both blocks are scanned with their own positive-weight masks. - Map each extreme through the exact TOF→E relation
E' = (TOF_FACTOR·L/(t∓dt))²witht = TOF_FACTOR·L/√Eand return the larger energy excursion:max( E·((t/(t−dt⁺))² − 1), E·(1 − (t/(t+dt⁻))²) ). The convolution gather reads theory att − dt(seeSelf::broaden), so the positive-offset tail reaches up in energy — and because the map is convex int, the up-side excursion strictly exceeds the linear chain-rule estimate2·E^{3/2}·dt/(TOF_FACTOR·L)that this function previously returned, which under-covered exactly the side the delayed-emission tail loads.
Returns 0.0 for non-positive e_ev, an empty kernel set, or
a non-positive flight path, and f64::INFINITY when the
positive-offset extreme reaches or exceeds the nominal flight
time (t − dt⁺ ≤ 0: the kernel maps past infinite energy — the
caller must clamp to its grid, which the GUI’s
partition_point slicing already does). Used by the GUI’s
fit-energy-range slicing to extend the model-evaluation grid
beyond the user’s [E_min, E_max] so the SAMMY EMIN/EMAX-
equivalent broadening at the boundaries is correct (#514).
Source§impl TabulatedResolution
impl TabulatedResolution
Sourcepub fn from_text(
text: &str,
flight_path_m: f64,
) -> Result<Self, ResolutionParseError>
pub fn from_text( text: &str, flight_path_m: f64, ) -> Result<Self, ResolutionParseError>
Parse a VENUS/FTS resolution file.
§Arguments
text— File contents as a string.flight_path_m— Flight path length in meters.
Sourcepub fn from_kernels(
ref_energies: Vec<f64>,
kernels: Vec<(Vec<f64>, Vec<f64>)>,
flight_path_m: f64,
) -> Result<Self, ResolutionParseError>
pub fn from_kernels( ref_energies: Vec<f64>, kernels: Vec<(Vec<f64>, Vec<f64>)>, flight_path_m: f64, ) -> Result<Self, ResolutionParseError>
Build a tabulated resolution directly from synthesized kernels.
Used by the analytical crate::ikeda_carpenter::IkedaCarpenter model,
which generates (tof_offset_µs, weight) kernels at a set of reference
energies and then rides the exact same broadening machinery as a
Monte-Carlo file. Validates the same invariants from_text enforces:
non-empty + strictly ascending reference energies, one kernel per energy,
each kernel non-empty with matching offset/weight lengths, and all-finite
offsets/weights.
§Errors
Returns ResolutionParseError::InvalidFormat if the reference
energies are empty / not strictly ascending, if the energy and kernel
counts differ, if any kernel is empty, if a kernel’s offset and weight
vectors differ in length, or if any offset/weight is non-finite.
Sourcepub fn from_file(
path: &str,
flight_path_m: f64,
) -> Result<Self, ResolutionParseError>
pub fn from_file( path: &str, flight_path_m: f64, ) -> Result<Self, ResolutionParseError>
Parse a VENUS/FTS resolution file from disk.
Sourcepub fn broaden(
&self,
energies: &[f64],
spectrum: &[f64],
) -> Result<Vec<f64>, ResolutionError>
pub fn broaden( &self, energies: &[f64], spectrum: &[f64], ) -> Result<Vec<f64>, ResolutionError>
Apply tabulated resolution broadening to a spectrum.
For each energy point:
- Find bracketing reference energies and interpolate kernel (log-space)
- Convert TOF offsets to energy offsets using exact TOF↔energy relation
- Convolve spectrum with interpolated kernel (trapezoidal integration)
Kernel points whose delayed-emission offset reaches the nominal
flight time at the target energy (dt ≥ TOF(E)) gather from
past infinite energy; they are dropped and the kernel
renormalized over the surviving points, mirroring the grid-edge
handling — see the tail-truncation note on broaden_presorted.
Self::kernel_support_ev returns f64::INFINITY in exactly
that regime, so callers consuming it as a fit-range margin
already have the signal.
§Errors
Returns ResolutionError::LengthMismatch if the arrays differ in
length, or ResolutionError::UnsortedEnergies if the energy grid is
not sorted in non-descending order.
Sourcepub fn plan(&self, energies: &[f64]) -> Result<ResolutionPlan, ResolutionError>
pub fn plan(&self, energies: &[f64]) -> Result<ResolutionPlan, ResolutionError>
Build a reusable broadening plan for a specific target energy grid.
Validates that energies is non-descending — the same sorted-grid
precondition enforced by TabulatedResolution::broaden via
validate_inputs. An
unsorted grid would produce a silently-wrong plan (misbracketed
e_prime lookups against e_min / e_max), so it must be
caught at build time rather than returning garbage from
ResolutionPlan::apply.
The plan hoists every quantity that depends only on
(target_energies, self.ref_energies, self.flight_path_m) —
namely the TOF conversion, the log-space kernel interpolation,
the per-kernel-point e_prime and spectrum-bracket lookup, and
the trapezoidal integration widths. Applying the plan to a
spectrum becomes a pure gather + multiply-add loop.
Build cost: same as one call to the private broaden_presorted
helper (O(N_target × N_kernel) TOF / bracket / interp work, plus
~2 × N_kernel log-interp ops per target energy for
interpolated_kernel). Apply cost per target: 1 branch +
~3 loads + 3 flops per retained entry, plus the final divide —
typically < 10 % of the build cost. The payoff comes from
reusing one plan across many spectra.
Bit-exact with broaden_presorted: pre-computes the same
floating-point sequences (TOF, e_prime, dt_width, frac,
weight, norm) in the same order.
§Errors
Returns ResolutionError::UnsortedEnergies if energies is
not non-descending.
Trait Implementations§
Source§impl Clone for TabulatedResolution
impl Clone for TabulatedResolution
Source§fn clone(&self) -> TabulatedResolution
fn clone(&self) -> TabulatedResolution
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreAuto Trait Implementations§
impl Freeze for TabulatedResolution
impl RefUnwindSafe for TabulatedResolution
impl Send for TabulatedResolution
impl Sync for TabulatedResolution
impl Unpin for TabulatedResolution
impl UnsafeUnpin for TabulatedResolution
impl UnwindSafe for TabulatedResolution
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more